Разбор файла xml с использованием lxml - PullRequest
0 голосов
/ 22 мая 2018

Я пытаюсь отредактировать XML-файл, найдя каждый тег Watts и изменив текст в нем.До сих пор мне удалось изменить все теги, но не тег Watts.

Мой анализатор:

from lxml import etree
tree = etree.parse("cycling.xml")
root = tree.getroot()

for watt in root.iter():
    if watt.tag == "Watts":
        watt.text = "strong"

tree.write("output.xml")

Это сохраняет мой файл cycling.xml без изменений.Фрагмент из файла output.xml (который также является файлом cycling.xml, поскольку он не изменился):

<TrainingCenterDatabase xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
  <Activities>
    <Activity Sport="Biking">
      <Id>2018-05-06T20:49:56Z</Id>
      <Lap StartTime="2018-05-06T20:49:56Z">
        <TotalTimeSeconds>2495.363</TotalTimeSeconds>
        <DistanceMeters>15345</DistanceMeters>
        <MaximumSpeed>18.4</MaximumSpeed>
        <Calories>0</Calories>
        <Intensity>Active</Intensity>
        <TriggerMethod>Manual</TriggerMethod>
        <Track>
          <Trackpoint>
            <Time>2018-05-06T20:49:56Z</Time>
            <Position>
              <LatitudeDegrees>49.319297</LatitudeDegrees>
              <LongitudeDegrees>-123.024128</LongitudeDegrees>
            </Position>
            <HeartRateBpm>
              <Value>99</Value>
            </HeartRateBpm>
            <Extensions>
              <TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">
                <Watts>0</Watts>
                <Speed>2</Speed>
              </TPX>
            </Extensions>
          </Trackpoint>

Если я изменю свой анализатор, чтобы изменить все теги на:

for watt in root.iter():
    if watt.tag != "Watts":
        watt.text = "strong"

Тогда мой файл output.xml становится:

<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">strong<Activities>strong<Activity Sport="Biking">strong<Id>strong</Id>
      <Lap StartTime="2018-05-06T20:49:56Z">strong<TotalTimeSeconds>strong</TotalTimeSeconds>
        <DistanceMeters>strong</DistanceMeters>
        <MaximumSpeed>strong</MaximumSpeed>
        <Calories>strong</Calories>
        <Intensity>strong</Intensity>
        <TriggerMethod>strong</TriggerMethod>
        <Track>strong<Trackpoint>strong<Time>strong</Time>
            <Position>strong<LatitudeDegrees>strong</LatitudeDegrees>
              <LongitudeDegrees>strong</LongitudeDegrees>
            </Position>
            <HeartRateBpm>strong<Value>strong</Value>
            </HeartRateBpm>
            <Extensions>strong<TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">strong<Watts>strong</Watts>
                <Speed>strong</Speed>
              </TPX>
            </Extensions>
          </Trackpoint>
          <Trackpoint>strong<Time>strong</Time>
            <Position>strong<LatitudeDegrees>strong</LatitudeDegrees>
              <LongitudeDegrees>strong</LongitudeDegrees>
            </Position>
            <AltitudeMeters>strong</AltitudeMeters>
            <HeartRateBpm>strong<Value>strong</Value>
            </HeartRateBpm>
            <Extensions>strong<TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">strong<Watts>strong</Watts>
                <Speed>strong</Speed>
              </TPX>
            </Extensions>
          </Trackpoint>
  1. Как мне изменить только тег Watts?
  2. Я не понимаю, что делает root = tree.getroot().Я просто подумал, что задам этот вопрос одновременно, хотя я не уверен, что он имеет значение в моей конкретной проблеме.

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

В качестве альтернативы, поскольку вы используете два важных слова edit xml и используете lxml, рассмотрите XSLT (язык преобразования XML), где вы можете определить префикс пространства имен и изменить Ватт в любом месте документа без зацикливания.Кроме того, вы можете передавать значения в XSLT из Python!

XSLT (сохранить как файл .xsl)

<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"              
               xmlns:doc="http://www.garmin.com/xmlschemas/ActivityExtension/v2" version="1.0">
    <xsl:output version="1.0" encoding="UTF-8" omit-xml-declaration="no" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- VALUE TO BE PASSED INTO FROM PYTHON -->
    <xsl:param name="python_value">

    <!-- Identity Transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
           <xsl:apply-templates select="@*|node()"/>
       </xsl:copy>
    </xsl:template>

    <!-- ADJUST WATTS TEXT -->
    <xsl:template match="doc:Watts">
        <xsl:copy><xsl:value-of select="$python_value"/></xsl:copy>
    </xsl:template>

</xsl:transform>

Python

from lxml import etree

# LOAD XML AND XSL
doc = etree.parse("cycling.xml")
xsl = etree.parse('XSLT_Script.xsl')

# CONFIGURE TRANSFORMER
transform = etree.XSLT(xsl)    

# RUN TRANSFORMATION WITH PARAM
n = etree.XSLT.strparam('Strong')
result = transform(doc, python_value=n)

# PRINT TO CONSOLE
print(result) 

# SAVE TO FILE
with open('Output.xml', 'wb') as f:
    f.write(result)
0 голосов
/ 22 мая 2018

Ваш документ определяет пространство имен XML по умолчанию.Посмотрите на атрибут xmlns= в конце открывающего тега:

<TrainingCenterDatabase
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">

Это означает, что в вашем документе нет элемента с именем "Watts";вам нужно будет квалифицировать имена тегов в соответствующем пространстве имен.Если вы напечатаете значение watt.tag в нашем цикле, вы увидите:

$ python filter.py 
{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}TrainingCenterDatabase
[...]
{http://www.garmin.com/xmlschemas/ActivityExtension/v2}Watts
{http://www.garmin.com/xmlschemas/ActivityExtension/v2}Speed

Имея это в виду, вы можете изменить свой фильтр так, чтобы он выглядел так:

from lxml import etree
tree = etree.parse("cycling.xml")
root = tree.getroot()

for watt in root.iter():
    if watt.tag == "{http://www.garmin.com/xmlschemas/ActivityExtension/v2}Watts":
        watt.text = "strong"

tree.write("output.xml")

Подробнее об обработке пространства имен можно прочитать в документации lxml .

...