【问题标题】:Missing Child While Using lxml to Parse Children of Paragraph Tag使用 lxml 解析段落标记的子项时缺少子项
【发布时间】:2017-12-12 02:23:55
【问题描述】:

我正在使用 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 只检测到初始锚标记&lt;a&gt; 并忽略我需要选择的兄弟&lt;table&gt; 标记(由代码中的上述注释表示)。

这是控制台输出:

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???????????>]

知道为什么&lt;p&gt; 标签的子标签中缺少&lt;table&gt; 标签吗? 如何选择这个&lt;table&gt; 标签?我需要解析 table 标记中的所有内容,但 lxml 似乎无法将其识别为有效的子元素。如果有人可以为所需的&lt;table&gt; 标签提供有效的 xpath 选择器,我将非常感激!

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

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

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

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

  • 链接到我已经阅读过的页面(例如How to create a Minimal, Complete, and Verifiable example没有评论 不是建设性的批评。
  • 指出我可能遗漏了哪些步骤,或者将来需要改进的地方(来自特定资源)是建设性的批评,对我自己和整个社区都有好处。
  • 我很乐意接受有关如何改进我的帖子的建议,但请提供实际的建议。请记住,几个人可能会阅读相同的资源并得出不同的结论。

【问题讨论】:

  • "这是我用来解析 HTML 的 Python 代码" - 不,我认为这不是您正在使用的 Python 代码。特别是 tree 从未定义过。请复制粘贴,切勿重新键入您正在使用的确切短程序。请参阅minimal reproducible example 了解更多信息。
  • @Rob 感谢您指出这一点,代码没有重新输入。我只是错过了复制那行代码。代码分散在一个大方法中,所以我只需要复制会重现上述问题的相关行。
  • XPath 中的tbody 在哪里?既然它不见了,为什么你会在控制台中看到你声称的 a 元素?
  • @kjhughes 出于某种原因(我不知道),当包含&lt;tbody&gt; 时,xpath 选择器不起作用。如果您按原样运行代码:tree.xpath("//table[@id='AutoNumber7']/tr[2]/td/p[1]")[0].getchildren(),您应该会看到与我收到的相同的控制台输出。
  • 实际的 HTML 与您发布的不同,没有 tbody。回去创建一个真正的 minimal reproducible example,不要再浪费大家的时间了。你的问题应该独立存在——不依赖于任何外部链接——这样它对未来的读者就会有价值。问题也应该是可验证,仅基于您的问题。

标签: python html xpath xml-parsing lxml


【解决方案1】:

我认为问题在于 lxml 试图按照 HTML 规则进行操作。根据这些规则,&lt;table&gt;(块级元素)不能是&lt;p&gt; 的子元素。见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 拒绝将 &lt;table&gt; 解释为 &lt;p&gt; 的子代:

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

</body></html>

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

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

【讨论】:

  • 谢谢,这正是我所担心的。我很欣赏你简洁易懂的例子(不像我的)。对于如何在 Python 中选择作为 &lt;p&gt; 标记的子元素的 &lt;table&gt; 元素,您有什么建议吗?我应该为此完全放弃 xpath 选择器吗?
  • 我认为如果您寻找&lt;p&gt; 的跟随兄弟而不是孩子,它应该可以工作。
猜你喜欢
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 1970-01-01
  • 2020-02-27
  • 2018-12-11
  • 1970-01-01
相关资源
最近更新 更多