使用Python和lxml验证XML与外部DTD
我正在尝试验证一个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 个回答
我至今不知道为什么,我的问题和我本地文件系统上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)。
在创建解析器对象的时候,你需要加上 no_network=False
这个选项。默认情况下,这个选项是设置为 True
的。
根据解析器选项的文档说明,链接在这里:http://lxml.de/parsing.html#parsers:
no_network - 在查找外部文档时,防止网络访问(默认是开启的)