You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python SAX Parser解析多XML存CSV仅保留最后文件数据问题

问题原因及解决办法

核心问题1:CSV写入时的覆盖

你每次调用df.to_csv(fp2, index=False)都是用默认的覆盖模式(mode='w')写入文件,所以每解析一个XML,就会把之前的CSV内容清空,最后只留下最后一个文件的数据。

核心问题2:SAX解析器状态未重置

你用同一个parser实例解析所有XML文件,之前解析的属性值(比如self.rc、self.dd)不会自动清空,可能导致后面的文件数据混入之前的旧值。

核心问题3:characters方法的误用

SAX的characters()方法会被多次触发(比如元素内容有换行、被其他节点分割时),直接在这里构建行数据并写入,很可能收集到不完整的内容,而且会重复写入数据。


修正后的代码示例

第一步:重构SAX Handler类

把数据收集逻辑移到endElement(),并在每次解析新文件前重置Handler的状态:

import os
import pandas as pd
from xml.sax import make_parser, handler

class CaseHandler(handler.ContentHandler):
    def __init__(self):
        # 初始化所有字段
        self.reset_state()
        # 存储所有案例数据的列表
        self.all_rows = []
        self.current_tag = ""

    def reset_state(self):
        # 重置所有属性,避免旧数据干扰
        self.rc = ""
        self.dd = ""
        self.can = ""
        self.fn = ""
        self.loc = ""
        self.cn = ""
        self.ca = ""
        self.j = ""
        self.cl = ""
        self.cc = ""
        self.pc = ""
        self.cd = ""
        self.rn = ""
        self.current_tag = ""

    def startElement(self, name, attrs):
        # 记录当前正在解析的标签
        self.current_tag = name

    def characters(self, content):
        # 只在对应标签下收集内容,注意去空格
        content = content.strip()
        if not content:
            return
        if self.current_tag == "ReporterCite":
            self.rc += content
        elif self.current_tag == "DecisionDate":
            self.dd += content
        elif self.current_tag == "CaseName":
            self.can += content
        # 其他字段依次类推,对应你的XML标签名

    def endElement(self, name):
        # 当遇到整个案例的结束标签时(比如<Case>标签结束),收集数据
        if name == "Case":  # 这里替换成你XML中每个案例的根标签
            row = {
                "ReporterCite": self.rc,
                "DecisionDate": self.dd,
                "CaseName": self.can,
                "FileNum": self.fn,
                "CourtLocation": self.loc,
                "CourtName": self.cn,
                "CourtAbbrv": self.ca,
                "Judge": self.j,
                "CaseLength": self.cl,
                "CourtCite": self.cc,
                "ParallelCite": self.pc,
                "CitedCount": self.cd,
                "UCN": self.rn
            }
            self.all_rows.append(row)
            # 重置状态,准备解析下一个案例(如果一个XML中有多个案例)
            self.reset_state()

    def get_all_data(self):
        return self.all_rows

第二步:遍历文件并解析

每次解析新文件时,重置Handler状态,最后一次性写入CSV:

fp1 = "你的文件夹路径"
fp2 = "输出的CSV路径"

# 创建Handler实例
handler = CaseHandler()
parser = make_parser()
parser.setContentHandler(handler)

# 遍历所有XML文件
for dirpath, _, files in os.walk(fp1):
    for filename in files:
        fname = os.path.join(dirpath, filename)
        if fname.endswith('.xml'):
            print(f"正在解析: {fname}")
            # 重置Handler状态,避免旧文件数据干扰
            handler.reset_state()
            parser.parse(fname)

# 所有文件解析完成后,一次性写入CSV
cols = ["ReporterCite","DecisionDate","CaseName","FileNum","CourtLocation","CourtName","CourtAbbrv","Judge","CaseLength","CourtCite","ParallelCite","CitedCount","UCN"]
df = pd.DataFrame(handler.get_all_data(), columns=cols)
df.to_csv(fp2, index=False)

替代方案:追加写入CSV

如果数据量很大,不想全部存在内存里,可以每次解析完一个文件就追加写入CSV,但要注意只在第一次写入表头:

fp1 = "你的文件夹路径"
fp2 = "输出的CSV路径"
cols = ["ReporterCite","DecisionDate","CaseName","FileNum","CourtLocation","CourtName","CourtAbbrv","Judge","CaseLength","CourtCite","ParallelCite","CitedCount","UCN"]
# 判断文件是否存在,决定是否写入表头
file_exists = os.path.isfile(fp2)

for dirpath, _, files in os.walk(fp1):
    for filename in files:
        fname = os.path.join(dirpath, filename)
        if fname.endswith('.xml'):
            print(f"正在解析: {fname}")
            # 每次解析新文件都创建新的Handler实例
            handler = CaseHandler()
            parser = make_parser()
            parser.setContentHandler(handler)
            parser.parse(fname)
            # 追加写入CSV
            df = pd.DataFrame(handler.get_all_data(), columns=cols)
            df.to_csv(fp2, index=False, mode='a', header=not file_exists)
            # 第一次写入后,标记文件已存在,后续不再写表头
            file_exists = True

内容的提问来源于stack exchange,提问作者PythonKS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 02:56:52