【问题标题】:Processing html text nodes with scrapy and XPath使用 scrapy 和 XPath 处理 html 文本节点
【发布时间】:2016-09-09 16:22:58
【问题描述】:

我正在使用scrapy来处理这样的文档:

...
<div class="contents">
    some text
    <ol>
        <li>
            more text
        </li>
        ...
    </ol>
</div>
...

我想将内容区域内的所有文本收集到一个字符串中。 我还需要 &lt;li&gt; 元素中的 '1., 2., 3....',所以我的结果应该是 'some text 1. more text...'

所以,我正在循环遍历&lt;div class="contents"&gt; 的孩子

for n in response.xpath('//div[@class="contents"]/node()'):
    if n.xpath('self::ol'):
        result += process_list(n)
    else:
        result += n.extract()

如果n 是一个有序列表,我会遍历它的元素并将一个数字添加到li/text()(在process_list() 中)。如果n 本身是一个文本节点,我只是读取它的值。 但是,'some text' 似乎不是节点集的一部分,因为循环没有进入else 部分。我的结果是'1. more text'

查找相对于其父节点的文本节点有效:

response.xpath('//div[@class="contents"]//text()')

找到所有文本,但这样我无法添加列表项编号。

我做错了什么,有没有更好的方法来完成我的任务?

【问题讨论】:

    标签: python xpath scrapy


    【解决方案1】:

    Scrapy 的选择器在底层使用lxml,但lxml doesn't work with XPath calls on text nodes

    >>> import scrapy
    >>> s = scrapy.Selector(text='''<div class="contents">
    ...     some text
    ...     <ol>
    ...         <li>
    ...             more text
    ...         </li>
    ...         ...
    ...     </ol>
    ... </div>''')
    >>> s.xpath('.//div[@class="contents"]/node()')
    [<Selector xpath='.//div[@class="contents"]/node()' data='\n    some text\n    '>, <Selector xpath='.//div[@class="contents"]/node()' data='<ol>\n        <li>\n            more text\n'>, <Selector xpath='.//div[@class="contents"]/node()' data='\n'>]
    >>> for n in s.xpath('.//div[@class="contents"]/node()'):
    ...     print(n.xpath('self::ol'))
    ... 
    []
    [<Selector xpath='self::ol' data='<ol>\n        <li>\n            more text\n'>]
    []
    

    但是您可以侵入底层的 lxml 对象来测试它的文本节点类型(它“隐藏”在每个 scrapy 选择器的 .root 属性中):

    >>> for n in s.xpath('.//div[@class="contents"]/node()'):
    ...     print([type(n.root), n.root])
    ... 
    [<class 'str'>, '\n    some text\n    ']
    [<class 'lxml.etree._Element'>, <Element ol at 0x7fa020f2f9c8>]
    [<class 'str'>, '\n']
    

    另一种方法是使用一些 HTML 到文本的转换库,例如 html2text

    >>> import html2text
    >>> html2text.html2text("""<div class="contents">
    ...     some text
    ...     <ol>
    ...         <li>
    ...             more text
    ...         </li>
    ...         ...
    ...     </ol>
    ... </div>""")
    'some text\n\n  1. more text \n...\n\n'
    

    【讨论】:

      【解决方案2】:

      如果n 不是ol 元素,self::ol 将产生一个空节点集。当表达式的结果是空节点集时,n.xpath(...) 应该返回什么?

      空节点集在 XPath 中是“假的”,但您不会在 XPath 中将其评估为布尔值,仅在 Python 中。 Python中的空节点集是假的吗?

      如果这是问题所在,您可以通过将 if 语句更改为来解决它

      if n.xpath('boolean(self::ol)'):
      

      if n.xpath('count(self::ol) > 1'):
      

      【讨论】:

      • 谢谢,我试过了,但没用。如果节点集为空,n.xpath(...) 返回一个空列表,在 Python 中应该是 falsy。在上面的例子中,else 路径应该在第一次迭代中输入,但它不是。使用if n.xpath('self::text()') 显式测试文本节点也不起作用。
      • @analina:n.xpath() 在第一次迭代中返回什么?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多