我正在用Python实现Web抓取程序。
请考虑以下HTML代码段。
<div>
<b>
<i>
HelloWorld
</i>
HiThere
</b>
</div>
如果我只想使用lxml提取粗体或斜体文本,请使用以下命令
tree = etree.fromstring(myhtmlstr, htmlparser)
opp1 = tree.xpath(".//text()[ancestor::b or ancestor::i or ancestor::strong]")
这给了我正确的结果,即我的opp1的结果是:
[['HelloWorld','HiThere']
到目前为止,一切都很完美。但是,如果我尝试查询标签的父母,就会出现真正的问题。不出所料,opp1[0].getparent().tag
和opp1[0].getparent().getparent().tag
的输出是i
和b
。
但是真正的问题在第二个标签中。理想情况下,opp[1]
的父级应该是b
标记。但是,opp1[1].getparent().tag
和opp1[1].getparent().getparent().tag
的输出又是i
和b
。
您可以在以下代码中进行验证:
from lxml import etree
htmlstr = """<div><b><i>HelloWorld</i>HiThere</b></div>"""
htmlparser = etree.HTMLParser()
tree = etree.fromstring(htmlstr, htmlparser)
opp1 = tree.xpath(".//text()[ancestor::b or ancestor::i or ancestor::strong]")
print(opp1)
print(opp1[0].getparent(), opp1[0].getparent().getparent())
print(opp1[1].getparent(), opp1[1].getparent().getparent())
有人可以指出为什么会这样吗?我该怎么做才能纠正它?我计划仅对我的程序使用lxml,并且不希望使用bs4的任何解决方案。
这个问题似乎源于LXML(和ElementTree)的数据模型,其中一个元素大致是“标签,属性,文本,子项,尾部”; DOM数据模型也具有文本的实际节点。
如果您更改程序以执行此操作
for x in tree.xpath(".//text()[ancestor::b or ancestor::i or ancestor::strong]"):
print(x, x.getparent(), "text?", x.is_text, "tail?", x.is_tail)
它将打印
HelloWorld <Element i at 0x10aa0ccd0> text? True tail? False
HiThere <Element i at 0x10aa0ccd0> text? False tail? True
即"HiThere"
是i元素的尾部,因为这是Etree数据模型表示混合的文本和标签的方式。
这里的重点(可能适用于您的用例)是将.getparent().getparent()
视为具有is_tail=True
的文本结果的有效父级。