使用lxml来解析段落标记

时间:2017-07-07 17:34:17

标签: python html xpath xml-parsing lxml

我正在使用Python库lxml对从this url检索到的HTML执行XML解析。我在过去使用lxml时遇到了麻烦,但是我可能刚刚遇到了一个缺少子元素(在lxml树中)的错误,它明显出现在HTML中。

以下是我用来解析HTML的Python代码:

from urllib.request import urlopen
from lxml import etree

html_response = urlopen("http://ohhla.com/YFA_natedogg.html")
html_parser = etree.HTMLParser()
tree = etree.parse(html_response, html_parser)
tree.xpath("//table[@id='AutoNumber7']/tr[2]/td/p[1]")[0]

我正在解析的网站的HTML代码的简化版本如下所示:

<table id='AutoNumber7'>
    <tbody>
        <tr></tr>
        <tr>
            <td>
                # ... (irrelevant tags) ... 
                <p>
                    <a></a>
                    # The following <table> tag is what I need to target:
                    <table></table>
                </p>
                # ... (seven <p> tags identical to the above) ...
            </td>
        </tr>
    </tbody>

当我在控制台中运行tree.xpath("//table[@id='AutoNumber7']/tr[2]/td/p[1]")[0].getchildren() 时,lxml只检测初始锚标记<a>并忽略我需要选择的兄弟<table>标记(由上面的注释表示)码)。

这是控制台输出:

tree.xpath("//table[@id='AutoNumber7']/tr[2]/td/p[1]")[0].getchildren()
Out[22]: [<Element a at 0x2904a2a5808>]

我期望看到的是:

tree.xpath("//table[@id='AutoNumber7']/tr[2]/td/p[1]")[0].getchildren()
Out[22]: [<Element a at 0x2904a2a5808>, <Element table at 0x???????????>]

<table>代码的孩子遗漏了<p>标记的任何想法? 如何选择此<table>代码?我需要解析表标记中的所有内容,但lxml似乎无法将其识别为有效的子元素。如果任何人都可以为所需的<table>标签提供有效的xpath选择器,我会非常感激!

注意:我知道我应该看到[<Element tr at 0x??????????>, <Element tr at 0x???????????>, ...]而不是[<Element table at 0x??????????>],但我试图更加简洁。

编辑:对于那些不考虑上述代码可重现性的人,只需将其复制并粘贴到控制台中即可:

from urllib.request import urlopen
from lxml import etree

html_response = urlopen("http://ohhla.com/YFA_natedogg.html")
html_parser = etree.HTMLParser()
tree = etree.parse(html_response, html_parser)
print(tree.xpath("//table[@id='AutoNumber7']/tr[2]/td/p[1]")[0].getchildren())

与我之前尝试解析的HTML一样,位于here

我真的不知道如何更简洁。建设性意见(一如既往)受到赞赏。

  • 链接到我已阅读过的网页(例如How to create a Minimal, Complete, and Verifiable example,无需评论 不是建设性的批评。
  • 指出我可能错过的步骤,或将来(从特定资源)改进的内容,是对我自己和整个社区都有益的建设性批评。
  • 我很乐意接受有关如何改进帖子的建议,但请提供实际建议。请记住,有些人可能会阅读相同的资源并得出单独的结论。

1 个答案:

答案 0 :(得分:1)

我认为问题在于lxml试图通过HTML规则来玩。根据这些规则,<table>(块级元素)不能是<p>的子级。请参阅https://www.w3.org/TR/html4/struct/text.html#h-9.3.1

简短演示:

from lxml import html

test = """
<html>
  <p>
    <table>
      <tr>
        <td>XXX</td>
      </tr>
    </table>
  </p>
</html>"""

root = html.fromstring(test)

# Just print the string representation of the parsed HTML
print(html.tostring(root).decode("UTF-8"))

在此代码的输出中,我们可以看到lxml拒绝将<table>解释为<p>的子项:

<html>
  <body><p>
    </p><table>
      <tr>
        <td>XXX</td>
      </tr>
    </table>

</body></html>

<a>是一个内联元素,因此它包含在getchildren()的返回值中是有意义的。您必须找到其他方法来识别您感兴趣的<table>元素。

http://ohhla.com/YFA_natedogg.html文档声称是XHTML,但它有很多错误,无法解析为XML文档。