用Python解析XML解决外部实体参考



在我的s1000d XML中,它指定了一个doctype,其中包含包含许多其他包含所有有效字符实体的其他文件的公共URL引用。我已经使用xml.etree.elementtree和lxml尝试解析它并获得解析错误,两者都指示:

undefined entity −: line 82, column 652

即使−是根据所指定的实体参考的有效实体。

XML顶部如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dmodule [
<!ENTITY % ISOEntities PUBLIC 'ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML' 'http://www.s1000d.org/S1000D_4-1/ent/ISOEntities'>
%ISOEntities;]>

如果您出去获取http://www.s1000d.org/s1000d_4-1/ent/isoentities,它将包含20个其他ENT文件,其中一个iso-tech.ent包含该行:

<!ENTITY minus "&#x2212;"> <!-- MINUS SIGN -->

在第652列附近的XML文件的第82行中,如下:....请参阅70 &minus; 41 ....

我如何运行一个python脚本以在没有未定义的实体的情况下解析此文件?

对不起,我不想指定parser.entity['minus'] = chr(2212)。我这样做是为了快速修复,但是有许多角色实体参考。我希望解析器检查XML中指定的实体参考。

我很惊讶,但是我绕着阳光和后退,没有找到该怎么做(或者我有但不能遵循它)。如果我更新XML文件并添加 <!ENTITY minus "&#x2212;">它不会失败,所以不是XML。

它在分析上失败。这是我用于ElementTree的代码

 fl = os.path.join(pth, fn)
 try:
     root = ET.parse(fl)
 except ParseError as p:
     print("ParseError : ", p)

这是我用于LXML的代码

fl = os.path.join(pth, fn)
try:
    parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
    root = etree.parse(fl, parser=parser)
except etree.XMLSyntaxError as pe:
    print("lxml XMLSyntaxError: ", pe)

我希望解析器加载实体参考,以便知道&sionus;所有文件中指定的所有其他字符实体都是有效的实体字符。

非常感谢您的建议和帮助。

我要为lxml回答。如果您可以使用LXML,则没有理由考虑ElementTree。

我认为您缺少的作品是XMLPARSER中的no_network=False;默认情况下是正确的。

示例...

XML输入(test.xml)

<!DOCTYPE doc [
<!ENTITY % ISOEntities PUBLIC 'ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML' 'http://www.s1000d.org/S1000D_4-1/ent/ISOEntities'>
%ISOEntities;]>
<doc>
    <test>Here's a test of minus: &minus;</test>
</doc>

python

from lxml import etree
parser = etree.XMLParser(load_dtd=True,
                         no_network=False)
tree = etree.parse("test.xml", parser=parser)
etree.dump(tree.getroot())

输出

<doc>
    <test>Here's a test of minus: −</test>
</doc>

如果您希望保留实体参考,请将resolve_entities=False添加到XMLPARSER。


另外,不要去外部位置解决参数实体,而是考虑设置XML目录。这将使您可以解决公共和/或系统标识符到本地版本。

使用上面的相同XML输入的示例...

XML目录(目录中的" catalog.xml"目录测试中的"目录"(目录名称中用于测试的空间))

<!DOCTYPE catalog PUBLIC "-//OASIS//DTD XML Catalogs V1.1//EN" "http://www.oasis-open.org/committees/entity/release/1.1/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
    <!-- The path in @uri is relative to this file (catalog.xml). -->
    <uri name="http://www.s1000d.org/S1000D_4-1/ent/ISOEntities" uri="./ents/ISOEntities_stackoverflow.ent"/>
</catalog>

实体文件(" isoentities_stackoverflow.ent"目录中的"目录测试/ents"。将值更改为" BAM!"以进行测试)

>
<!ENTITY minus "BAM!">

Python (将no_network更改为True,以证明正在使用http://www.s1000d.org/S1000D_4-1/ent/ISOEntities的本地版本。)

import os
from urllib.request import pathname2url
from lxml import etree
# The XML_CATALOG_FILES environment variable is used by libxml2 (which is used by lxml).
# See http://xmlsoft.org/catalog.html.
try:
    xcf_env = os.environ['XML_CATALOG_FILES']
except KeyError:
    # Path to catalog must be a url.
    catalog_path = f"file:{pathname2url(os.path.join(os.getcwd(), 'catalog test/catalog.xml'))}"
    # Temporarily set the environment variable.
    os.environ['XML_CATALOG_FILES'] = catalog_path
parser = etree.XMLParser(load_dtd=True,
                         no_network=True)
tree = etree.parse("test.xml", parser=parser)
etree.dump(tree.getroot())

输出

<doc>
    <test>Here's a test of minus: BAM!</test>
</doc>

最新更新