【问题标题】:Python lxml find text efficientlyPython lxml 高效查找文本
【发布时间】:2021-06-30 10:14:40
【问题描述】:

使用python lxml我想测试一个XML文档是否包含EXPERIMENT_TYPE,如果存在,提取

例子:

<EXPERIMENT_SET>
  <EXPERIMENT center_name="BCCA" alias="Experiment-pass_2.0">
    <TITLE>WGBS (whole genome bisulfite sequencing) analysis of SomeSampleA (library: SomeLibraryA).</TITLE>
    <STUDY_REF accession="SomeStudy" refcenter="BCCA"/>
    <EXPERIMENT_ATTRIBUTES>
      <EXPERIMENT_ATTRIBUTE><TAG>EXPERIMENT_TYPE</TAG><VALUE>DNA Methylation</VALUE></EXPERIMENT_ATTRIBUTE>
      <EXPERIMENT_ATTRIBUTE><TAG>EXPERIMENT_ONTOLOGY_URI</TAG><VALUE>http://purl.obolibrary.org/obo/OBI_0001863</VALUE></EXPERIMENT_ATTRIBUTE>
      <EXPERIMENT_ATTRIBUTE><TAG>EXPERIMENT_ONTOLOGY_CURIE</TAG><VALUE>obi:0001863</VALUE></EXPERIMENT_ATTRIBUTE>
      <EXPERIMENT_ATTRIBUTE><TAG>MOLECULE</TAG><VALUE>genomic DNA</VALUE></EXPERIMENT_ATTRIBUTE>
    </EXPERIMENT_ATTRIBUTES>
  </EXPERIMENT>
</EXPERIMENT_SET>

有没有比遍历所有元素更快的方法?

    all = etree.findall('EXPERIMENT/EXPERIMENT_ATTRIBUTES/EXPERIMENT_ATTRIBUTE/TAG')
    
    for e in all:
        if e.text == 'EXPERIMENT_TYPE':
            print("Found")

当我想提取 时,这种尝试也变得混乱。

【问题讨论】:

    标签: python xml xpath lxml


    【解决方案1】:

    最好使用 XPath 来执行此操作,这肯定会非常快。我的建议(经过测试和工作)。它将返回一个(可能为空的)VALUE 元素列表,您可以从中添加text

    PS:不要使用诸如all之类的“特殊”词作为变量名。不好的做法,可能会导致意外的错误。

    import lxml.etree as ET
    from lxml.etree import Element
    from typing import List
    
    xml_str = """
    <EXPERIMENT_SET>
      <EXPERIMENT center_name="BCCA" alias="Experiment-pass_2.0">
        <TITLE>WGBS (whole genome bisulfite sequencing) analysis of SomeSampleA (library: SomeLibraryA).</TITLE>
        <STUDY_REF accession="SomeStudy" refcenter="BCCA"/>
        <EXPERIMENT_ATTRIBUTES>
          <EXPERIMENT_ATTRIBUTE><TAG>EXPERIMENT_TYPE</TAG><VALUE>DNA Methylation</VALUE></EXPERIMENT_ATTRIBUTE>
          <EXPERIMENT_ATTRIBUTE><TAG>EXPERIMENT_ONTOLOGY_URI</TAG><VALUE>http://purl.obolibrary.org/obo/OBI_0001863</VALUE></EXPERIMENT_ATTRIBUTE>
          <EXPERIMENT_ATTRIBUTE><TAG>EXPERIMENT_ONTOLOGY_CURIE</TAG><VALUE>obi:0001863</VALUE></EXPERIMENT_ATTRIBUTE>
          <EXPERIMENT_ATTRIBUTE><TAG>MOLECULE</TAG><VALUE>genomic DNA</VALUE></EXPERIMENT_ATTRIBUTE>
        </EXPERIMENT_ATTRIBUTES>
      </EXPERIMENT>
    </EXPERIMENT_SET>
    """
    
    
    tree = ET.ElementTree(ET.fromstring(xml_str))
    vals: List[Element] = tree.xpath(".//EXPERIMENT_ATTRIBUTE/TAG[text()='EXPERIMENT_TYPE']/following-sibling::VALUE")
    print(vals[0].text)
    # DNA Methylation
    

    Michael Kay 在下面提供了另一种 XPath 声明,这与 Martin Honnen 的答案相同。

    .//EXPERIMENT_ATTRIBUTE[TAG='EXPERIMENT_TYPE']/VALUE
    

    【讨论】:

    • 我个人会将.//EXPERIMENT_ATTRIBUTE/TAG[text()='EXPERIMENT_TYPE']/following-sibling::VALUE"重写为.//EXPERIMENT_ATTRIBUTE[TAG='EXPERIMENT_TYPE']/VALUE
    • @MichaelKay 这确实可能是一个更好的解决方案。我会把它添加到帖子中。
    【解决方案2】:

    就 XPath 而言,您似乎只想根据 TAG 元素选择 VALUE 元素,例如/EXPERIMENT_SET/EXPERIMENT/EXPERIMENT_ATTRIBUTES/EXPERIMENT_ATTRIBUTE[TAG = 'EXPERIMENT_TYPE']/VALUE.

    我认为对于 Python 和 lxml,人们经常使用文本节点选择,例如/EXPERIMENT_SET/EXPERIMENT/EXPERIMENT_ATTRIBUTES/EXPERIMENT_ATTRIBUTE[TAG = 'EXPERIMENT_TYPE']/VALUE/text() 然后 xpath 函数将其作为 Python 字符串返回。

    【讨论】:

      【解决方案3】:

      使用findall 是很自然的做法。我建议使用以下代码来查找 VALUE:

      from lxml import etree
      
      root = etree.parse('toto.xml').getroot()
      
      all = root.findall('EXPERIMENT/EXPERIMENT_ATTRIBUTES/EXPERIMENT_ATTRIBUTE/TAG')
      for e in all:
          if e.text == 'EXPERIMENT_TYPE':
              v = e.getparent().find('VALUE')
              if v is not None:
                  print(f'Found val="{v.text}"')
      

      这个输出:

      Found val="DNA Methylation"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-03
        • 1970-01-01
        • 2021-12-24
        • 2012-07-25
        • 2015-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多