【问题标题】:Using lxml xpath to parse xml file使用 lxml xpath 解析 xml 文件
【发布时间】:2018-11-27 03:45:14
【问题描述】:

我正在使用 lxml XPath 来解析以下 xml 文件

<urlset
    xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
    xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
    xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
    <url>
        <loc>
    https://www.reuters.com/article/us-campbellsoup-thirdpoint/campbell-soup-nears-deal-with-third-point-to-end-board-challenge-sources-idUSKCN1NU11I
    </loc>
        <image:image>
            <image:loc>
    https://www.reuters.com/resources/r/?m=02&d=20181126&t=2&i=1328589868&w=&fh=&fw=&ll=460&pl=300&r=LYNXNPEEAO0WM
    </image:loc>
        </image:image>
        <news:news>
            <news:publication>
                <news:name>Reuters</news:name>
                <news:language>eng</news:language>
            </news:publication>
            <news:publication_date>2018-11-26T02:55:00+00:00</news:publication_date>
            <news:title>
    Campbell Soup nears deal with Third Point to end board challenge: sources
    </news:title>
            <news:keywords>Headlines,Business, Industry</news:keywords>
            <news:stock_tickers>NYSE:CPB</news:stock_tickers>
        </news:news>
    </url>
</urlset>

Python 代码示例

import lxml.etree
import lxml.html
import requests

def main():
    r = requests.get("https://www.reuters.com/sitemap_news_index1.xml")

    namespace = "http://www.google.com/schemas/sitemap-news/0.9"
    root = lxml.etree.fromstring(r.content)


    records = root.xpath('//news:title', namespaces = {"news": "http://www.google.com/schemas/sitemap-news/0.9"})
    for record in records:
        print(record.text)


    records = root.xpath('//sitemap:loc', namespaces = {"sitemap": "http://www.sitemaps.org/schemas/sitemap/0.9"})
    for record in records:
        print(record.text)


if __name__ == "__main__":
    main()

目前,我是 XPath 来获取所有 URLtitle,但这不是我想要的,因为我不知道哪个 URL 属于哪个标题。我的问题是如何获取每个&lt;url&gt;,然后循环每个&lt;url&gt; 作为项目以获取对应的&lt;loc&gt;&lt;news:keywords&gt; 等。谢谢!

编辑: 期待输出

foreach <url>
      get <loc>
      get <news:publication_date>
      get <news:title>

【问题讨论】:

  • 你能发布一个你预期输出的例子吗?
  • @BernardL 预期输出已添加。

标签: python xml lxml


【解决方案1】:

使用相对 XPath 从每个标题获取到其关联的 URL:

ns = {
    "news": "http://www.google.com/schemas/sitemap-news/0.9",
    "sitemap": "http://www.sitemaps.org/schemas/sitemap/0.9",
    "image": "http://www.google.com/schemas/sitemap-image/1.1"
}

r = requests.get("https://www.reuters.com/sitemap_news_index1.xml")
root = lxml.etree.fromstring(r.content)

for title in root.xpath('//news:title', namespaces=ns):
    print(title.text)

    loc = title.xpath('ancestor::sitemap:url/sitemap:loc', namespaces=ns)
    print(loc[0].text)

练习:改写它以从 URL 获取相关标题。

注意:标题(可能还有 URL)似乎是 HTML 转义的。使用unescape()函数

from html import unescape

让他们逃脱。

【讨论】:

    【解决方案2】:

    答案是

    from datetime import datetime
    from html import unescape
    from lxml import etree
    import requests
    
    r = requests.get("https://www.reuters.com/sitemap_news_index1.xml")
    root = etree.fromstring(r.content)
    
    ns = {
        "news": "http://www.google.com/schemas/sitemap-news/0.9",
        "sitemap": "http://www.sitemaps.org/schemas/sitemap/0.9",
        "image": "http://www.google.com/schemas/sitemap-image/1.1"
    }
    
    for url in root.iterfind("sitemap:url", namespaces=ns):
        loc = url.findtext("sitemap:loc", namespaces=ns)
        print(loc)
        title = unescape(url.findtext("news:news/news:title", namespaces=ns))
        print(title)
        date = unescape(url.findtext("news:news/news:publication_date", namespaces=ns))
        date = datetime.strptime(date, '%Y-%m-%dT%H:%M:%S+00:00')
        print(date)
    

    经验法则是:

    尽量不要使用 xpath。不要使用 xpath,而是使用 find、findall、iterfind。 xpath 是一种比 find、findall 或 iterfind 更复杂的算法,它需要更多的时间和资源。

    使用 iterfind 而不是使用 findall。因为 iterfind 将产生返回项目。也就是说,它将一次返回一项。因此它使用的内存更少。

    如果您只需要文本,请使用 findtext

    更一般的规则是阅读official document

    首先,让我们创建3个for循环函数并比较它们。

    def for1():
        for url in root.iterfind("sitemap:url", namespaces=ns):
            pass
    
    def for2():
        for url in root.findall("sitemap:url", namespaces=ns):
            pass
    
    def for3():
        for url in root.xpath("sitemap:url", namespaces=ns):
            pass
    
    
    function time
    root.iterfind 70.5 µs ± 543 ns
    root.findall 72.3 µs ± 839 ns
    root.xpath 84.8 µs ± 567 ns

    我们可以看到 iterfind 是预期最快的。

    接下来,让我们检查一下 for 循环中的语句。

    statement time
    url.xpath('string(news:news/news:title)', namespaces=ns) 15.7 µs ± 112 ns
    url_item.xpath('news:news/news:title', namespaces=ns)[0].text 14.4 µs ± 53.7 ns
    url_item.find('news:news/news:title', namespaces=ns).text 3.74 µs ± 60 ns
    url_item.findtext('news:news/news:title', namespaces=ns) 3.71 µs ± 40.3 ns

    从上表可以看出find/findtext比xpath快4倍。而且 findtext 比 find 还要快。

    与 Tomalak 的 8.33 ms ± 52.4 µs 相比,此答案仅需 3.41 ms ± 53 µs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-23
      • 2014-08-22
      • 2020-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多