【问题标题】:finding text into namespaced xml elements with lxml.etree使用 lxml.etree 在命名空间的 xml 元素中查找文本
【发布时间】:2012-05-29 11:57:23
【问题描述】:

我尝试使用 lxml.etree 来解析 XML 文件并在 XML 的元素中查找文本。

XML 文件可以是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/
     http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
 <responseDate>2002-06-01T19:20:30Z</responseDate> 
 <request verb="ListRecords" from="1998-01-15"
      set="physics:hep"
      metadataPrefix="oai_rfc1807">
      http://an.oa.org/OAI-script</request>
 <ListRecords>
  <record>
    <header>
      <identifier>oai:arXiv.org:hep-th/9901001</identifier>
      <datestamp>1999-12-25</datestamp>
      <setSpec>physics:hep</setSpec>
      <setSpec>math</setSpec>
    </header>
    <metadata>
     <rfc1807 xmlns=
    "http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1807.txt" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation=
       "http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1807.txt
    http://www.openarchives.org/OAI/1.1/rfc1807.xsd">
    <bib-version>v2</bib-version>
    <id>hep-th/9901001</id>
    <entry>January 1, 1999</entry>
    <title>Investigations of Radioactivity</title>
    <author>Ernest Rutherford</author>
    <date>March 30, 1999</date>
     </rfc1807>
    </metadata>
    <about>
      <oai_dc:dc 
      xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ 
      http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
    <dc:publisher>Los Alamos arXiv</dc:publisher>
    <dc:rights>Metadata may be used without restrictions as long as 
       the oai identifier remains attached to it.</dc:rights>
      </oai_dc:dc>
    </about>
  </record>
  <record>
    <header status="deleted">
      <identifier>oai:arXiv.org:hep-th/9901007</identifier>
      <datestamp>1999-12-21</datestamp>
    </header>
  </record>
 </ListRecords>
</OAI-PMH>

对于以下部分,我们假设 doc = etree.parse("/tmp/test.xml") 其中 text.xml 包含上面粘贴的 xml。

首先我尝试使用doc.findall(".//record") 查找所有&lt;record&gt; 元素,但它返回一个空列表。

其次,对于给定的单词,我想检查它是否在&lt;dc:publisher&gt; 中。 为了实现这一点,我首先尝试做与之前相同的事情:doc.findall(".//publisher") 但我有同样的问题......我很确定所有这些都与命名空间相关,但我不知道如何处理它们。

我已经阅读了 libxml tutorial,并在基本 xml 文件(没有任何命名空间)上尝试了 findall 方法的示例,结果成功了。

