亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何使用 Python 和 XSLT 文件迭代解析巨大的 XML 并寫入 CSV

如何使用 Python 和 XSLT 文件迭代解析巨大的 XML 并寫入 CSV

陪伴而非守候 2021-11-09 20:28:19
在處理大型 XML 文件時,我無法使用 XSLT 將 XML 展平和轉換為 CSV 文件。目前,我正在lxml使用 XSL 文件解析嵌套的 XML 文件來展平輸出,然后將輸出寫入 CSV 文件。我的 XML 看起來像這樣:<root>    <level1>        <level2>            <topid>1</topid>            <level3>                <subtopid>1</topid>                <level4>                    <subid>1</id>                    <descr>test</descr>                </level4>                <level4>                    <subid>2</id>                    <descr>test2</descr>                </level4>                ...            </level3>            ...        </level2>    </level1></root>我想最終得到以下 CSV 文件:topid,subtopid,subid,descr1,1,1,test1,1,2,test2....我的 XSLT:<?xml version="1.0" encoding="UTF-8" ?><xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="text" encoding="utf-8" use-character-maps="map"/><xsl:character-map name="map">    <xsl:output-character character="," string=" "/></xsl:character-map><xsl:strip-space elements="*"/><xsl:variable name="delimiter" select="','"/><xsl:variable name="newline" select="'&#xd;'" /><xsl:template match="/root">    <xsl:text>topid,subtopid,subid,descr</xsl:text>    <xsl:value-of select="$newline" />    <xsl:for-each select="level1/level2/level3/level4">        <xsl:value-of select="ancestor::root/level1/level2/topid" />        <xsl:value-of select="$delimiter" />        <xsl:value-of select="ancestor::root/level1/level2/level3/subtopid" />        <xsl:value-of select="$delimiter" />        <xsl:value-of select="subid" />        <xsl:value-of select="$delimiter" />        <xsl:value-of select="descr" />        <xsl:value-of select="$newline" />    </xsl:for-each></xsl:template>這對小文件很有用,但現在我想對 +- 2.5gb 的 XML 文件做同樣的事情。使用 etree.parse 會將其加載到內存中,這顯然不適用于較大的文件。我想在某個地方迭代,所以我沒有將 XML 文件加載到內存中并逐行寫入 CSV 行,同時仍然使用 XSLT 進行轉換。我正在使用 XSLT 文件,因為這是我知道(現在)如何展平嵌套的 XML 文件的唯一方法。
查看完整描述

3 回答

?
至尊寶的傳說

TA貢獻1789條經驗 獲得超10個贊

我寧愿在 Python 中使用 XSLT 3.0(甚至 2.0!),但還沒有時間弄清楚如何使用 Saxon/C。


另一種選擇是使用iterparse().


例子...


XML 輸入(固定為格式良好并添加第二個level3用于測試)


<root>

    <level1>

        <level2>

            <topid>1</topid>

            <level3>

                <subtopid>1</subtopid>

                <level4>

                    <subid>1</subid>

                    <descr>test</descr>

                </level4>

                <level4>

                    <subid>2</subid>

                    <descr>test2</descr>

                </level4>

            </level3>

            <level3>

                <subtopid>2</subtopid>

                <level4>

                    <subid>1</subid>

                    <descr>test</descr>

                </level4>

                <level4>

                    <subid>2</subid>

                    <descr>test2</descr>

                </level4>

            </level3>

        </level2>

    </level1>

</root>

Python


from lxml import etree

import csv


context = etree.iterparse("test.xml", events=("start", "end"))


fields = ("topid", "subtopid", "subid", "descr")


