python - 使用 Python 解析 XML 解析外部实体引用

标签 python xml

在我的 S1000D xml 中,它指定了一个引用公共(public) URL 的 DOCTYPE,该 URL 包含对包含所有有效字符实体的许多其他文件的引用。我已经使用 xml.etree.ElementTree 和 lxml 来尝试解析它并得到一个解析错误,两者都指示:

undefined entity −: line 82, column 652

尽管−根据指定的 ENTITY Reference 是一个有效的实体。

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)

我希望解析器加载 ENTITY 引用,以便它知道 - 以及所有文件中指定的所有其他字符实体都是有效的实体字符。

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

最佳答案

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

我认为您缺少的部分是 XMLParser 中的 no_network=False;这是True by default .

例子...

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 Catalog .这将使您能够将公共(public)和/或系统标识符解析为本地版本。

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

XML 目录(目录“catalog test”中的“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>

实体文件(目录“catalog test/ents”中的“ISOEntities_stackoverflow.ent”。将值更改为“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>

关于python - 使用 Python 解析 XML 解析外部实体引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55307786/

相关文章:

python - 如何在 Django 中渲染缓存的 JSONP View

java - UnmarshallerHandler 相对于 Unmarshaller 有什么优势

java - 某些 mipmap 不显示

python - 名称 'get_config' 未定义

javascript - UI5 将新数据从 Controller 绑定(bind)到模型

Python-存在多个同名元素属性时如何编辑特定的XML元素内容?

swift - Bundle.main.path(forResource... 在查找 xml 文件时总是返回 nil

Python Pandas self join 用于合并笛卡尔积以产生所有组合和总和

Python 正则表达式 : find "com" or "org"

python - 按分组然后应用函数然后在 Pandas Python 中展平回数据框