如何将XML文件转换为易读的pandas数据框?

91

假设我有这样一个XML:

<author type="XXX" language="EN" gender="xx" feature="xx" web="foobar.com">
    <documents count="N">
        <document KEY="e95a9a6c790ecb95e46cf15bee517651" web="www.foo_bar_exmaple.com"><![CDATA[A large text with lots of strings and punctuations symbols [...]
]]>
        </document>
        <document KEY="bc360cfbafc39970587547215162f0db" web="www.foo_bar_exmaple.com"><![CDATA[A large text with lots of strings and punctuations symbols [...]
]]>
        </document>
        <document KEY="19e71144c50a8b9160b3f0955e906fce" web="www.foo_bar_exmaple.com"><![CDATA[A large text with lots of strings and punctuations symbols [...]
]]>
        </document>
        <document KEY="21d4af9021a174f61b884606c74d9e42" web="www.foo_bar_exmaple.com"><![CDATA[A large text with lots of strings and punctuations symbols [...]
]]>
        </document>
    </documents>
</author>

我想阅读这个XML文件并将其转换为Pandas DataFrame:

key                                         type     language    feature            web                         data
e95324a9a6c790ecb95e46cf15bE232ee517651      XXX        EN          xx      www.foo_bar_exmaple.com     A large text with lots of strings and punctuations symbols [...]
bc360cfbafc39970587547215162f0db             XXX        EN          xx      www.foo_bar_exmaple.com     A large text with lots of strings and punctuations symbols [...]
19e71144c50a8b9160b3cvdf2324f0955e906fce     XXX        EN          xx      www.foo_bar_exmaple.com     A large text with lots of strings and punctuations symbols [...]
21d4af9021a174f61b8erf284606c74d9e42         XXX        EN          xx      www.foo_bar_exmaple.com     A large text with lots of strings and punctuations symbols [...]

这就是我已经尝试过的,但我遇到了一些错误,可能有更有效的方法来完成这个任务:

from lxml import objectify
import pandas as pd

path = 'file_path'
xml = objectify.parse(open(path))
root = xml.getroot()
root.getchildren()[0].getchildren()
df = pd.DataFrame(columns=('key','type', 'language', 'feature', 'web', 'data'))

for i in range(0,len(xml)):
    obj = root.getchildren()[i].getchildren()
    row = dict(zip(['key','type', 'language', 'feature', 'web', 'data'], [obj[0].text, obj[1].text]))
    row_s = pd.Series(row)
    row_s.name = i
    df = df.append(row_s)

有没有人可以为我提供一个更好的解决方案?

5个回答

59
你可以轻松使用 Python 标准库中的 xml 将数据转换为 pandas.DataFrame。以下是我建议的方法(从文件读取时,将xml_data替换为你的文件名或文件对象):
import pandas as pd
import xml.etree.ElementTree as ET
import io

def iter_docs(author):
    author_attr = author.attrib
    for doc in author.iter('document'):
        doc_dict = author_attr.copy()
        doc_dict.update(doc.attrib)
        doc_dict['data'] = doc.text
        yield doc_dict

xml_data = io.StringIO(u'''YOUR XML STRING HERE''')

etree = ET.parse(xml_data) #create an ElementTree object 
doc_df = pd.DataFrame(list(iter_docs(etree.getroot())))

如果您的原始文档中有多个作者或者您的XML根元素不是author,那么我会添加以下生成器:

def iter_author(etree):
    for author in etree.iter('author'):
        for row in iter_docs(author):
            yield row

doc_df = pd.DataFrame(list(iter_docs(etree.getroot()))) 更改为 doc_df = pd.DataFrame(list(iter_author(etree)))

请查看在 xml文档中提供的 ElementTree 教程


2
问题是关于如何加载一个 XML 文件,因此最好您的回答应该解决这个问题,而不是加载一个 XML 字符串... - birgersp
1
@gromit190 很好的建议。我已经更新了我的答案,可以从文件中读取。 - JaminSore
2
微小的问题:需要将 xml_data = io.StringIO(''' 替换为 xml_data = io.StringIO(u''',因为 StringIO 的参数需要是 Unicode。否则会出现 "TypeError: initial_value must be unicode or None, not str" 的错误。 - Cristian Ciupitu
@CristianCiupitu 我看到这个问题被标记为python-2.7 ---已添加u前缀。 - JaminSore

30

v1.3 版本开始,您可以简单地使用以下方法:

pandas.read_xml(path_or_file)

8
实际上,从这篇帖子中可以看出,OP需要调整XPath以从根节点向下查找一级:pandas.read_xml(path_or_file, xpath="/Author/document") - Parfait

16

这里有另一种将XML转换为Pandas数据框的方法。例如,我从字符串解析XML,但是这个逻辑同样适用于从文件中读取。

import pandas as pd
import xml.etree.ElementTree as ET

xml_str = '<?xml version="1.0" encoding="utf-8"?>\n<response>\n <head>\n  <code>\n   200\n  </code>\n </head>\n <body>\n  <data id="0" name="All Categories" t="2018052600" tg="1" type="category"/>\n  <data id="13" name="RealEstate.com.au [H]" t="2018052600" tg="1" type="publication"/>\n </body>\n</response>'

etree = ET.fromstring(xml_str)
dfcols = ['id', 'name']
df = pd.DataFrame(columns=dfcols)

for i in etree.iter(tag='data'):
    df = df.append(
        pd.Series([i.get('id'), i.get('name')], index=dfcols),
        ignore_index=True)

df.head()

4
我建议使用xmltodict库来处理XML文本,该库可以很好地处理您的XML文本。我曾使用它来摄取一个包含近百万条记录的XML文件。

3

您还可以通过创建元素字典,然后直接转换为数据帧来进行转换:

import xml.etree.ElementTree as ET
import pandas as pd

# Contents of test.xml
# <?xml version="1.0" encoding="utf-8"?> <tags>   <row Id="1" TagName="bayesian" Count="4699" ExcerptPostId="20258" WikiPostId="20257" />   <row Id="2" TagName="prior" Count="598" ExcerptPostId="62158" WikiPostId="62157" />   <row Id="3" TagName="elicitation" Count="10" />   <row Id="5" TagName="open-source" Count="16" /> </tags>

root = ET.parse('test.xml').getroot()

tags = {"tags":[]}
for elem in root:
    tag = {}
    tag["Id"] = elem.attrib['Id']
    tag["TagName"] = elem.attrib['TagName']
    tag["Count"] = elem.attrib['Count']
    tags["tags"]. append(tag)

df_users = pd.DataFrame(tags["tags"])
df_users.head()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接