当XML属性包含Unicode时,xml.dom.minidom.parse()失败
我正在使用urllib2.request查询一个网络服务,并且收到了XML格式的数据。如果我违反了这个网络服务的调用频率限制(每秒只能调用一次),我会收到一段HTML内容,告诉我违反了频率限制。
虽然我可以在每次调用后使用time.sleep()暂停2到3秒,但不知什么原因,我还是会违反频率限制。
为了测试我的响应是XML还是HTML,我使用了xml.dom.minidom(),然后检查是否有HTML元素。
try:
dom = xml.dom.minidom.parseString(response_text)
except xml.parsers.expat.ExpatError:
return False
if len(dom.getElementsByTagName('html')) == 0:
return True
else:
return False
这样做虽然能解决问题,但我遇到了一种情况,其中一个XML属性包含了XML内容。在这种情况下,parseString()命令会失败,出现了
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/python/default-2.6/lib/python2.6/xml/dom/minidom.py", line 1918, in parse
return expatbuilder.parse(file)
File "/opt/python/default-2.6/lib/python2.6/xml/dom/expatbuilder.py", line 924, in parse
result = builder.parseFile(fp)
File "/opt/python/default-2.6/lib/python2.6/xml/dom/expatbuilder.py", line 207, in parseFile
parser.Parse(buffer, 0)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 3125
在这个情况下,第3125列是某个属性值文本的一部分,里面包含了ampersand-pound-x-9(Stackoverflow隐藏了我的unicode)。
xml.dom.minidom应该能处理这个吗?除了这个问题,XML中还有其他可能导致解析失败的问题吗?
另外,如果社区有其他处理这种情况的方法,我也很乐意尝试。
如果有帮助的话,这里是当我违反他们的频率限制时,网络服务返回的内容:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="eng">
<head>
<title>Service Temporarily Unavailable - Rate Limited</title>
</head>
<body style="text-align:center;background-color:white;">
<h1>Service Temporarily Unavailable</h1>
<hr />
<div>
You have used this service too often in a short time. Please wait before using this service again.
<br/><br/>
Please visit the <a href="http://wiki.xxxx.com/index.php?title=API_Usage">wiki</a> for more details.
</div>
</body>
</html>
2 个回答
我觉得 	
是一个制表符。你可以试试这个链接 http://docs.python.org/library/htmllib.html#module-htmlentitydefs,它可以把特殊的 HTML 实体转换回它们原来的样子。(不过这可能会有 <
之类的问题)。或者你也可以做一个字符串替换,把 	
替换成一个空格。
我有个建议,当你在解析一些东西时,如果解析器遇到问题,比如不符合你的模式,不要让它停下来,而是让它继续运行,同时给出一个警告。这样你就能看到问题出在哪里,可能还可以修正它,或者至少知道有问题存在。
至于你提到的速率限制问题,为什么不先缓存请求的 HTML,这样你就可以在本地处理了。
你可以在尝试解析结果之前,先检查一下这个字符串是否是HTML格式:
if response_text.lstrip().startswith('<!DOCTYPE html'):
# we received an html response, sleep again
...
我也发现minidom在处理包含制表符实体的属性时没有出错。可能是因为有些实体序列没有正确结束,比如	
后面没有分号?不过minidom对于属性里面正确转义的实体是没问题的:
text = '<root><a href="	foo<">link</a></root>'
tree = minidom.parseString(text)
print tree.toxml()
u'<?xml version="1.0" ?>\n<root><a href="\tfoo<">link</a></root>'