1

我正在使用 lxml.html 解析一个 html 文件......该 html 文件包含带有小写字母和大写字母的标签。我的部分代码如下所示:

        response = urllib2.urlopen(link)
        html = response.read().decode('cp1251')
        content_html = etree.HTML(html_1)
        first_link_xpath =  content_html.xpath('//TR')
        print (first_link_xpath)

我的 HTML 文件的一小部分如下所示:

<TR>
    <TR vAlign="top" align="left">
        <!--<TD><B  onmouseover="tips.Display('Metadata_WEB', event)" onmouseout="tips.Hide('Metadata_WEB')">Meta Data:</B></TD>-->
        <TD></TD>
    </TR>
 </TR>

因此,当我为下面的 html 示例运行上面的代码时,它会给出一个空列表。然后我尝试运行这一行first_link_xpath = content_html_1.xpath('//tr/node()'),所有大写标签都表示为\r\n\t\t\t\t'输出:这个问题背后的原因是什么?

注意:如果问题不能令人信服,请告诉我修改

4

2 回答 2

1

为了跟进 unutbu 的回答,我建议您比较lxmlXML 和 HTML 解析器,特别是它们如何通过使用lxml.etree.tostring(). 您可以看到不同的标签、标签大小写和层次结构(这可能与人类的想法不同;)

$ python
>>> import lxml.etree
>>> doc = """<TR>
...     <TR vAlign="top" align="left">
...         <!--<TD><B  onmouseover="tips.Display('Metadata_WEB', event)" onmouseout="tips.Hide('Metadata_WEB')">Meta Data:</B></TD>-->
...         <TD></TD>
...     </TR>
...  </TR>"""
>>> xmldoc = lxml.etree.fromstring(doc)
>>> xmldoc
<Element TR at 0x1e79b90>
>>> htmldoc = lxml.etree.HTML(doc)
>>> htmldoc
<Element html at 0x1f0baa0>
>>> lxml.etree.tostring(xmldoc)
'<TR>\n    <TR vAlign="top" align="left">\n        <!--<TD><B  onmouseover="tips.Display(\'Metadata_WEB\', event)" onmouseout="tips.Hide(\'Metadata_WEB\')">Meta Data:</B></TD>-->\n        <TD/>\n    </TR>\n </TR>'
>>> lxml.etree.tostring(htmldoc)
'<html><body><tr/><tr valign="top" align="left"><!--<TD><B  onmouseover="tips.Display(\'Metadata_WEB\', event)" onmouseout="tips.Hide(\'Metadata_WEB\')">Meta Data:</B></TD>--><td/>\n    </tr></body></html>'
>>> 

您可以看到,使用 HTML 解析器,它创建了封闭html和标记,并且开头body有一个空节点,因为在 HTML 中 a不能直接跟随 a (您提供的 HTML 片段被破坏,要么是拼写错误,要么原始文件也坏了)trtrtr

然后,再次按照 unutbu 的建议,您可以尝试不同的 XPath 表达式:

>>> xmldoc.xpath('//tr')
[]
>>> xmldoc.xpath('//TR')
[<Element TR at 0x1e79b90>, <Element TR at 0x1f0baf0>]
>>> xmldoc.xpath('//TR/node()')
['\n    ', <Element TR at 0x1f0baf0>, '\n        ', <!--<TD><B  onmouseover="tips.Display('Metadata_WEB', event)" onmouseout="tips.Hide('Metadata_WEB')">Meta Data:</B></TD>-->, '\n        ', <Element TD at 0x1f0bb40>, '\n    ', '\n ']
>>> 
>>> htmldoc.xpath('//tr')
[<Element tr at 0x1f0bbe0>, <Element tr at 0x1f0bc30>]
>>> htmldoc.xpath('//TR')
[]
>>> htmldoc.xpath('//tr/node()')
[<!--<TD><B  onmouseover="tips.Display('Metadata_WEB', event)" onmouseout="tips.Hide('Metadata_WEB')">Meta Data:</B></TD>-->, <Element td at 0x1f0bbe0>, '\n    ']
>>> 

事实上,正如 unutbu 所强调的,对于 HTML,XPath 表达式应该使用小写标签来选择元素。

对我来说, '\r\n\t\t\t\t' 输出不是错误,它只是各种标签tr之间的空格。td对于文本内容,如果你不想要这个空格,你可以使用lxml.etree.tostring(element, memthod="text", encoding=unicode).strip()element例如来自 XPath 的地方。(这适用于前导和尾随空格)。(注意method参数很重要,默认会输出上面测试的HTML表示)

>>> map(lambda element: lxml.etree.tostring(element, method="text", encoding=unicode), htmldoc.xpath('//tr'))
[u'', u'\n    ']
>>> 

您可以验证文本表示是否全是空格。

于 2013-07-16T08:52:29.130 回答
0

HTML 解析器将所有标记名称转换为小写。这就是为什么xpath('//TR')返回一个空列表。

我无法重现第二个问题,即大写标签打印为\r\n\t\t\t\t'. 你能修改下面的代码来演示这个问题吗?

import lxml.etree as ET

content = '''\
<TR>
    <TR vAlign="top" align="left">
        <!--<TD><B  onmouseover="tips.Display('Metadata_WEB', event)" onmouseout="tips.Hide('Metadata_WEB')">Meta Data:</B></TD>-->
        <TD></TD>
    </TR>
 </TR>'''

root = ET.HTML(content)
print(root.xpath('//TR'))
# []
print(root.xpath('//tr/node()'))
# [<!--<TD><B  onmouseover="tips.Display('Metadata_WEB', event)" onmouseout="tips.Hide('Metadata_WEB')">Meta Data:</B></TD>-->, <Element td at 0xb77463ec>, '\n    ']
print(root.xpath('//tr'))
# [<Element tr at 0xb77462fc>, <Element tr at 0xb77463ec>]
于 2013-07-15T14:24:35.423 回答