XML-документ не может быть проанализирован из-за странных символов - PullRequest
0 голосов
/ 23 октября 2019

Я использую Python 3 для извлечения данных из API, но у меня проблемы с анализом некоторых документов XML из извлеченных строк.

Я определил конкретную строку, которая вызывает эту проблему:

from xml.etree import ElementTree

bad_string = '<tag>Sample &#x91;cp 99-3a&#x92</tag>'
ElementTree.fromstring(bad_string)

Это возвращаемая ошибка, которая останавливает сценарий:

ParseError: not well-formed (invalid token): line 1, column 31

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

ElementTree.fromstring('<tag>Sample &#x91;cp 99-3a&#x92</tag>'.encode('ascii', 'ignore'))

Как я могу очистить эту строку, не применяя одно конкретное регулярное выражение к другим подобным строкам?

Редактировать: Теперь, когда @b_c и @mzjn объясняют, что моя проблема - символы без экранирования, я нахожу один из возможныхрешение ( Экранирование неэкранированных символов в XML с помощью Python )

ElementTree.fromstring('<tag>&amp;Sample &#x91;cp 99-3a&#x92</tag>', parser = etree.XMLParser(recover = True))

1 Ответ

0 голосов
/ 23 октября 2019

Ваша строка содержит HTML-сущности (будь то XML или HTML) и должна быть экранирована. &#x91; и &#x92 соотносятся с и соответственно.

Если вы используете html.unescape, вы увидите очищенный текст:

>>> import html
>>> html.unescape('<tag>Sample &#x91;cp 99-3a&#x92</tag>')
'<tag>Sample ‘cp 99-3a’</tag>'

Редактировать : @mzjn указал, что вы также можете исправить строку, добавив пропущенную точку с запятой во 2-й объект:

>>> import xml.etree.ElementTree as ET
>>> tag = ET.fromstring('<tag>Sample &#x91;cp 99-3a&#x92;</tag>')
>>> tag.text
'Sample \x91cp 99-3a\x92'

Но выубедитесь, что все еще есть символы \x91 и \x92 (и требует, чтобы вы могли контролировать содержимое строки). Это кодировки MS CP1252 для одинарных кавычек слева и справа. Использование метода html.unescape, описанного выше, все равно даст вам очищенный текст.

Последующая обработка комментариев

В своем комментарии вы добавили дополнительную складку вашей строкисодержит другие допустимые escape-последовательности XML (такие как &amp;), которые html.unescape будут удачно очищены. К сожалению, как вы видели, это в конечном итоге возвращает вас на круги своя, так как у вас теперь есть &, что должно быть экранированным, но это не так (ElementTree вырвется из него дляyou).

>>> import html
>>> import xml.etree.ElementTree as ET
>>> cleaned = html.unescape('<tag>&amp;Sample &#x91;cp 99-3a&#x92</tag>')
>>> print(cleaned)
<tag>&Sample ‘cp 99-3a’</tag>
>>> ET.fromstring(cleaned)
Traceback (most recent call last):
  ...
ParseError: not well-formed (invalid token): line 1, column 12

Некоторые другие варианты, которые у вас есть, попробуйте использовать soupparser из lxml.html, что намного лучше при работе с проблемным HTML / XML:

>>> from lxml.html import soupparser
>>> soupparser.fromstring('<tag>&amp;Sample &#x91;cp 99-3 a&#x92;</tag>').text_content()
'&Sample ‘cp 99-3 a’'

Или, в зависимости от ваших потребностей, вам может быть лучше заменить строку / регулярное выражение перед анализом, чтобы удалить раздражающие символы cp1252:

>>> import re
# Matches "&#x91" or "&#x92", with or without trailing semicolon
>>> node = ET.fromstring(re.sub(r'&#x9[1-2];?', "'", '<tag>&amp;Sample &#x91;cp 99-3 a&#x92;</tag>'))
>>> node.text
"&Sample 'cp 99-3 a'"
...