использование XPath с ElementTree в Python 3.7 для поиска и извлечения значений из XML-файла

#python-3.x #xml #xpath #elementtree

#python-3.x #xml #xpath #elementtree

Вопрос:

XML-файл, который я пытаюсь проанализировать, можно найти здесь . Этот XML имеет определенное пространство имен. Вот пример из XML-файла с элементами pertintnet:

     <series>
        <header>
            <type>instantaneous</type>
            <locationId>Fredericton</locationId>
            <parameterId>HG</parameterId>
            <timeStep unit="second" multiplier="3600"/>
            <startDate date="2020-05-11" time="07:00:00"/>
            <endDate date="2020-05-15" time="07:00:00"/>
            <missVal>-999</missVal>
            <stationName>SAINT JOHN RIVER AT FREDERICTON</stationName>
            <units>M</units>
        </header>
        <event date="2020-05-11" time="07:00:00" value="4.69" flag="0"/>
        <event date="2020-05-11" time="08:00:00" value="4.66" flag="0"/>
        <event "many records deleted to save space"/>
        <event date="2020-05-15" time="06:00:00" value="4.27" flag="0"/>
        <event date="2020-05-15" time="07:00:00" value="-999" flag="8"/>
    </series>
  

Мне нужно выполнить поиск в XML-файле по тексту, хранящемуся в <locationId> элементах, например, «Fredericton». Как только я нахожу «Fredericton», мне нужно извлечь <parmeterId> текст, а также получить атрибуты из первого и последнего <event> элементов. Вот код, который у меня есть до сих пор. Как я могу использовать XPath для доступа к нужным мне элементам? Я прокомментировал свою попытку, которая не сработала.

 import os
from xml.etree import ElementTree as ET
file_name = 'StJohn_FEWSNB_export.xml'
full_file = os.path.abspath(os.path.join('data', file_name))
print(full_file)

tree = ET.parse(full_file)
root = tree.getroot()

location_lst = [
'Nashwaak','Kennebecasis','Fredericton','Maugerville','Jemseg','Grand_Lake',
'Lakeville_Corner','Gagetown','Oak_Point','Hampton','Saint_John','Connors',
'St_Francois','Ft_Kent','Baker_Brook','St_Hilaire','Edmundston','Iroquois',
'St_Basile','St_Anne','St_Leonard','Perth','Simonds','Hartland','Woodstock'
]

for loc in location_lst:
    for location in root.iter('{http://www.wldelft.nl/fews/PI}locationId'):
        if location.text == loc:
##            type = element.findall('.//{http://www.wldelft.nl/fews/PI}parameterId')
            print(loc, location.text)
  

Спасибо,
Берни.

Ответ №1:

Вот ответ, использующий lxml вместо elementtree и упрощенные версии вашего xml и списка местоположений, чтобы извлечь уменьшенную версию вашего вывода. Очевидно, что вы можете изменить все это в соответствии с фактическим XML и выводом:

     from lxml import etree
    events = """<?xml version="1.0" encoding="UTF-8"?>
    <root>
       <series>
          <header>
             <type>instantaneous</type>
             <locationId>Lakeville_Corner</locationId>
             <parameterId>SSTG</parameterId>
             <timeStep unit="second" multiplier="3600" />
          </header>
          <event date="2020-05-15" time="07:00:00" value="3.64" flag="0" />
          <event date="2020-05-15" time="08:00:00" value="3.64" flag="0" />
          <event date="2020-05-20" time="07:00:00" value="3.157" flag="0" />
       </series>
       <series>
          <header>
             <type>instantaneous</type>
             <locationId>Gagetown</locationId>
             <parameterId>HG</parameterId>
             <timeStep unit="second" multiplier="3600" />
          </header>
          <event date="2020-05-11" time="07:00:00" value="3.99" flag="0" />
          <event date="2020-05-11" time="08:00:00" value="3.99" flag="0" />
          <event date="2020-05-15" time="07:00:00" value="3.43" flag="0" />
       </series>
    </root>
    """
    doc = etree.XML(events.encode())
    location_lst = ["Lakeville_Corner",'Gagetown']
    series = doc.xpath('//series')
    location_lst = ["Lakeville_Corner",'Gagetown']
    series = doc.xpath('//series')
    for loc in location_lst:
      for s in series:
        exp = f'./header/locationId[text()="{loc}"]'
        target = s.xpath(exp)
        if target:
            pid = target[0].xpath('./following-sibling::parameterId/text()')[0]
            date_first = s.xpath('.//event[1]/@date')[0]
            value_first = s.xpath('.//event[1]/@value')[0]
            date_last = s.xpath('.//event[last()]/@date')[0]
            value_last = s.xpath('.//event[last()]/@value')[0]
      print(loc,pid,date_first,value_first,date_last,value_last)
        
  

Вывод:

 Lakeville_Corner SSTG 2020-05-15 3.64 2020-05-20 3.157
Gagetown HG 2020-05-11 3.99 2020-05-15 3.43