提问人:pyj 提问时间:7/14/2023 最后编辑:Parfaitpyj 更新时间:7/15/2023 访问量:45
有没有更好的方法来使用 BeautifulSoup 解析以下 xml,该 xml 返回有组织的 panda 表中的所有内容?
Is there a better way to parse the following xml using BeautifulSoup that returns everything in an organized panda table?
问:
我正在使用 BeautifulSoup 来解析和 xml 文件。我已经能够成功解析文本列的所有级别。我的代码只返回 xml 中每个段落的最后一项,而不是返回其各自行中的所有项。
以下是我遇到问题的代码块:
# Rest of text
article_text = ""
alinea = article.find_all("ALINEA")
****** I believe the problem starts here ******
for index, p in enumerate(alinea, 1):
txt = p.find_all("TXT")
if p.find_previous_sibling():
# Captures Alinea text of parent
if p.find_previous_sibling().name == "NO.PARAG":
# Used for main text of paragraph
if p.contents[0].name == "P":
article_text = p.contents[0].text
c8 = c7 + '.' + p.find_previous_sibling().text.replace('.', '')
parent_citation = c8
# used for definitions -- do not have a "NO.PARAG" before ALINEA
article_text = p.text
txt = p.find_all("TXT")
for index, t in enumerate(txt, 1):
if p.find_previous_sibling():
if t.find_previous_sibling().name == 'NO.P':
citation_level_8 = parent_citation + p.find_previous_sibling().text
article_text = t.text
lst.append([t2, t3, t4, t5, t6, t7,
c2, c3, c4, c5, c6, c7, c8,
article_text])
以下是我目前的结果:
T2型 | T3型 | T4型 | T5型 | T6型 | T7型 | C2型 | C3型 | C4型 | C5型 | C6型 | C7型 | C8型 | T |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1条:第3款正文 | |||||||||
第一章 标题 | 第2条 标题 | 第一章 | 第1条 | 第2条:第4款正文 | |||||||||
第一章 标题 | 定义 | 第一章 | 第3条 | (3) 文本 |
以下是我想要的结果:
T2型 | T3型 | T4型 | T5型 | T6型 | T7型 | C2型 | C3型 | C4型 | C5型 | C6型 | C7型 | C8型 | 发短信 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1.1条 | 第1条:第1款正文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1.1(a)条 | (a) 案文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1.1(i)条 | (a) 案文(换行符) (i) 案文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1.1(ii)条 | (a) 案文(换行符) (二) 案文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1.1(b)条 | (b) 案文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第1条 | 第1.2条 | 第1条:第2款正文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第2条 | 第2.1条 | 第2条:第1款正文 | ||||||||
第一章 标题 | 第2条 标题 | 第一章 | 第2条 | 第2.1(a)条 | (a) 案文 | ||||||||
第一章 标题 | 第2条 标题 | 第一章 | 第2条 | 第2.1(b)条 | (b) 案文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第2条 | 第2.2条 | 第2条:第2款正文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第2条 | 第2.3条 | 第2条:第3款正文 | ||||||||
第一章 标题 | 第1条 标题 | 第一章 | 第2条 | 第2.3(a)条 | (a) 案文 | ||||||||
第一章 标题 | 定义 | 第一章 | 第3条 | 第3条:定义正文 | |||||||||
第一章 标题 | 定义 | 第一章 | 第3条 | 第3条第(1)款 | 第3条:定义 正文(换行符)(1)正文 | ||||||||
第一章 标题 | 定义 | 第一章 | 第3条 | 第3条第(2)款 | 第3条:定义 正文(换行符)(2)正文 |
以下是 xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<DIVISION>
<TITLE>
<TI>
<P>
<HT TYPE="ITALIC">CHAPTER I</HT>
</P>
</TI>
<STI>
<P>
<HT TYPE="BOLD">
<HT TYPE="ITALIC">Chapther I Title</HT>
</HT>
</P>
</STI>
</TITLE>
<ARTICLE IDENTIFIER="001">
<TI.ART>Article 1</TI.ART>
<STI.ART>Article 1 Title</STI.ART>
<PARAG IDENTIFIER="001.001">
<NO.PARAG>1.</NO.PARAG>
<ALINEA>
<P>Article 1 : Paragraph 1 Main Textt</P>
<LIST TYPE="alpha">
<ITEM>
<NP>
<NO.P>(a)</NO.P>
<TXT>(a) Text</TXT>
<P>
<LIST TYPE="roman">
<ITEM>
<NP>
<NO.P>(i)</NO.P>
<TXT>(i) text</TXT>
</NP>
</ITEM>
<ITEM>
<NP>
<NO.P>(ii)</NO.P>
<TXT>(ii) text</TXT>
</NP>
</ITEM>
</LIST>
</P>
</NP>
</ITEM>
<ITEM>
<NP>
<NO.P>(b)</NO.P>
<TXT>(b) text</TXT>
</NP>
</ITEM>
</LIST>
</ALINEA>
</PARAG>
<PARAG IDENTIFIER="001.002">
<NO.PARAG>2.</NO.PARAG>
<ALINEA>Article 1 : Paragraph 2 Main Text</ALINEA>
</PARAG>
</ARTICLE>
<ARTICLE IDENTIFIER="002">
<TI.ART>Article 2</TI.ART>
<STI.ART>Article 2 Title</STI.ART>
<PARAG IDENTIFIER="002.001">
<NO.PARAG>1.</NO.PARAG>
<ALINEA>
<P>Article 2 : Paragraph 1 Main Text</P>
<LIST TYPE="alpha">
<ITEM>
<NP>
<NO.P>(a)</NO.P>
<TXT>(a) text;</TXT>
</NP>
</ITEM>
<ITEM>
<NP>
<NO.P>(b)</NO.P>
<TXT>(b) text</TXT>
</NP>
</ITEM>
</LIST>
</ALINEA>
</PARAG>
<PARAG IDENTIFIER="002.002">
<NO.PARAG>2.</NO.PARAG>
<ALINEA>Article 2 : Paragraph 2 Main Text.</ALINEA>
</PARAG>
<PARAG IDENTIFIER="002.003">
<NO.PARAG>3.</NO.PARAG>
<ALINEA>
<P>Article 2 : Paragraph 3 Main Text</P>
<LIST TYPE="alpha">
<ITEM>
<NP>
<NO.P>(a)</NO.P>
<TXT>(a) text</TXT>
</NP>
</ITEM>
</LIST>
</ALINEA>
</PARAG>
<PARAG IDENTIFIER="002.004">
<NO.PARAG>4.</NO.PARAG>
<ALINEA>Article 2 : Paragraph 4 Main Text</ALINEA>
</PARAG>
</ARTICLE>
<ARTICLE IDENTIFIER="003">
<TI.ART>Article 3</TI.ART>
<STI.ART>Definitions</STI.ART>
<ALINEA>
<P>Article 3: Definitions Main Text</P>
<LIST
TYPE="ARAB">
<ITEM>
<NP>
<NO.P>(1)</NO.P>
<TXT>(1) text</TXT>
</NP>
</ITEM>
<ITEM>
<NP>
<NO.P>(2)</NO.P>
<TXT>(2) text</TXT>
</NP>
</ITEM>
</LIST>
</ALINEA>
</ARTICLE>
</DIVISION>
以下是我当前的所有代码
import pandas as pd
from bs4 import BeautifulSoup
import unicodedata
from bs4 import BeautifulSoup
def determine_level(bs4_object):
if "subsection" in bs4_object.find('TI').text.lower():
c6 = unicodedata.normalize("NFKD", bs4_object.find("TI").text)
if (bs4_object.find("STI") is not None):
t6 = unicodedata.normalize("NFKD", bs4_object.find("STI").text)
return 6, c6, t6
return 6, c6, ''
elif "sub-section" in bs4_object.find('TI').text.lower():
c6 = unicodedata.normalize("NFKD", bs4_object.find("TI").text)
if (bs4_object.find("STI") is not None):
t6 = unicodedata.normalize("NFKD", bs4_object.find("STI").text)
return 6, c6, t6
return 6, c6, ''
elif "section" in bs4_object.find('TI').text.lower():
c5 = unicodedata.normalize("NFKD", bs4_object.find("TI").text)
if (bs4_object.find("STI") is not None):
t5 = unicodedata.normalize("NFKD", bs4_object.find("STI").text)
return 5, c5, t5
return 5, c5, ''
elif "chapter" in bs4_object.find('TI').text.lower():
c4 = unicodedata.normalize("NFKD", bs4_object.find("TI").text)
if (bs4_object.find("STI") is not None):
t4 = unicodedata.normalize("NFKD", bs4_object.find("STI").text)
return 4, c4, t4
return 4, c4, ''
elif "title" in bs4_object.find('TI').text.lower():
c3 = unicodedata.normalize("NFKD", bs4_object.find("TI").text)
if (bs4_object.find("STI") is not None):
t3 = unicodedata.normalize("NFKD", bs4_object.find("STI").text)
return 3, c3, t3
return 3, c3, ''
elif "part" in bs4_object.find('TI').text.lower():
c2 = unicodedata.normalize("NFKD", bs4_object.find("TI").text)
if (bs4_object.find("STI") is not None):
t2 = unicodedata.normalize("NFKD", bs4_object.find("STI").text)
return 2, c2, t2
return 2, c2, ''
else:
"Cannot be determined"
def scrape_file():
# Reading data from the xml file
with open('./info.xml', 'r') as f:
data = f.read()
info_data = BeautifulSoup(data, 'xml')
articles = info_data.find_all("ARTICLE")
lst = []
for index, article in enumerate(articles):
c2 = None
t2 = None
c3 = None
t3 = None
c4 = None
t4 = None
c5 = None
t5 = None
c6 = None
t6 = None
t7 = None
c7 = None
c8 = None
# Article level 7
c7 = unicodedata.normalize("NFKD", article.find("TI.ART").text)
if article.find("STI.ART"):
t7 = unicodedata.normalize("NFKD", article.find("STI.ART").text)
# Determining the number and levels of parents
article_parents = article.findParents('DIVISION')
for parent in reversed(article_parents):
level_determined = determine_level(parent)
if level_determined is None:
continue
elif 2 == level_determined[0]:
c2 = level_determined[1].title()
t2 = level_determined[2].capitalize()
elif 3 == level_determined[0]:
c3 = level_determined[1].split(" ")[0].capitalize() + ' ' + \
level_determined[1].split(" ")[1]
citations = (c2, c3)
c3 = ', '.join(str(citation)
for citation in citations if citation is not None)
t3 = level_determined[2].capitalize()
elif 4 == level_determined[0]:
c4 = level_determined[1].split(" ")[0].capitalize() + ' ' + \
level_determined[1].split(" ")[1]
citations = (c3, c4)
c4 = ', '.join(str(citation)
for citation in citations if citation is not None)
t4 = level_determined[2].capitalize()
elif 5 == level_determined[0]:
c5 = level_determined[1].split(" ")[0].capitalize() + ' ' + \
level_determined[1].split(" ")[1]
citations = (c4, c5)
c5 = ', '.join(str(citation)
for citation in citations if citation is not None)
t5 = level_determined[2].capitalize()
elif 6 == level_determined[0]:
c6 = level_determined[1].split(" ")[0].capitalize() + ' ' + \
level_determined[1].split(" ")[1]
citations = (c5, c6)
c6 = ', '.join(str(citation)
for citation in citations if citation is not None)
t6 = level_determined[2].capitalize()
# Rest of text
article_text = ""
alinea = article.find_all("ALINEA")
#I believe the problem starts here
for index, p in enumerate(alinea, 1):
txt = p.find_all("TXT")
if p.find_previous_sibling():
# Captures Alinea text of parent
if p.find_previous_sibling().name == "NO.PARAG":
# Used for main text of paragraph
if p.contents[0].name == "P":
article_text = p.contents[0].text
c8 = c7 + '.' + p.find_previous_sibling().text.replace('.', '')
parent_citation = c8
# used for definitions -- do not have a "NO.PARAG" before ALINEA
article_text = p.text
txt = p.find_all("TXT")
for index, t in enumerate(txt, 1):
if p.find_previous_sibling():
if t.find_previous_sibling().name == 'NO.P':
citation_level_8 = parent_citation + p.find_previous_sibling().text
article_text = t.text
lst.append(
[c2, t2, c3, t3, c4, t4,
c5, t5, c6, t6, t7,
c7, c8, article_text])
df = pd.DataFrame.from_records(
lst, columns=["T2", "T3", "T4","T5", "T6", "T7",
"C2", "C3", "C4", "C5", "C6", "C7","C8", "Text"]
)
提前感谢您的任何帮助/指导
答:
0赞
aspen l
7/14/2023
#1
我用这个,pip install lxml Lib:https://lxml.de/xpathxslt.html 我的示例,它解析所有级别,只需过滤掉您不需要的东西,快速示例:https://github.com/spawnmarvel/test/blob/main/xmlwalk.jpg
这里的快速例子是必须改进。
# pip install lxml
from lxml import etree as et
def xml_to_dict(xmlfile):
msg = {}
count = 1
with open(xmlfile) as r:
xml = r.read()
# print(type(xml))
parser = et.XMLParser(recover=True, collect_ids=True)
root = et.fromstring(bytes(xml, encoding="utf-8"), parser)
x = root.xpath("//*")# get all
#x = root.xpath("//*[@id]")# get all
for y in x:
key = et.QName(y).localname
print(key)
value = y.text
print(value)
i = y.attrib.get("id")
print(i)
# the rest is for making a dict and using it later if needed
if key in msg:
key = key + str(count)
count += 1
tmp = {key:value}
msg.update(tmp)
for k, v in msg.items():
print(k, ":", v)
#call
xml_to_dict("test.xml")
Xml 示例文件
{<?xml version="1.0"?>
<company>
<name>Prod</name>
<sys id="1001">
<name>sr1</name>
<task>dest</task>
</sys>
<sys id="1002">
<name>sr2</name>
<task>dest</task>
</sys>
<sys id="1003">
<name>sr3</name>
<task>source</task>
</sys>
</company>}
0赞
Parfait
7/14/2023
#2
考虑一下 XSLT,这是一种用于转换 XML 文件的特殊用途语言,可用于为 Pandas 表格结构生成更平坦的版本。使用第三方库(底层解析器),Python 可以运行 XSLT 1.0 脚本。lxml
'xml'
BeautifulSoup
使用解析器,pandas.read_xml
在解析为数据帧之前支持 XSLT 转换,所有这些都适用于一行代码!lxml
XSLT(另存为 .xsl 文件,一种特殊的 .xml 文件)
- 将节点解析到最低级别并向下复制祖先,跨节点(即行)重复值。
- 重点介绍三个部分:带子节点、不带子节点、所有节点。
ALINEA
P
ALINEA
P
TXT
- 使用(类似于 Python 方法)来避免重复代码。
call-template
def
- 使用具有条件逻辑的多个变量来连接值。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template name="get_titles">
<xsl:param name="curr_node"/>
<xsl:variable name="sect1">
<xsl:if test="preceding-sibling::NO.PARAG">
<xsl:value-of select="concat('.', translate(preceding-sibling::NO.PARAG, '.', ''))"/>
</xsl:if>
</xsl:variable>
<xsl:variable name="sect2">
<xsl:if test="ancestor::PARAG/NO.PARAG and name()='TXT'">
<xsl:value-of select="concat('.', translate(ancestor::PARAG/NO.PARAG, '.', ''))"/>
</xsl:if>
</xsl:variable>
<xsl:variable name="sect3">
<xsl:if test="name()='TXT'">
<xsl:value-of select="preceding-sibling::NO.P"/>
</xsl:if>
</xsl:variable>
<Chapter_Title><xsl:value-of select="ancestor::DIVISION/TITLE/TI"/></Chapter_Title>
<Chapter_Subtitle><xsl:value-of select="ancestor::DIVISION/TITLE/STI"/></Chapter_Subtitle>
<Article_Title><xsl:value-of select="ancestor::ARTICLE/TI.ART"/></Article_Title>
<Article_Subtitle><xsl:value-of select="ancestor::ARTICLE/STI.ART"/></Article_Subtitle>
<Aline>
<xsl:value-of select="concat(ancestor::ARTICLE/TI.ART, $sect1, $sect2, $sect3)"/>
</Aline>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="descendant::ALINEA[not(P)]|descendant::ALINEA[P]|descendant::TXT"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ALINEA[P]">
<row>
<xsl:call-template name="get_titles"/>
<TEXT><xsl:value-of select="P"/></TEXT>
</row>
</xsl:template>
<xsl:template match="ALINEA[not(P)]">
<row>
<xsl:call-template name="get_titles"/>
<TEXT><xsl:value-of select="text()"/></TEXT>
</row>
</xsl:template>
<xsl:template match="TXT">
<TEXT>
<xsl:variable name="list">
<xsl:if test="ancestor::LIST[name(..) != 'ALINEA']">
<xsl:value-of select="concat(ancestor::LIST/preceding-sibling::P, ' (line break) ', TXT)"/>
</xsl:if>
</xsl:variable>
<xsl:call-template name="get_titles"/>
<TEXT><xsl:value-of select="concat($list, text())"/></TEXT>
</TEXT>
</xsl:template>
</xsl:stylesheet>
Python(一行代码!
article_df = pd.read_xml("myInput.xml", stylesheet="myStyle.xsl")
评论