如何使用Python与SAX高效读取解析大型XML文件?
问题描述
环境与项目背景
- 系统环境:Windows 11 / Python 3.8.10
- 使用IDE:Spyder、PyCharm
- 项目需求:处理大型XML文件,实现7*24小时并发解析、数据存入数据库,后续基于数据库数据生成新XML文件
目标XML结构示例
<PillCall XMLInstanceID="98089D9A-768A-4FA0-A7CD-DC5966EB5B06" PillCallID="49" VersionNumber="1.2"> </PillCall>
当前问题与尝试代码
基础SAX解析代码(Spyder可运行,PyCharm无输出)
尝试过ElementTree、BeautifulSoup均未成功,目前使用SAX编写了基础解析程序:
import xml.sax class XMLHandler(xml.sax.ContentHandler): def __init__(self): self.CurrentData = "" self.pillcall = "" self.pillcallid= "" self.vernum = "" # Call when an element starts def startElement(self, tag, attributes): self.CurrentData = tag if(tag == "PillCall"): print("*****PillCall*****") title = attributes["XMLInstanceID"] print("XMLInstanceID:=", title) # 想在这里输出多个属性值? # print(sorted() # create an XMLReader parser = xml.sax.make_parser() # turn off namepsaces parser.setFeature(xml.sax.handler.feature_namespaces, 0) # override the default ContextHandler Handler = XMLHandler() parser.setContentHandler( Handler ) parser.parse("xmltest10.xml")
- Spyder运行输出:
*****PillCall***** XMLInstanceID:= 98089D9A-768A-4FA0-A7CD-DC5966EB5B06 - PyCharm运行无任何输出
扩展SAX代码(未正常工作)
尝试添加endElement和characters方法,但未达到预期效果:
# Call when an elements ends def endElement(self, tag): if(self.CurrentData != "/PillCall"): print("End of PillCall:", self.pillcall) elif(self.CurrentData == "PillCallID"): print("PillCallID:=", self.pillcallid) elif(self.CurrentData == "VersionNumber"): print("VersionNumber:=", self.vernum) self.CurrentData = "" # Call when a character is read def characters(self, content): if(self.CurrentData == "PillCall"): self.pillcall = content elif(self.CurrentData == "qty"): self.pillcallid = content elif(self.CurrentData == "company"): self.vernum = content
需要实现的功能
- 完整读取XML中的所有数据(包括
PillCall的所有属性) - 循环输出解析得到的数据
- 将解析后的数据生成新的XML文件
解决方案
1. 修复SAX解析代码,读取所有属性
PillCall的属性都是元素的属性值,不是子元素文本,无需用characters方法处理。在startElement中直接遍历所有属性即可:
import xml.sax class XMLHandler(xml.sax.ContentHandler): def __init__(self): self.current_tag = "" self.pillcall_data = [] # 存储所有PillCall的属性数据 def startElement(self, tag, attributes): self.current_tag = tag if tag == "PillCall": # 读取当前PillCall的所有属性 pillcall_attrs = {key: attributes[key] for key in attributes.keys()} self.pillcall_data.append(pillcall_attrs) # 打印当前PillCall的所有属性 print("*****PillCall*****") for key, value in pillcall_attrs.items(): print(f"{key}:= {value}") # 初始化解析器 parser = xml.sax.make_parser() parser.setFeature(xml.sax.handler.feature_namespaces, 0) handler = XMLHandler() parser.setContentHandler(handler) parser.parse("xmltest10.xml") # 循环输出所有解析到的PillCall数据 print("\n=== 所有PillCall数据 ===") for idx, data in enumerate(handler.pillcall_data): print(f"第{idx+1}条PillCall:") for k, v in data.items(): print(f" {k}: {v}")
PyCharm无输出问题解决
- 检查PyCharm运行配置,确保选择了Python 3.8.10解释器
- 使用XML文件的绝对路径(如
C:/project/xmltest10.xml)避免路径错误 - 查看Run Console是否隐藏,或检查是否有报错信息
2. 基于解析数据生成新XML文件
使用xml.etree.ElementTree生成XML,比SAX更简洁直观:
import xml.etree.ElementTree as ET from xml.dom import minidom def generate_xml(pillcall_list, output_path): # 创建根节点(根据你的XML结构调整,这里假设根为<PillCalls>) root = ET.Element("PillCalls") for data in pillcall_list: # 创建PillCall元素并添加属性 pillcall_elem = ET.SubElement(root, "PillCall") for key, value in data.items(): pillcall_elem.set(key, value) # 格式化XML,输出更美观 rough_string = ET.tostring(root, 'utf-8') reparsed = minidom.parseString(rough_string) pretty_xml = reparsed.toprettyxml(indent=" ") # 写入文件 with open(output_path, "w", encoding="utf-8") as f: f.write(pretty_xml) # 使用解析得到的数据生成新XML generate_xml(handler.pillcall_data, "new_pillcalls.xml")
3. 大型文件与并发处理建议
- SAX是流式解析,天生适合大型XML,无需加载整个文件到内存
- 并发处理用
concurrent.futures.ThreadPoolExecutor(XML解析属于IO密集型任务),数据库操作需用连接池避免频繁创建连接 - 数据库存储建议用ORM(如SQLAlchemy)简化代码,确保线程安全
内容的提问来源于stack exchange,提问作者WeDoneLost
相关产品推荐
相关产品推荐

