使用Python和lxml验证XML与外部DTD

4 投票
2 回答
4483 浏览
提问于 2025-04-17 22:13

我正在尝试验证一个XML文件,这个文件中引用了一个外部的DTD,具体是在doctype标签里提到的。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">
...the rest of the document...

我使用的是Python 3.3和lxml模块。根据我在http://lxml.de/validation.html#validation-at-parse-time上看到的信息,我把这个代码写出来了:

enexFile = open(sys.argv[2], mode="rb") # sys.argv[2] is the path to an XML file in local storage.
enexParser = etree.XMLParser(dtd_validation=True)
enexTree = etree.parse(enexFile, enexParser)

根据我对validation.html的理解,lxml库应该可以自动获取DTD并进行验证。但是,结果却是这样的:

$ ./mapwrangler.py validate notes.enex
Traceback (most recent call last):
  File "./mapwrangler.py", line 27, in <module>
    enexTree = etree.parse(enexFile, enexParser)
  File "lxml.etree.pyx", line 3239, in lxml.etree.parse (src/lxml/lxml.etree.c:69955)
  File "parser.pxi", line 1769, in lxml.etree._parseDocument (src/lxml/lxml.etree.c:102257)
  File "parser.pxi", line 1789, in lxml.etree._parseFilelikeDocument (src/lxml/lxml.etree.c:102516)
  File "parser.pxi", line 1684, in lxml.etree._parseDocFromFilelike (src/lxml/lxml.etree.c:101442)
  File "parser.pxi", line 1134, in lxml.etree._BaseParser._parseDocFromFilelike (src/lxml/lxml.etree.c:97069)
  File "parser.pxi", line 582, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:91275)
  File "parser.pxi", line 683, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:92461)
  File "parser.pxi", line 622, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:91757)
lxml.etree.XMLSyntaxError: Validation failed: no DTD found !, line 3, column 43

这让我感到惊讶,因为如果我关闭验证,文档就能正常解析,我可以用print(enexTree.docinfo.doctype)来获取

$ ./mapwrangler.py validate notes.enex
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">

所以在我看来,找DTD应该没有问题。

谢谢你的帮助。

2 个回答

0

我至今不知道为什么,我的问题和我本地文件系统上XML目录的位置有关。

在我的情况下,我使用的XML编辑器和一个内容管理系统(CCMS,这里是SDL Trisoft 2011 R2)紧密集成。当编辑器连接到CCMS时,DTD文件、目录文件和其他一些文件会被同步。这些文件最终会放在本地文件系统的:

C:\Users\[username]\AppData\Local\Trisoft\InfoShare Client\[id]\Config\DocTypes\catalog.xml

我一直无法让它正常工作。简单地把整个目录复制到另一个位置就解决了问题,这样就可以用了:

f = r"path/to/my/file.xml"
# set XML catatog file path
os.environ['XML_CATALOG_FILES'] = r'C:\DATA\Mydoctypes\catalog.xml'
# configure parser
parser = etree.XMLParser(dtd_validation=True, no_network=True)
# validate
try:
   valid = etree.parse(f, parser=parser)
    print("This file is valid against the DTD.")
except etree.XMLSyntaxError, error:
   print("This file is INVALID against the DTD!")
   print(error)

显然,这并不是最理想的解决办法,但确实有效。

这可能和文件权限有关,或者是Windows里那个老掉牙的“文件路径太长”的问题?我还没有尝试过使用符号链接是否能解决这个问题。

我使用的是Windows 7,Python 2.7.11,lxml的版本是(3.6.0)。

3

在创建解析器对象的时候,你需要加上 no_network=False 这个选项。默认情况下,这个选项是设置为 True 的。

根据解析器选项的文档说明,链接在这里:http://lxml.de/parsing.html#parsers

no_network - 在查找外部文档时,防止网络访问(默认是开启的)

撰写回答