簡體   English   中英

使用 LXML Python 處理 XML 文件極其緩慢

[英]Processing of XML files excruciatingly slow with LXML Python

我正在處理 XML 文檔,如下所示。

<tok lemma="i" xpos="CC">e</tok> 
<tok lemma="que" xpos="CS">que</tok> 
<tok lemma="aquey" xpos="PD0MP0">aqueys</tok> 
<tok lemma="marit" xpos="NCMP000">marits</tok> 
<tok lemma="estar" xpos="VMIP3P0">stiguen</tok>  
[...]
<tok lemma="habitar" xpos="VMIP3P0">habiten</tok> 
<tok lemma="en" xpos="SPS00">en</tok>
<tok lemma="aquex" xpos="PD0FS0">aqueix</tok> 
<tok lemma="terra" xpos="NCMS000">món</tok>
[...]
<tok lemma="viure" xpos="VMIP3P0">viuen</tok> 
<tok lemma="en" xpos="SPS00">en</tok>
<tok lemma="aquex" xpos="PD0FP0">aqueixes</tok> 
<tok lemma="casa" xpos="NCFP000">cases</tok>

每當滿足某些條件時,我都需要更改某些元素的屬性。 在@LMC 的幫助下(請參閱: https://stackoverflow.com/questions/73545510/python-and-lxml-extremely-slow-more-efficient-code/73545789 )我優化了我必須處理 Z0F635D0E0F3874FFF8B581C13 的初始代碼文件。 這是我現在使用的代碼的精確副本。

# coding: utf-8
import os
import lxml.etree as et


ROOT = '/Path-to-input-xml-files'
ext = ('.xml')


def xml_change(root_element):


    for el in root.xpath('//tok[following-sibling::tok[1][starts-with(@xpos, "N")]]'):        
                          
        if el.text == 'aquest' or el.text == 'Aquest' or el.text == 'AQUEST' or el.text == 'aquast' or el.text == 'Aquast' or el.text == 'AQUAST' or el.text == 'aqast' or el.text == 'Aqast' or el.text == 'AQAST' or el.text == 'aqax' or el.text == 'Aqax' or el.text == 'AQAX' or el.text == 'aqest' or el.text == 'Aqest' or el.text == 'AQEST' or el.text == 'aqet' or el.text == 'Aqet' or el.text == 'AQET' or el.text == 'aquet' or el.text == 'Aquet' or el.text == 'AQUET':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0MS0')
            el.set('lemma', 'aquest')



        elif el.text == 'aquel' or el.text == 'Aquel' or el.text == 'AQUEL' or el.text == 'aquell' or el.text == 'Aquell' or el.text == 'AQUELL' or el.text == 'aqal' or el.text == 'Aqal' or el.text == 'AQAL' or el.text == 'aqual' or el.text == 'Aqual' or el.text == 'AQUAL' or el.text == 'aqueyl' or el.text == 'Aqueyl' or el.text == 'AQUEYL' or el.text == 'aqueil' or el.text == 'Aqueil' or el.text == 'AQUEIL':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0MS0')
            el.set('lemma', 'aquell')
       

        elif el.text == 'aquests' or el.text == 'Aquests' or el.text == 'AQUESTS' or el.text == 'aquets' or el.text == 'Aquets' or el.text == 'AQUETS' or el.text == 'aquetz' or el.text == 'Aquetz' or el.text == 'AQUETZ':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0MP0')
            el.set('lemma', 'aquest')

        elif el.text == 'aquells' or el.text == 'Aquells' or el.text == 'AQUELLS' or el.text == 'aqueys' or el.text == 'Aqueys'  or el.text == 'AQUEYS' or el.text == 'aqueyls'  or el.text == 'Aqueyls'  or el.text == 'AQUEYLS':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0MP0')
            el.set('lemma', 'aquell')

        elif el.text == 'aquestas' or el.text == 'Aquestas' or el.text == 'AQUESTAS' or el.text == 'aquestes' or el.text == 'Aquestes' or el.text == 'AQUESTES' or el.text == 'aquetes' or el.text == 'Aquetes' or el.text == 'AQUETES' or el.text == 'aquastes' or el.text == 'Aquastes' or el.text == 'AQUASTES' or el.text == 'aquastas' or el.text == 'Aquastas' or el.text == 'AQUASTAS'  or el.text == 'aqastas' or el.text == 'Aqastas' or el.text == 'AQASTAS' or el.text == 'aquexas' or el.text == 'Aquexas' or el.text == 'AQUEXAS':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0FP0')
            el.set('lemma', 'aquest')
        
        elif el.text == 'aqualas' or el.text == 'Aqualas' or el.text == 'AQUALAS' or el.text == 'aquelas' or el.text == 'Aquelas' or el.text == 'AQUELAS' or el.text == 'aqueles' or el.text == 'Aqueles' or el.text == 'AQUELES' or el.text == 'aquellas' or el.text == 'Aquellas' or el.text == 'AQUELLAS' or el.text == 'aquelles' or el.text == 'Aquelles' or el.text == 'AQUELLES' or el.text == 'aquales' or el.text == 'Aquales' or el.text == 'AQUALES' or el.text == 'aqueylas' or el.text == 'Aqueylas' or el.text == 'AQUEYLAS' or el.text == 'aqueyles' or el.text == 'Aqueyles' or el.text == 'AQUEYLES':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0FP0')
            el.set('lemma', 'aquell')

        elif el.text == 'aquesta' or el.text == 'Aquesta' or el.text == 'AQUESTA' or el.text == 'aquasta' or el.text == 'Aquasta' or el.text == 'AQUASTA' or el.text == 'aquaste' or el.text == 'Aquaste' or el.text == 'AQUASTE' or el.text == 'aqasta' or el.text == 'Aqasta' or el.text == 'AQASTA' or el.text == 'aquetes' or el.text == 'aqaste' or el.text == 'Aqaste' or el.text == 'AQASTE' or el.text == 'aquaxa' or el.text == 'Aquaxa' or el.text == 'AQUAXA' or el.text == 'aqexa' or el.text == 'Aqexa'  or el.text == 'AQEXA' or el.text == 'aquexa' or el.text == 'Aquexa' or el.text == 'AQUEXA':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0FS0')
            el.set('lemma', 'aquest')

        elif el.text == 'aquala' or el.text == 'Aquala' or el.text == 'AQUALA' or el.text == 'aquale' or el.text == 'Aquale' or el.text == 'AQUALE'  or el.text == 'aquela' or el.text == 'Aquela' or el.text == 'AQUELA' or el.text == 'aqueyla' or el.text == 'Aqueyla' or el.text == 'AQUEYLA' or el.text == 'aqueila' or el.text == 'Aqueila' or el.text == 'AQUEILA':

            print('Current value is:', el.get('lemma'), el.get('xpos'))
            el.set('xpos', 'DD0FS0')
            el.set('lemma', 'aquell')
# iterate all dirs
for root, dirs, files in os.walk(ROOT):

    # iterate all files
    for file in files:
        if file.endswith(ext):
            # join root dir and file name
            file_path = os.path.join(ROOT, file)

            # load root element from file
            root = et.parse(file_path).getroot()

            # recursively change  elements from xml
            xml_change(root)
    
        

            # init tree object from root
            tree = et.ElementTree(root)

            # save cleaned xml tree object to file. Important to specify encoding
                
            tree.write(file_path.replace('.xml', '-clean.xml'), encoding='utf-8', doctype='<!DOCTYPE document SYSTEM "estcorpus.dtd">', xml_declaration=True)
            

@LMC 的建議確實很有用,通過涉及一些 xml 文檔的測試運行,我注意到優化導致速度略有提高。 但是,我認為我所做的事情存在根本性的問題,因為已經 38 小時了,而這個過程還沒有完成。 當然,有很多條件需要檢查,處理這類文本文檔應該很慢。 但是 38 小時並指望一台功能強大的計算機(帶有 M1 max 芯片的 Mac Studio)? 我從來沒有經歷過這樣的事情。

我提供了一些更多的信息,這些信息可能對在類似項目上有一些工作經驗的人有用。 我正在處理的 XML 文檔的總量為 395,總大小為 585 MB。 最大的文檔為 34MB,最小的為 3KB,但大多數文檔在 100KB 到 4MB 之間。

現在,奇怪的事情來了。 處理速度似乎與處理文件的長度無關。 就好像處理是在突發中完成的。 突然間,我得到一堆打印語句(來自print('Current value is:', el.get('lemma'), el.get('xpos'))) ,表明找到了匹配項和一堆生成不同大小的output文檔。

但是,在這之后的很多小時,go 都不會生成任何新的打印語句或 output 文檔。 這是創建 output 文件的目錄的幾個屏幕截圖,以便您可以看到創建新文件之間的時間間隔。

在此處輸入圖像描述

在此處輸入圖像描述

在此處輸入圖像描述

我看不出文件大小與處理它們所需的時間之間有很大的相關性。 無論如何,即使文件很大,在我看來 17 小時處理一個文件也有點太多了。 你怎么看? 我錯了,這是這類工作應該預期的,還是我做錯了什么? 我能做些什么來加快速度嗎?

這里發生了一些病態的事情,不可能花這么長時間。 我會嘗試找出原因的事情:

(a) 查看是否有任何網絡流量產生。

(b) 看一下memory的消耗看是否有過多的分頁或者垃圾回收

(c) 將您對每個文檔所做的處理減少到微不足道的程度,以查看問題是與解析/保存文檔有關,還是與您對每個文檔進行的處理有關。

變量命名可能存在問題,因為root變量在代碼中有 2 個含義,這可能會導致memory 問題
給出下面的例子

>>> t = os.walk('/home/lmc/tmp/a')
>>> for root, dirs, files in t:
...     print(root)
...     root= uuid.uuid4()
...     print(root)
... 
/home/lmc/tmp/a
ab5839a8-43b5-4d9d-bbb3-4836c612abaf
/home/lmc/tmp/a/b
7a8ba22e-7a02-45d6-82ce-538e11b70e7d
/home/lmc/tmp/a/b/c
de7c0e08-edc4-43e6-9bc1-9b1d7dd7e9db
/home/lmc/tmp/a/b/c/f
2536e2dc-11d1-4b41-86fd-128c3eeaddbc
/home/lmc/tmp/a/b/c/f/g
7d7e61b0-31d4-4af4-9097-540fc2bbac1c
/home/lmc/tmp/a/b/d
1a671eb2-7efe-4dc4-891b-94d1710ef638
/home/lmc/tmp/a/b/d/e
420d5228-44f1-493d-9dae-e2005c4e0f61

因此, root可能會在該列表的每個實例上保存一個 xml 元素,而不是目錄名稱。
從解析樹中刪除 withespace 也可以減少樹中的節點數

for root, dirs, files in os.walk(ROOT):

    # iterate all files
    for file in files:
        if file.endswith(ext):
            # join root dir and file name
            file_path = os.path.join(ROOT, file)

            # load root element from file
            parser = etree.XMLParser(remove_blank_text=True)
            root_ele = et.parse(file_path, parser).getroot()

            # recursively change  elements from xml
            xml_change(root_ele)

最后,按照建議,更改 xpath 搜索策略也會有所不同

for el in root.xpath('//tok[starts-with(@xpos, "N")]/preceding-sibling::tok[1]'):

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM