我需要从网站中提取文本。该网站有两种不同的结构,其中一种在正文之前有一个附加元素。
我尝试提取文本如下:
//div[contains(@id, "text")]/(p|.)/text()
据我所知,eTree 的 Xpath 不喜欢
(p|.)
。是否有另一种简短的方式来编写此代码,以便 lxml 不会抱怨?我知道我可以把两者都写出来并在它们之间放置一个或,但是上面的这个选项节省了大量的输入(考虑到纯 XPath 也可以工作)。
谢谢!
编辑 以下是这两种情况的两个小示例片段(已简化):
没有孩子:
<div class="article_text">
...
<div id="bodytext"...>
"yadda yadda here be dragons"
</div>
</div>
带着孩子:
<div class="article_text">
...
<div id="bodytext">
<p>
"Here be paragraphed dragons"
</p>
</div>
</div>
编辑 2: 这不完全是关于这个特定的案例,而是直接关于 lxml - 我从很多不同的网站提取文本,这个“可选的孩子”很常见 - 我的问题是是否有另一种选择(不同的写作)这种语法,还是 lxml 不接受它,我必须全部“或”它?
编辑 3: 我刚才遇到的事情是:新闻站点有不同类型的文章,有时是所谓的“信息框”,不需要提取。所以我会做
//main/(div[not(contains(@class, "infobox"))]|.)/p/text()
,排除这个特定的div,但包括其他div或其他类型的标签。文本在一些页面中包裹在 div 中,在其他包裹在多个 div 中,有时在跨度等中。意思是“或”与那里的解决方案相比,它们一起会产生一个极其/不必要的复杂 XPath - 但如前所述,lxml 不会似乎喜欢它。
如果你想要所有后代的文本,你可以这样做(我把你的两个例子都加入到一个 XML 文档中):
>>> doc.xpath('//div[contains(@id, "text")]//text()')
['\n "yadda yadda here be dragons"\n ', '\n ', '\n "Here be paragraphed dragons"\n ', '\n ']
注意
//
之前的text()
,输出是一个平面列表。
如果你想在节点上分割输出,你可以做一些事情:
>>> for node in doc.xpath('//div[contains(@id,"text")]'):
print((''.join(node.xpath('.//text()')).strip()))
"yadda yadda here be dragons"
"Here be paragraphed dragons"