9

我在 Python 中将 XML 文件转换为 CSV 时遇到了很多麻烦。我查看了很多论坛,尝试了 lxml 和 xmlutils.xml2csv,但我无法让它工作。它是来自 Garmin GPS 设备的 GPS 数据。

这是我的 XML 文件的样子,当然缩短了:

<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:tc2="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tp1="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="TC2 to GPX11 XSLT stylesheet" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">
  <trk>
      <name>2013-12-03T21:08:56Z</name>
      <trkseg>
          <trkpt lat="45.4852855" lon="-122.6347885">
              <ele>0.0000000</ele>
              <time>2013-12-03T21:08:56Z</time>
          </trkpt>
          <trkpt lat="45.4852961" lon="-122.6347926">
              <ele>0.0000000</ele>
              <time>2013-12-03T21:09:00Z</time>
          </trkpt>
          <trkpt lat="45.4852982" lon="-122.6347897">
              <ele>0.2000000</ele>
              <time>2013-12-03T21:09:01Z</time>
          </trkpt>
      </trkseg>
  </trk>
</gpx>

在我的大型 XML 文件中有几个 trk 标签,但我可以设法将它们分开——它们代表 GPS 设备上的不同“段”或行程。我想要的只是一个 CSV 文件,它绘制如下内容:

LAT         LON         TIME         ELE
45.4...     -122.6...   2013-12...   0.00...
...         ...         ...          ...

这是我到目前为止的代码:

## Call libraries
import csv
from xmlutils.xml2csv import xml2csv

inputs = "myfile.xml"
output = "myfile.csv"

converter = xml2csv(inputs, output)
converter.convert(tag="WHATEVER_GOES_HERE_RENDERS_EMPTY_CSV")

这是另一个替代代码。它只输出一个没有数据的 CSV 文件,只有标题latlon.

import csv
import lxml.etree

x = '''
<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:tc2="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tp1="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="TC2 to GPX11 XSLT stylesheet" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">
<trk>
  <name>2013-12-03T21:08:56Z</name>
  <trkseg>
    <trkpt lat="45.4852855" lon="-122.6347885">
      <ele>0.0000000</ele>
      <time>2013-12-03T21:08:56Z</time>
    </trkpt>
    <trkpt lat="45.4852961" lon="-122.6347926">
      <ele>0.0000000</ele>
      <time>2013-12-03T21:09:00Z</time>
    </trkpt>
    <trkpt lat="45.4852982" lon="-122.6347897">
      <ele>0.2000000</ele>
      <time>2013-12-03T21:09:01Z</time>
    </trkpt>
  </trkseg>
</trk>
</gpx>
'''

with open('output.csv', 'w') as f:
    writer = csv.writer(f)
    writer.writerow(('lat', 'lon'))
    root = lxml.etree.fromstring(x)
    for trkpt in root.iter('trkpt'):
        row = trkpt.get('lat'), trkpt.get('lon')
        writer.writerow(row)

我该怎么做呢?请意识到我是新手,所以更全面的解释会非常棒!

4

3 回答 3

22

这是一个命名空间的XML 文档。因此,您需要使用它们各自的命名空间来寻址节点。

文档中使用的命名空间在顶部定义:

xmlns:tc2="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tp1="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns="http://www.topografix.com/GPX/1/1"

所以第一个命名空间被映射到简写形式tc2,并且会用在像<tc2:foobar/>. 最后一个在 之后没有简短形式xmlns,称为默认命名空间,它适用于文档中没有明确使用命名空间的所有元素 - 因此它也适用于您的<trkpt />元素。

因此,您需要编写root.iter('{http://www.topografix.com/GPX/1/1}trkpt')来选择这些元素。

为了同时获得时间和高度,您可以使用trkpt.find()访问trkpt节点下方的这些元素,然后element.text检索这些元素的文本内容(与 和 等属性相反latlon。此外,由于timeandele元素也使用默认命名空间,因此您必须{namespace}element再次使用该语法来选择这些节点。

所以你可以使用这样的东西:

NS = 'http://www.topografix.com/GPX/1/1'
header = ('lat', 'lon', 'ele', 'time')

with open('output.csv', 'w') as f:
    writer = csv.writer(f)
    writer.writerow(header)
    root = lxml.etree.fromstring(x)
    for trkpt in root.iter('{%s}trkpt' % NS):
        lat = trkpt.get('lat')
        lon = trkpt.get('lon')
        ele = trkpt.find('{%s}ele' % NS).text
        time = trkpt.find('{%s}time' % NS).text

        row = lat, lon, ele, time
        writer.writerow(row)

有关 XML 命名空间的更多信息,请参阅lxml 教程中的命名空间部分有关 XML 命名空间的 Wikipedia 文章。有关格式的一些详细信息,另请参阅GPS eXchange.gpx格式。

于 2013-12-21T01:32:08.153 回答
1

很抱歉在这里使用已经制作的工具,但这对您的数据起到了作用:

  1. 将 XML 转换为 JSON:http ://convertjson.com/xml-to-json.htm
  2. 获取该 JSON 并将 JSON 转换为 CSV:https ://konklone.io/json/

它对您的数据很有帮助。

ele,time,_lat,_lon
0.0000000,2013-12-03T21:08:56Z,45.4852855,-122.6347885
0.0000000,2013-12-03T21:09:00Z,45.4852961,-122.6347926
0.2000000,2013-12-03T21:09:01Z,45.4852982,-122.6347897

所以对于编码,我认为 XML > JSON > CSV 可能是一个好方法。您会发现这些链接中指向的相关脚本。

于 2018-01-16T17:39:27.293 回答
1

我专门为这种情况编写了gpxcsv,并处理其他转换器省略的 gpx 扩展字段。

一样容易:

from gpxcsv import gpxtolist
import pandas as pd

df = pd.DataFrame(
    pxtolist('myfile.gpx'))

对于数据框,或者存在一个命令行工具来创建一个 .csv 或 .json 文件,在跟踪点中保留尽可能多的列,因为它使用标签作为列名。

项目源码在 github 上

于 2021-07-27T01:01:45.140 回答