我正在使用LXML从一堆XML文件中提取信息。我想知道我处理这个任务的方式是否是最有效的。现在我在LXML中使用xpath()方法来识别特定的目标,然后在lxml中使用不同的方法来提取这些信息。
正如我在前面的问题(用LXML Python处理XML文件非常慢)中注意到的,当文件达到一定大小时,使用etree.parse(file)或etree.parse(file). getroot()会非常慢。它们不需要很大,12MB的XML文件已经很慢了。
我现在想知道的是是否有更快的替代方法。在LXML文档中,使用XPath类可能比使用XPath()方法更快。我遇到的问题是XPath类处理Element对象,而不是etree.parse()生成的ElementTree对象。
我所需要的是一些比我现在所做的更快的替代方法,基本上是下面内容的一些变体。这只是我用来从相关XML文件中提取信息的许多同类脚本中的一个示例。如果您认为是使用正则表达式导致了速度缓慢,我做过一些测试,其中使用了XPath root_element.xpath('//tok[text()="lo"]')
,但没有使用正则表达式。处理20到30MB文件所花费的时间可能会少一些,但不会少很多。无论我对所有这些文件做什么,如果它涉及到检查XPath表达式并执行某些操作的for循环,它只是需要很长的时间比人们所期望的使用最新的Python和Mac与M1 Max芯片。我有一个旧的笔记本电脑,当我尝试同样的事情,它需要3天!!
XMLDIR = "/path_to_dir_with_xml_files"
myCSV_FILE = "/path_to_some_csv_file.csv"
ext = ".xml"
def xml_extract(root_element):
for el in root_element.xpath('//tok[re:match(., "^[EeLl][LlOoAa][Ss]*$") and not(starts-with(@xpos, "D"))]',
namespaces={"re": "http://exslt.org/regular-expressions"}):
target = el.text
# allRelevantElements = el.xpath('preceding::tok[position() >= 1 and not(position() > 6)]/following::tok[position() >= 1 and not(position() > 6)]')
RelevantPrecedingElements = el.xpath(
"preceding::tok[position() >= 1 and not(position() > 6)]"
)
RelevantFollowingElements = el.xpath(
"following::tok[position() >= 1 and not(position() > 6)]"
)
context_list = []
for elem in RelevantPrecedingElements:
elem_text = "".join(elem.itertext())
assert elem_text is not None
context_list.append(elem_text)
# adjective = '<' + str(el.text) + '>'
target = f"<{el.text}>"
print(target)
context_list.append(target)
following_context = []
for elem in RelevantFollowingElements:
elem_text = "".join(elem.itertext())
assert elem_text is not None
following_context.append(elem_text)
lema_fol = el.xpath('following::tok[1]')[0].get('lemma') if el.xpath('following::tok[1]') else None
lema_prec = el.xpath('preceding::tok[1]')[0].get('lemma') if el.xpath('preceding::tok[1]') else None
xpos_fol = el.xpath('following::tok[1]')[0].get('xpos') if el.xpath('following::tok[1]') else None
xpos_prec = el.xpath('preceding::tok[1]')[0].get('xpos') if el.xpath('preceding::tok[1]') else None
form_fol = el.xpath('following::tok[1]')[0].text if el.xpath('following::tok[1]') else None
form_prec = el.xpath('preceding::tok[1]')[0].text if el.xpath('preceding::tok[1]') else None
context = " ".join(context_list)
print(f"Context is: {context}")
llista = [
context,
lema_prec,
xpos_prec,
form_prec,
target,
lema_fol,
xpos_fol,
form_fol,
]
writer = csv.writer(csv_file, delimiter=";")
writer.writerow(llista)
with open(myCSV_FILE, "a+", encoding="UTF8", newline="") as csv_file:
for root, dirs, files in os.walk(XMLDIR):
for file in files:
if file.endswith(ext):
file_path = os.path.join(XMLDIR, file)
file_root = et.parse(file_path).getroot()
doc = file
xml_extract(file_root)
下面是一段XML文档的示例,其中包含了我正在使用的XPath表达式的匹配项。函数'xml_extract'将在此匹配项上被调用,不同的信息段将被正确地提取并存储到CSV文件中。这工作得很好,并且做了我想要做的事情,但是它太慢了。
<tok id="w-6387" ord="24" lemma="per" xpos="SPS00">per</tok>
<tok id="w-6388" ord="25" lemma="algun" xpos="DI0FP0">algunes</tok>
<tok id="w-6389" ord="26" lemma="franquesa" xpos="NCFP000">franqueses</tok>
<tok id="w-6390" nform="el" ord="27" lemma="el" xpos="L3MSA">lo</tok>
<tok id="w-6391" ord="28" lemma="haver" xpos="VMIP1S0">hac</tok>
编辑:
提供一些额外的相关信息,可能对试图帮助我的@人有所帮助。前面的XML内容相当直接,但文档的结构有时会变得相当复杂。我正在研究中世纪的文本,这些文本中的XML标签可能包含不同类型的信息。"tok"标签包含语言注解,这是我感兴趣的。在正常情况下,XML看起来与前面的示例类似,但是在某些情况下,编辑会包含其他带有手稿元数据的标记(例如,是否存在抄写员的修改或删除,是否存在新的节或新的页,节的标题,等等)。这可以让你对能找到什么有所了解,也许还能帮助你理解我为什么要使用这种方法。在这个阶段,大多数元数据对我来说都不相关。相关的是包含在"dtok"标签中的信息。当缩略形式必须分解为独立的单词时,这些标签是"tok"的子标签。这个系统允许将缩略语可视化为单个单词,但提供有关其组成部分的语言信息。标签是自动完成的,但错误百出。我提取信息的目标之一是能够检测模式,从而帮助我们以半自动化的方式改进语言注解。
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE document SYSTEM "estcorpus.dtd">
<TEI title="Full title" name="Doc_I44">
<!--this file comes from the Stand-by folder: it needs to be checked because it has inaccurate xml tags-->
<header>
<filiation type="obra">Book title</filiation>
<filiation type="autor">Author name</filiation>
<filiation type="data">Segle XVIIa</filiation>
<filiation type="tipologia">Letters</filiation>
<filiation type="dialecte">Oc:V</filiation>
</header>
<text section="logic" lang="català" analyse="norest">
<pb n="1r" type="folio" id="e-1" />
<space />
<space />
<mark name="empty line" />
<add>
<tok form="IX" id="w-384" ord="1" lemma="IX" xpos="Z">IX</tok>
</add>
<mark name="lang=Latin" />
<tok id="w-385" ord="2" lemma="morir" xpos="TMMS">Mort</tok>
<tok id="w-386" ord="3" lemma="de" xpos="SPC00">de</tok>
<tok id="w-387" ord="4" lemma="sant" xpos="NCMS000">sent</tok>
<tok id="w-388" ord="5" lemma="Vicent" xpos="NP00000">Vicent</tok>
<tok id="w-389" ord="6" lemma="Ferrer" xpos="NPCS00">Ferrer</tok>
<tok id="w-99769" ord="23" xpos="CC" lemma="i">e</tok>
<tok id="w-99770" ord="24" lemma="jo" xpos="PP1CSN00">jo</tok>
<tok id="w-99771">dar-los
<dtok form="dar" id="d-99771-1" ord="25" lemma="dar" xpos="VMN0000" />
<dtok form="los" id="d-99771-2" ord="26" lemma="els" xpos="L3CP0" /></tok>
<tok id="w-99772" ord="27" lemma="haver" xpos="V0IF3S0">hé</tok>
<tok id="w-99773" ord="28" lemma="diner" xpos="NCMP000">diners</tok>
<space />
<mark name="/lang" />
<foreign name="Latin">
<tok id="w-390" ord="7" lemma="any" xpos="CC">Annum</tok>
</foreign>
</text>
</TEI>
这是使用LXML处理XML文件的唯一方法吗?还是有更快的方法?目前,处理一个30MB的文件并获取与特定XPath表达式相关的信息需要21分钟。我使用的是Python 3.11和一台功能强大的计算机。我不禁想到,一定有更有效的方法来完成我正在做的事情。我有大约400个文件在目录中。它需要永远每次我必须通过他们和做一些事情。
编辑2:
按照建议使用编译过的XPath表达式之后,我使用@Martin Honnen提供的修改过的代码运行了一个测试,下面是结果。我还没有尝试其他推荐的替代方法。当我尝试时,我会报告。
File sizes:
A-01.xml : 13.2 MB
A-02.xml : 31.4 MB
A-03.xml : 7.7 MB
A-04.xml : 11.6 MB
I-44.xml : 22.9 MB
Original run:
File: seconds
A-01.xml ➝ 56.845274686813354
A-02.xml ➝ 1281.4102880954742
A-03.xml ➝ 80.60795021057129
A-04.xml ➝ 149.65892505645752
I-44.xml ➝ 983.7257928848267
With compiled XPath expressions:
File: seconds
A-01.xml ➝ 59.663841009140015
A-02.xml ➝ 1533.5482828617096
A-03.xml ➝ 78.68556118011475
A-04.xml ➝ 149.15855598449707
I-44.xml ➝ 876.2536578178406
2条答案
按热度按时间mwecs4sa1#
你能试试
XMLPullParser()
吗,应该很快,而且不会阻塞你的机器。如果你有非常大的文件,你可以决定读取的哪一部分应该馈送到解析器。在我的例子中,我加载了整个XML,但在实际工作中一定不是这样的。输出:
或者,如果您对所有tok和dtok感兴趣:
输出:
e4eetjau2#
基于编译XPath表达式一次的建议,以及我的评论,即您实际上多次执行XPath求值前后的所有操作,而不是我尝试使用的一次
并检查这是否提高了性能。
作为另一种选择,我现在还尝试在Python列表上实现大多数比较,而不是在lxmlXPath中实现,我在XSLT 3中尝试过的东西就像Python3一样实现了(例如,首先用XPath选择所有
tok
元素,然后在XSLT中查找序列,或者在Python中查找所有toks的列表,以查找regexp匹配toks之一的索引,并查找前面的和/或在该序列/列表中跟随):作为一种替代方法,首先对于单个文件,SaxonC(https://saxonica.com/saxon-c/1199/)和XSLT 3如何使用下面这样的XSLT样式表(但是为了测试,我不得不注解掉所有示例中的
<!DOCTYPE ..>
节点,因为没有提供引用的DTD),这会很有趣:当然,XSLT 3 with Saxon也可以轻松地处理一个目录中的所有
.xml
文件,以生成单个输出文件(例如'csv'):最后一个示例可以使用SaxonC的Python API运行,例如: