сбой xml.dom.minidom.parse(), когда XML-атрибут содержит unicode

#python #xml #web-services #parsing #minidom

#python #xml #веб-сервисы #синтаксический анализ #minidom

Вопрос:

Я запрашиваю веб-службу, используя urllib2.request, и получаю XML. Если я нарушаю ограничение скорости веб-службы (1 вызов в секунду), я получаю обратно 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 является частью некоторого текстового значения атрибута, который содержит амперсанд-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>
  

Ответ №1:

Я думаю, что amp;#x9 это вкладка. Вы должны попробовать http://docs.python.org/library/htmllib.html#module-htmlentitydefs для преобразования специальных объектов html обратно в то, чем они являются. (Это может быть проблемой amp;< и т.д.). Или вы могли бы выполнить замену строки, которая amp;#x9 заменяет пробел.

Просто в качестве предложения, когда вы разбираете материал, и анализатор сталкивается с проблемой, такой как несоответствие вашему шаблону, вместо остановки операции вы должны разрешить анализатору продолжить, но выдать предупреждение. Таким образом, вы можете увидеть, в чем проблема, и потенциально исправить ее или, по крайней мере, увидеть, что проблема существует.

Также что касается вашей проблемы с ограничением скорости, почему бы не кэшировать запрошенный HTML один раз, чтобы вы могли выполнять обработку локально.

Комментарии:

1. спасибо — я посмотрю. Проблема в том, что ограничение скорости API стороннего веб-сервиса нарушено. Они документируют 1 вызов в секунду, с чем я могу смириться, но даже если я сплю более 3 секунд, я все равно время от времени получаю ошибку ограничения скорости.

Ответ №2:

Вы также можете проверить строку на наличие HTML, прежде чем пытаться проанализировать результат:

 if response_text.lstrip().startswith('<!DOCTYPE html'):
    # we received an html response, sleep again
...
  

Я также не смог заставить minidom работать с атрибутом, содержащим объект tab. Возможно, это неправильно завершенная последовательность объектов, например, amp;#9 без конечной точки с запятой? Minidom, похоже, в порядке с правильно экранированными объектами внутри атрибутов:

 text = '<root><a href="amp;#9;fooamp;<">link</a></root>'
tree = minidom.parseString(text)
print tree.toxml()

u'<?xml version="1.0" ?>n<root><a href="tfooamp;<">link</a></root>'