with open("test.csv", "w", newline="", encoding="utf8") as xml_data_to_csv:


    csv_writer = csv.DictWriter(xml_data_to_csv, fieldnames=fields,

                                delimiter=",", quoting=csv.QUOTE_MINIMAL)


    csv_writer.writeheader()


    topid = None

    subtopid = None

    values = {}


    for event, elem in context:

        tag = elem.tag

        text = elem.text


        if tag == "topid" and text:

            topid = text


        if tag == "subtopid" and text:

            subtopid = text


        if tag == "subid" and text:

            values["subid"] = text


        if tag == "descr" and text:

            values["descr"] = text


        if event == "start" and tag == "level4":

            # Build a dict containing all of the "fields" with default values of "Unknown".

            values = {key: "Unknown" for key in fields}


        if event == "end" and tag == "level4":

            values["topid"] = topid

            values["subtopid"] = subtopid

            csv_writer.writerow(values)


        elem.clear()

CSV 輸出


topid,subtopid,subid,descr

1,1,1,test

1,1,2,test2

1,2,1,test

1,2,2,test2


查看完整回答
反對 回復 2021-11-09
?
holdtom

TA貢獻1805條經驗 獲得超10個贊

一種可能性是使用 XSLT 3.0 流。這里有兩個挑戰:


(a) 使您的代碼可流式傳輸。如果沒有看到樣式表代碼,我們無法判斷這有多難。


(b) 安裝和運行流式 XSLT 3.0 處理器。這取決于您對 Python 環境的鎖定程度。如果必須在 Python 中完成,您可以嘗試安裝 Saxon/C。另一種方法是調用不同的環境,在這種情況下您有更多選擇,例如您可以在 Java 上運行 Saxon-EE。


之后


看你貼的代碼,比較奇怪


<xsl:for-each select="level1/level2/level3/level4">

    <xsl:value-of select="ancestor::root/level1/level2/topid" />

我懷疑您想輸出topid“當前”level2元素的 ,但這不是這樣做的(在 XSLT 1.0 中它將打印第一個的值level2/topic,在 XSLT 2.0+ 中將打印所有level2/topic元素的值。我懷疑你真的想要這樣的東西:


    <xsl:for-each select="level1/level2/level3/level4">

        <xsl:value-of select="ancestor::level2/topid" />

        <xsl:value-of select="$delimiter" />

        <xsl:value-of select="ancestor::level3/subtopid" />

        <xsl:value-of select="$delimiter" />

        <xsl:value-of select="subid" />

        <xsl:value-of select="$delimiter" />

        <xsl:value-of select="descr" />

        <xsl:value-of select="$newline" />

    </xsl:for-each>

這幾乎是可流式傳輸的,但不完全是。流式傳輸不允許您回到 toppid 和 subtopid 元素。使其可流式傳輸的最簡單方法可能是將這些元素的最新值保存在累加器中:


<xsl:accumulator name="topid" as="xs:string" initial-value="''">

  <xsl:accumulator-rule match="topid/text()" select="string(.)"/>

</xsl:accumulator>


<xsl:accumulator name="subtopid" as="xs:string" initial-value="''">

  <xsl:accumulator-rule match="subtopid/text()" select="string(.)"/>

</xsl:accumulator>

然后訪問這些值:


    <xsl:for-each select="level1/level2/level3/level4">

        <xsl:value-of select="accumulator-before('topid')" />

        <xsl:value-of select="$delimiter" />

        <xsl:value-of select="accumulator-before('subtopid')" />

        <xsl:value-of select="$delimiter" />

        <xsl:value-of select="subid" />

        <xsl:value-of select="$delimiter" />

        <xsl:value-of select="descr" />

        <xsl:value-of select="$newline" />

    </xsl:for-each>


查看完整回答
反對 回復 2021-11-09
?
守著星空守著你

TA貢獻1799條經驗 獲得超8個贊

Saxon/C 和 python 可以工作:

一位用戶已成功使用 Boost.Python 與 C++ 庫交互。

另一個用戶以不同的方式完成了接口:https : //github.com/ajelenak/pysaxon


查看完整回答
反對 回復 2021-11-09
  • 3 回答
  • 0 關注
  • 262 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號