使用lxml验证XML与DTD时实体导入失败
我有一个工具可以生成NewsML类型的XML文件,我想在生成文件后对它们进行验证。
我遇到了一个错误:
尝试加载网络实体 http://www.w3.org/TR/ruby/xhtml-ruby-1.mod
我在Python中调用的代码是:
parser = etree.XMLParser(load_dtd=True, dtd_validation=True)
treeObject = etree.parse(f, parser)
首先,我不确定是否需要同时使用“load_dtd=True, dtd_validation=True”,但我还是用了这两个选项。其次,错误似乎是来自一个叫做nitf-3-4.dtd的文件,它的定义是:
<!ENTITY % xhtml-ruby.mod PUBLIC
"-//W3C//ELEMENTS XHTML Ruby 1.0//EN" "http://www.w3.org/TR/ruby/xhtml-ruby-1.mod">
%xhtml-ruby.mod;
lxml会去网上获取这个xhtml-ruby-1.mod文件吗,还是说我必须把所有的DTD文件都放在本地?
1 个回答
试着用 no_network=False
来构建解析器。正如文档中所说:
no_network - 在查找外部文档时防止网络访问(默认是开启的)
导入的 dtd 模块应该可以被 lxml 获取,但如果不允许网络访问,它就无法做到这一点(这不包括文档本身,只是加载外部引用的文档时会受影响。实际上,我预计你在加载 dtd 时会遇到错误,所以我猜测文档是指向本地可用的 dtd 副本,而只有 dtd 本身才引用了远程资源?)
你也可以使用目录来使用本地可用的副本(这不仅能解决这个问题,还能提高性能,对 w3c 服务器也更友好 ;-))。Libxml2(lxml 使用的库)会检查 /etc/xml/catalog
中是否存在目录,以及 XML_CATALOG_FILES
环境变量(见Libxml2 文档)
(你也可以为 lxml 编写自己的解析器来拦截和处理请求,但在这种情况下可能有些过于复杂)
请注意,除了在解析时验证,还有另一个选项:使用DTD 类单独加载 dtd,并将其用作验证器。
这样,无论文档类型声明中引用了哪个 dtd(如果有的话),都可以用提供的 dtd 来验证解析后的文档(这很方便:并不是每个有效的 xml 文件都一定符合你想要的 dtd)。
因为 dtd 只需要获取和解析一次,如果你要验证很多文档,这样会更快),而且(如果我没记错的话),你不会遇到 no_network 的问题。
这种方法的另一个好处是:你甚至可以在序列化之前验证你的元素/元素树(如果你的生成工具使用的是 lxml)。
最后一点:有些文档只能在解析时访问 dtd 才能解析(不可解析的实体...)。如果可以的话,尽量避免这种情况。(而且,虽然并不是每个人都同意:如果可能的话,尽量避免使用文档类型声明)。