使用 lxml 解析大型 XML 文件
Parsing large XML file with lxml
我正在尝试使用 lxml 解析 dblp.xml 文件 (3.2gb)。
以下是我的代码。
from lxml import etree
from io import StringIO, BytesIO
tree = etree.parse("dblp.xml")
但是我收到一条错误消息:
OSError Traceback (most recent call last)
<ipython-input-5-6a342013a160> in <module>
1 from lxml import etree
2 from io import StringIO, BytesIO
----> 3 tree = etree.parse("dblp.xml")
src/lxml/etree.pyx in lxml.etree.parse()
src/lxml/parser.pxi in lxml.etree._parseDocument()
src/lxml/parser.pxi in lxml.etree._parseDocumentFromURL()
src/lxml/parser.pxi in lxml.etree._parseDocFromFile()
src/lxml/parser.pxi in lxml.etree._BaseParser._parseDocFromFile()
src/lxml/parser.pxi in lxml.etree._ParserContext._handleParseResultDoc()
src/lxml/parser.pxi in lxml.etree._handleParseResult()
src/lxml/parser.pxi in lxml.etree._raiseParseError()
OSError: Error reading file 'dblp.xml': failed to load external entity "dblp.xml"
dblp.xml 和 dblp.dtd 都已在根文件夹中。
请帮忙!
您可以使用 etree.iterparse
来避免将整个文件加载到内存中:
events = ("start", "end")
with open("dblp.xml", "r") as fo:
context = etree.iterparse(fo, events=events)
for action, elem in context:
# Do something
这将允许您只提取您需要的实体而忽略其他实体。
正如 Jan Jaap Meijerink 所说,您可以尝试使用 iterparse。可能您还可以禁用 lxml 安全功能以防止解析大文件(请参阅 https://lxml.de/api/lxml.etree.XMLParser-class.html 处的文档):
with open('', 'r') as fobj:
for event, elem in etree.iterparse(
fobj,
huge_tree=True,
):
#do something with element or event
最后,如果您更喜欢尝试使用解析,您可以定义启用 huge_tree 的 xml 解析器,并将其设置为默认值以进一步使用 etree.parse:
xml_parser_settings = dict(
huge_tree=True, # resolve_entities=False, remove_pis=True, no_network=True
)
XMLPARSER = etree.XMLParser(xml_parser_settings)
etree.set_default_parser(XMLPARSER)
在这些语句之后,您可以将 etree.parser 与已配置的 XMLPARSER 一起使用。不过要注意多线程 (https://lxml.de/1.3/api/lxml.etree-module.html#set_default_parser)。
添加 resolve_entities、remove_pis 和 no_network 关键字可以(至少一点点)降低解析巨大外部文件的风险,如果它们来自不受信任的来源。
我正在尝试使用 lxml 解析 dblp.xml 文件 (3.2gb)。 以下是我的代码。
from lxml import etree
from io import StringIO, BytesIO
tree = etree.parse("dblp.xml")
但是我收到一条错误消息:
OSError Traceback (most recent call last)
<ipython-input-5-6a342013a160> in <module>
1 from lxml import etree
2 from io import StringIO, BytesIO
----> 3 tree = etree.parse("dblp.xml")
src/lxml/etree.pyx in lxml.etree.parse()
src/lxml/parser.pxi in lxml.etree._parseDocument()
src/lxml/parser.pxi in lxml.etree._parseDocumentFromURL()
src/lxml/parser.pxi in lxml.etree._parseDocFromFile()
src/lxml/parser.pxi in lxml.etree._BaseParser._parseDocFromFile()
src/lxml/parser.pxi in lxml.etree._ParserContext._handleParseResultDoc()
src/lxml/parser.pxi in lxml.etree._handleParseResult()
src/lxml/parser.pxi in lxml.etree._raiseParseError()
OSError: Error reading file 'dblp.xml': failed to load external entity "dblp.xml"
dblp.xml 和 dblp.dtd 都已在根文件夹中。
请帮忙!
您可以使用 etree.iterparse
来避免将整个文件加载到内存中:
events = ("start", "end")
with open("dblp.xml", "r") as fo:
context = etree.iterparse(fo, events=events)
for action, elem in context:
# Do something
这将允许您只提取您需要的实体而忽略其他实体。
正如 Jan Jaap Meijerink 所说,您可以尝试使用 iterparse。可能您还可以禁用 lxml 安全功能以防止解析大文件(请参阅 https://lxml.de/api/lxml.etree.XMLParser-class.html 处的文档):
with open('', 'r') as fobj:
for event, elem in etree.iterparse(
fobj,
huge_tree=True,
):
#do something with element or event
最后,如果您更喜欢尝试使用解析,您可以定义启用 huge_tree 的 xml 解析器,并将其设置为默认值以进一步使用 etree.parse:
xml_parser_settings = dict(
huge_tree=True, # resolve_entities=False, remove_pis=True, no_network=True
)
XMLPARSER = etree.XMLParser(xml_parser_settings)
etree.set_default_parser(XMLPARSER)
在这些语句之后,您可以将 etree.parser 与已配置的 XMLPARSER 一起使用。不过要注意多线程 (https://lxml.de/1.3/api/lxml.etree-module.html#set_default_parser)。
添加 resolve_entities、remove_pis 和 no_network 关键字可以(至少一点点)降低解析巨大外部文件的风险,如果它们来自不受信任的来源。