【问题讨论】:

    标签: python xpath lxml xml-namespaces elementtree


    【解决方案1】:

    正如 Chris 已经提到的,您还可以使用 lxml 和 xpath。由于 xpath 不允许您像 {http://www.openarchives.org/OAI/2.0/}record(所谓的“James Clark 表示法”*)那样完整地编写命名空间名称,因此您必须使用前缀,并为 xpath 引擎提供前缀到命名空间- uri 映射。

    lxml 示例(假设您已经拥有所需的tree 对象):

    nsmap = {'oa':'http://www.openarchives.org/OAI/2.0/', 
             'dc':'http://purl.org/dc/elements/1.1/'}
    tree.xpath('//oa:record[descendant::dc:publisher[contains(., "Alamos")]]',
                namespaces=nsmap)
    

    这将选择所有具有包含单词“Alamos”的后代元素{http://purl.org/dc/elements/1.1/}dc{http://www.openarchives.org/OAI/2.0/}record 元素。

    [*] 这来自article James Clark 解释 XML 命名空间,不熟悉命名空间的每个人都应该阅读这个! (虽然是很久以前写的)

    【讨论】:

    • 简而言之就是回答我的问题!无论如何,我需要它,我还有一个问题:descendant 关键字是针对第一级儿童还是所有儿童?
    • 来自xpath specification:“descendant 轴包含上下文节点的后代;后代是子节点或子节点的子节点等等”
    【解决方案2】:

    免责声明:我使用的是标准库 xml.etree.ElementTree 模块,而不是 lxml 库(尽管据我所知这是 lxml 的一个子集)。我敢肯定有一个答案比我的使用 lxml 和 XPATH 简单得多,但我不知道。

    命名空间问题

    您说得对,问题可能出在命名空间上。您的 XML 文件中没有 record 元素,但文件中有两个 {http://www.openarchives.org/OAI/2.0/}record 标记。如下所示:

    >>> import xml.etree.ElementTree as etree
    
    >>> xml_string = ...Your XML to parse...
    >>> e = etree.fromstring(xml_string)
    
    # Let's see what the root element is
    >>> e
    <Element {http://www.openarchives.org/OAI/2.0/}OAI-PMH at 7f39ebf54f80>
    
    # Let's see what children there are of the root element
    >>> for child in e:
    ...     print child
    ...
    <Element {http://www.openarchives.org/OAI/2.0/}responseDate at 7f39ebf54fc8>
    <Element {http://www.openarchives.org/OAI/2.0/}request at 7f39ebf58050>
    <Element {http://www.openarchives.org/OAI/2.0/}ListRecords at 7f39ebf58098>
    
    # Finally, let's get the children of the `ListRecords` element
    >>> for child in e[-1]:
    ...     print child
    ... 
    <Element {http://www.openarchives.org/OAI/2.0/}record at 7f39ebf580e0>
    <Element {http://www.openarchives.org/OAI/2.0/}record at 7f39ebf58908>
    

    所以,例如

    >>> e.find('ListRecords')
    

    返回None,而

    >>> e.find('{http://www.openarchives.org/OAI/2.0/}ListRecords'
    <Element {http://www.openarchives.org/OAI/2.0/}ListRecords at 7f39ebf58098>
    

    返回ListRecords 元素。

    请注意,我使用的是 find 方法,因为标准库 ElementTree 没有 xpath 方法。

    可能的解决方案

    解决此问题并获取名称空间前缀并将其添加到您要查找的标记的一种方法。你可以使用

    >>>> e.tag[:e.tag.index('}')+1]
    '{http://www.openarchives.org/OAI/2.0/}'
    

    在根元素e 上找到命名空间,尽管我确信有更好的方法来做到这一点。

    现在我们可以定义函数来提取我们想要的标签我们一个可选的命名空间前缀:

    def findallNS(element, tag, namespace=None):
    
        if namspace is not None:
            return element.findall(namepsace+tag)
        else:
            return element.findall(tag)
    
    def findNS(element, tag, namespace=None):
    
        if namspace is not None:
            return element.find(namepsace+tag)
        else:
            return element.find(tag)
    

    所以现在我们可以写:

    >>> list_records = findNS(e, 'ListRecords', namespace)
    >>> findallNS(list_records, 'record', namespace)
    [<Element {http://www.openarchives.org/OAI/2.0/}record at 7f39ebf580e0>, 
    <Element {http://www.openarchives.org/OAI/2.0/}record at 7f39ebf58908>]
    

    替代解决方案

    另一种解决方案可能是编写一个函数来搜索所有以您感兴趣的标签结尾的标签,例如:

    def find_child_tags(element, tag):
        return [child for child in element if child.tag.endswith(tag)]
    

    这里你根本不需要处理命名空间。

    【讨论】:

    • 非常感谢,这个答案对于处理命名空间非常清晰和解释
    【解决方案3】:

    @Chris 的回答非常好,它也适用于lxml。这是使用lxml 的另一种方式(使用xpath 而不是find 的方式相同):

    In [37]: xml.find('.//n:record', namespaces={'n': 'http://www.openarchives.org/OAI/2.0/'})
    Out[37]: <Element {http://www.openarchives.org/OAI/2.0/}record at 0x2a451e0>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-07
      • 1970-01-01
      相关资源
      最近更新 更多