提问人:x89 提问时间:1/23/2023 最后编辑:HedgeHogx89 更新时间:1/23/2023 访问量:408
如何解析嵌套的XML并提取属性+标签文本?
How to parse nested XML and extract attributes + tag text both?
问:
我的 XML 如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<main_heading timestamp="20220113">
<details>
<offer id="11" new_id="12">
<level>1&1</level>
<typ>Green</typ>
<name>Alpha</name>
<visits>
<name>DONT INCLUDE</name>
</visits>
</offer>
<offer id="12" new_id="31">
<level>1&1</level>
<typ>Yellow</typ>
<name>Beta</name>
<visits>
<name>DONT INCLUDE</name>
</visits>
</offer>
</details>
</main_heading>
我想将某些字段解析为数据帧。
预期输出
timestamp id new_id level name
20220113 11 12 1&1 Alpha
20220113 12 31 1&1 Beta
其中不包括嵌套在“visits”标记中的 NAME。我只想考虑外部的“名称”标签。
timestamp = soup.find('main_heading').get('timestamp')
df[timestamp'] = timestamp
这解决了一部分
其余的我可以这样做:
typ = []
for i in (soup.find_all('typ')):
typ.append(i.text)
但我不想为每个新字段创建多个 for 循环
答:
3赞
HedgeHog
1/23/2023
#1
遍历选件并选择其上一个选件:main_heading
for e in soup.select('offer'):
data.append({
'timestamp': e.find_previous('main_heading').get('timestamp'),
'id':e.get('id'),
'id_old':e.get('old_id'),
'level':e.level.text,
'typ':e.typ.text,
'name':e.select_one('name').text
})
或者,作为仅排除某些元素并更通用的替代方法:
for e in soup.select('offer'):
d = {
'timestamp': e.find_previous('main_heading').get('timestamp'),
'id':e.get('id'),
'id_old':e.get('old_id'),
}
d.update({c.name:c.text for c in e.children if c.name is not None and 'visits' not in c.name})
data.append(d)
例
from bs4 import BeautifulSoup
import pandas as pd
xml = '''<?xml version="1.0" encoding="UTF-8" ?>
<main_heading timestamp="20220113">
<details>
<offer id="11" new_id="12">
<level>1&1</level>
<typ>Green</typ>
<name>Alpha</name>
<visits>
<name>DONT INCLUDE</name>
</visits>
</offer>
<offer id="12" new_id="31">
<level>1&1</level>
<typ>Yellow</typ>
<name>Beta</name>
<visits>
<name>DONT INCLUDE</name>
</visits>
</offer>
</details>
</main_heading>
'''
soup = BeautifulSoup(xml,'xml')
data = []
for e in soup.select('offer'):
data.append({
'timestamp': e.find_previous('main_heading').get('timestamp'),
'id':e.get('id'),
'id_old':e.get('old_id'),
'level':e.level.text,
'typ':e.typ.text,
'name':e.select_one('name').text
})
pd.DataFrame(data)
输出
时间戳 | 编号 | id_old | 水平 | 典型值 | 名字 | |
---|---|---|---|---|---|---|
0 | 20220113 | 11 | 1&1 | 绿 | 阿尔法 | |
1 | 20220113 | 12 | 1&1 | 黄色 | 试用版 |
评论
0赞
eike
1/23/2023
只是出于兴趣,在这种情况下,和 之间有区别吗?select("offer")
find_all("offer")
1赞
HedgeHog
1/23/2023
不是在这种特定情况下,因为两者都使用元素名称,但通常使用 -> crummy.com/software/BeautifulSoup/bs4/doc/#css-selectorsselect
css selectors
0赞
HedgeHog
1/23/2023
@eike 此外,可以检查此问答:stackoverflow.com/questions/38028384/...
0赞
balderman
1/23/2023
@HedgeHog我认为这里已经足够好了。xml.etree.ElementTree
1赞
balderman
1/23/2023
#2
无需任何外部库。
核心 python 在这里就足够了。
import xml.etree.ElementTree as ET
import pandas as pd
xml = '''<?xml version="1.0" encoding="UTF-8" ?>
<main_heading timestamp="20220113">
<details>
<offer id="11" new_id="12">
<level>1&1</level>
<typ>Green</typ>
<name>Alpha</name>
<visits>
<name>DONT INCLUDE</name>
</visits>
</offer>
<offer id="12" new_id="31">
<level>1&1</level>
<typ>Yellow</typ>
<name>Beta</name>
<visits>
<name>DONT INCLUDE</name>
</visits>
</offer>
</details>
</main_heading>'''
data = []
root = ET.fromstring(xml)
timestamp = root.attrib.get('timestamp')
for offer in root.findall('.//offer'):
temp = {'timestamp': timestamp}
for attr in ['id', 'new_id']:
temp[attr] = offer.attrib.get(attr)
for ele in ['level', 'name']:
temp[ele] = offer.find(ele).text
data.append(temp)
df = pd.DataFrame(data)
print(df)
输出
timestamp id new_id level name
0 20220113 11 12 1&1 Alpha
1 20220113 12 31 1&1 Beta
评论
0赞
HedgeHog
1/23/2023
合理的替代方案,如有必要,将牢记它以使其更简单。
1赞
Jack Fleeting
1/23/2023
#3
为了完整起见(和未来的访问者),这里还有另一个:由于我们正在处理 xml,并且最终输出是 DataFrame,因此最好(也是最简单)使用 pandas.read_xml:
df = pd.read_xml(xml,xpath='//offer')
ts = pd.read_xml(xml,xpath="//main_heading")['timestamp'][0]
df.insert(0, 'timestamp', ts)
print(df.drop(['typ', 'visits'], axis=1))
这应该能给你带来预期的产出。
评论
0赞
HedgeHog
1/23/2023
就像一束可能性一样,今天又学到了一些东西——我发现直接使用和@jqurious一样好pandas
0赞
Jack Fleeting
1/23/2023
@HedgeHog - “可能性的花束” - 你绝对应该花一些时间在诗歌网站上:)。
0赞
HedgeHog
1/23/2023
难道我们所有人心中都不有一个小诗人吗?我们是源代码诗人,写着美丽的字母汤,寻找可爱的熊猫,想知道一束可能性。你是对的,从今天开始有更多的诗歌;)
2赞
jqurious
1/23/2023
#4
熊猫有.read_xml()
您可以使用传递自定义 XPath 表达式来指定要提取的内容。xpath=
例如,和标签:<offer>
<main_heading>
>>> pd.read_xml("main.xml", xpath="""//*[name() = "offer" or name() = "main_heading"]""")
timestamp details id new_id level typ name visits
0 20220113.0 NaN NaN NaN None None None NaN
1 NaN NaN 11.0 12.0 1&1 Green Alpha NaN
2 NaN NaN 12.0 31.0 1&1 Yellow Beta NaN
从那里您可以添加时间戳并删除详细信息/访问列:.ffill()
>>> (pd.read_xml("main.xml", xpath="""//*[name() = "offer" or name() = "main_heading"]""")
... .ffill()
... .drop(columns=["details", "visits"])
... .dropna()
... )
timestamp id new_id level typ name
1 20220113.0 11.0 12.0 1&1 Green Alpha
2 20220113.0 12.0 31.0 1&1 Yellow Beta
评论
0赞
HedgeHog
1/23/2023
从未想过使用 ,最好的替代方法可以直接使用xpath
pandas
0赞
Jack Fleeting
1/23/2023
看起来我们相隔几秒钟发布 - 使用同样的想法!伟大的思想是一样的(如果我自己这么说的话)!
评论
offer
offer