【问题标题】:Lxml Get all itens but test the next one as well - PythonLxml 获取所有项目,但也测试下一个项目 - Python
【发布时间】:2020-05-24 18:59:10
【问题描述】:

我在尝试解析这个 lxml 时遇到了麻烦。我正在使用python语言,3.6.9。

是这样的。

<download date="22/05/2020 08:34">
    <link url="http://xpto" document="y"/>
    <link url="http://xpto" document="y"/>
    <subjects number="2"><subject>Text explaining the previous link</subject><subject>Another text explaining the previous link</subject></subjects>
    <link url="http://xpto" document="z"/>
    <subjects number="1"><subject>Text explaining the previous link</subject></subjects>
    <link url="http://xpto" document="y"/>
    <link url="http://xpto" document="z"/>
</download>

目前,我可以使用此功能获取所有链接(这很容易实现):

import requests
from lxml import html 
response = html.fromstring(requests.post(url_post, data=data).content)
links = response.xpath('//link')

正如我在 lxml 中指出的那样,如果存在主题,则旨在解释上一个链接。有时,它可以有多个主题(如上面的示例,其中一个主题有数字 2,这意味着它里面有两个“主题”项目,但其他“主题”只有一个主题)。它是一个很大的 lxml 文件,所以这种差异(很多链接,直到它有一个链接,后面有一个解释)经常发生。

我如何构建一个查询来获取所有这些链接,并且当它旁边的主题存在时(更准确地说,在链接之后),将它们放在一起或将其插入链接中?

我的梦想是这样的:

<link url="http://xpto" document="y" subjects="Text explaining the previous link| Another text explaining the thing"/>

同时包含链接和主题的列表也会有很大帮助。

[
[<link url="http://xpto" document="y"/>], 
[<link url="http://xpto" document="y"/>, <subjects number="2"><subject>Text explaining the previous link</subject><subject>Another text explaining the previous link</subject></subjects>],
[<link url="http://xpto" document="y"/>], 
]

当然,请随意提出不同的建议。

谢谢各位!

【问题讨论】:

  • “有时,它可以有多个主题。”是什么意思?您可以编辑问题并添加这样的案例示例吗?
  • 嘿,@JackFleeting,我刚刚添加了更多解释。这是一个包含很多“链接”类别的列表,但链接后显示的“主题”部分实际上为我们提供了有关链接本身的解释。真是奇怪……因为是和链接有关的东西,要是进去就容易了。但这不是服务器响应的方式......太糟糕了。感谢您的关注!
  • @JackFleeting 我想我必须做一个'for循环'。也许有一个更好的方法可以用纯 lxml 做到这一点 - 顺便说一句,我不是很好......
  • 我理解正确吗:你想要所有的 &lt;link&gt; 元素,但是当后面跟着 &lt;subjects&gt; 元素时,你也需要这些吗?
  • 没错,@Grismar

标签: python parsing lxml


【解决方案1】:

这就是我认为你需要的:

from lxml import html

example = """
<link url="some_url" document="a"/>
<link url="some_url" document="b"/>
<subjects><subject>some text</subject></subjects>
<link url="some_url" document="c"/>
<link url="some_url" document="d"/>
<subjects><subject>some text</subject><subject>some more</subject></subjects>
"""

response = html.fromstring(example)
links = response.xpath('//link')
result = []
for link in links:
    result.append([link])
    next_element = link.getnext()
    if next_element is not None and next_element.tag == 'subjects':
        result[-1].append(next_element)

print(result)

结果:

[[<Element link at 0x1a0891e0d60>], [<Element link at 0x1a0891e0db0>, <Element subjects at 0x1a089096360>], [<Element link at 0x1a0891e0e00>], [<Element link at 0x1a0891e0e50>, <Element subjects at 0x1a0891e0d10>]]

请注意,列表仍然包含 lxml Element 对象,如果需要,当然可以将它们转换为字符串。

关键步骤是next_element = link.getnext() 行。对于lxmlElement.getnext() 方法返回文档中的下一个兄弟。因此,尽管您正在循环遍历与.xpath() 匹配的link 元素,但link.getnext() 仍然会为您提供subjects 元素,如果这是文档中的下一个兄弟元素。如果没有下一个元素(即最后一个link,如果它后面没有subjects),.getnext() 将返回None,这就是为什么下面的代码行检查is not None

【讨论】:

  • 嘿!谢谢你。我认为我可能会有另一个选择完全 lxml 查询,但我认为这种方法是解决我的问题的好方法。非常感谢!
  • 你能给我解释一下这个getnext吗?我从未在 for 循环中看到过这种情况。通常,我会对这样的事情使用枚举。
  • 我添加了一段,希望对您有所帮助。
【解决方案2】:

这不是最优雅的做事方式,但它可以完成工作......

subjects= """
<download date="22/05/2020 08:34">
    <link url="http://xpto" document="y"/>
    <link url="http://xpto" document="y"/>
    <subjects number="2">
      <subject>First Text explaining the previous link</subject>
      <subject>Another text explaining the previous link</subject>
     </subjects>
    <link url="http://xpto2" document="z"/>
    <subjects number="1"><subject>Second Text explaining the previous link</subject></subjects>
    <link url="http://xpto3" document="y"/>
    <link url="http://xpto4" document="z"/>
</download>

"""
#Note that I changed your html a bit to emphasize the differences between nodes

import lxml.html as lh
import elementpath
doc = lh.fromstring(subjects)

elements = elementpath.select(doc, "//link[following-sibling::*[1][name()='subjects']]/concat('<link url=',./@url, ' document=xxx',@document,'xxx subjects=xxx',string-join(./following-sibling::subjects[1]//subject,' | '),'xxx/>')")
# I needed to use the xxx placeholder because I couldn't find a way to escape the double quote marks inside the expression, and this way is simple to implement    

for element in elements:
    print(element.replace('xxx','"'))

输出:

<link url=http://xpto document="y" subjects="First Text explaining the previous link | Another text explaining the previous link"/>
<link url=http://xpto2 document="z" subjects="Second Text explaining the previous link"/>

【讨论】:

    【解决方案3】:

    我想出了这个解决方案。 它比@grismar 建议慢一点,但实现了将“主题”插入链接。另一方面,它让我不必再循环遍历列表来解析 '[[link,subject],]' 元素。

    filteredData = response.xpath('//link | //subjects') #get both link and subjects into a list
    for i, item in enumerate(filteredData):        
        if item.tag == 'subjects':
            filteredData[i-1].append(item)  
            filteredData.remove(item)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-05
      • 2021-04-26
      • 1970-01-01
      相关资源
      最近更新 更多