Etree.tostring LXML, экранирующий URL в атрибутах ссылки href

При использовании LXML для анализа html-документа, а затем при помощи etree.tostring() я замечаю, что амперсанды в ссылках преобразуются в html-экранированные сущности.

Это разрыв связи по понятным причинам. Вот простой автономный пример проблемы:

>>> from lxml import etree
>>> parser = etree.HTMLParser()
>>> tree = etree.fromstring("""<a href="https://www.example.com/?param1=value1&param2=value2">link</a>""", parser)
>>> etree.tostring(tree)
'<html><body><a href="https://www.example.com/?param1=value1&amp;param2=value2">link</a></body></html>'

Я хотел бы, чтобы результат был:

<html><body><a href="https://www.example.com/?param1=value1&param2=value2">link</a></body></html>

1 ответ

Хотя & кодирование должно быть стандартным способом. Если вам действительно необходимо избежать конвертации по каким-либо причинам, то вы можете сделать:

Шаг 1. Найдите уникальную строку, которой не должно быть в вашем HTML-источнике. Вы можете просто использовать ANDamp; как ваша переменная reserved_amp, если вы уверены "ANDamp;" Строка не будет отображаться в вашем источнике HTML. В противном случае вы можете сгенерировать случайный алфавит и убедиться, что эта строка не существует в вашем HTML-источнике:

>>> import random
>>> import string
>>> length = 15 #increase the length if it's still seems to be collide
>>> reserved_amp = "&amp;"
>>> html = """<a href="https://www.example.com/?param1=value1&param2=value2">link</a>"""
>>> while reserved_amp in [html, "&amp;"]: 
...     reserved_amp = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(length)) + "amp;" #amp; is for you easy to spot on
... 
>>> print reserved_amp
2eya6oywxg5z7q5amp;

Шаг 2. заменить все вхождения & перед анализом:

>>> html = html.replace("&", reserved_amp)
>>> html
'<a href="https://www.example.com/?param1=value12eya6oywxg5z7q5amp;param2=value2">link</a>'
>>> 

Шаг 3. Замените его обратно, только если вам нужна оригинальная форма:

>>> from lxml import etree
>>> parser = etree.HTMLParser()
>>> tree = etree.fromstring(html, parser)
>>> etree.tostring(tree).replace(reserved_amp, "&")
'<html><body><a href="https://www.example.com/?param1=value1&param2=value2">link</a></body></html>'
>>> 

[ОБНОВИТЬ]:

Двоеточие поставить в конце reserved_amp это безопасный охранник.

Что если мы сгенерировали reserved_amp как это?

ampXampXampXampX + amp;

И HTML содержит:

yyYampX&

Это будет закодировано в этой форме:

yyYampXampXampXampXampXamp;

Тем не менее, невозможно вернуть / декодировать неправильный результат yy&YampX (оригинал yyYampX&) из-за того, что последний символ защищен двоеточием, это не алфавит ASCII, который никогда не будет сгенерирован как reserved_amp от string.ascii_lowercase + string.digits выше.

Таким образом, убедитесь, что случайным образом не используется двоеточие (или другой не-ASCII символ), а затем добавьте его в конце (ДОЛЖЕН быть последним символом), не нужно беспокоиться о yyYampX& вернуться к yy&YampX ловушка.

Согласно документации lxml tostring(),method='xml' можно передать, чтобы избежать специфики html

etree.tostring(tree, method='xml')

В своих проектах я использую:

from lxml import html
html.tostring(node, with_tail=False, method='xml', encoding='unicode')
Другие вопросы по тегам