Использование python для редактирования html, но lxml преобразует хорошие HTML-сущности в странную кодировку - PullRequest
10 голосов
/ 02 февраля 2011

Я пытаюсь использовать python (с pyquery и lxml) для изменения и очистки некоторых html.

Eg. html = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>"

Функция lxml.html.clean, clean_html (), работает хорошо, за исключением того, что она заменяет красивые html-сущности, такие как

&#146; 

с некоторой строкой Unicode

\xc2\x92

Юникод выглядит странно в разных браузерах (Firefox и Opera, использующие автоматическое кодирование, utf8, latin-1 и т. Д.), Как пустое поле. Как я могу прекратить lxml конвертацию сущностей? Как я могу получить все это в кодировке latin-1? Кажется странным, что модуль, созданный специально для HTML, сделает это.

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

replace("\xc2\x92","&#146;").

Я пытался использовать

clean_html(html).encode('latin-1')

но Юникод сохраняется.

И да, я бы сказал людям, чтобы они перестали использовать слово для написания html, но тогда я бы услышал все

"Из-за этого мне нравится, ты не можешь сделать меня чан хитлр".

Редактировать: решение Beautifulsoup:

from BeautifulSoup import BeautifulSoup, Comment
soup = BeautifulSoup(str(desc[desc_type]))
                    comments = soup.findAll(text=lambda text:isinstance(text, Comment))
                    [comment.extract() for comment in comments]
                    print soup

Ответы [ 3 ]

11 голосов
/ 04 февраля 2011

Есть несколько вещей, которые - если вы их знаете - приведут к самому простому / лучшему решению:

  • clean_html() возвращает тот же тип, который вы ему предоставили: если выдать ему строку, он вернет строку, но если вы дадите ему Element или ElementTree, он вернет Element или ElementTree соответственно

  • вы можете контролировать способ Element или ElementTreeсериализуется, предоставляя опции кодирования для метода lxml.html.tostring() или метода write() дерева (кстати, то же самое относится и к xml).Вы можете сделать это, например, с помощью encoding='utf-8'.

  • любой контент, который МОЖЕТ быть закодирован в этой кодировке, будет выводиться как закодированная строка, любой контент, который не может быть "экранирован"как сущности.Использование encoding="ascii" приведет к тому, что любые символы, не относящиеся к ascii, станут «красивыми» сущностями, как вы хотите.

Соберите, это означает: сначала проанализируйте строку в элемент (или дерево, если выхотите), очистите его и при необходимости сериализуйте:

html = lxml.html.fromstring("<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>")
html = clean_html(html)
result = lxml.html.tostring(html, encoding="ascii")

(и немного более хитрый трюк заключается в использовании параметра ошибок в методе encode() строки Unicode: попробуйте кодировать строку Unicode, содержащую«специальные» символы с s.encode('ascii', 'xmlcharrefreplace') и посмотрите, что это делает ...)

2 голосов
/ 02 февраля 2011

Я предполагаю, что &#146; должен быть кавычкой.Объект str со значением байта 146, chr(146), декодированный с помощью cp1252, представляет собой кавычку:

In [46]: print(chr(146).decode('cp1252'))
’

Итак, вы можете сделать это:

import lxml.html.clean as clean
import re

html = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>"

html=re.sub('&#(\d+);',lambda m: chr(int(m.group(1))).decode('cp1252'),html)
print(html)
# <div><!-- word style><bleep><omgz 1,000 tags><--><p>It’s a spicy meatball!</div>
print(type(html))
# <type 'unicode'>
print(clean.clean_html(html))
# <div><p>It’s a spicy meatball!</p></div>

Или,

doc=lh.fromstring(html)
clean.clean(doc)

Обратите внимание, что кавычка имеет значение кодовой точки Unicode 8217. То есть ord(chr(146).decode('cp1252')) равно 8217, поэтому lh.tostring возвращает:

print(lh.tostring(doc))
# <div><p>It&#8217;s a spicy meatball!</p></div>   

Вы можете перекодировать егов cp1252 вот так:

print(repr(lh.tostring(doc,encoding='cp1252')))
# '<div><p>It\x92s a spicy meatball!</p></div>'

Я не знаю, как уговорить lxml, чтобы вернуть

'<div><p>It&#146;s a spicy meatball!</p></div>'

, чтобы соответствовать выводу вашего кода BeautifulSoup, однако.Что ж, ясно, что это можно сделать с помощью регулярных выражений (полностью изменив то, что я делал выше), но я не знаю, является ли это необходимым или целесообразным, поскольку lxml уже должен возвращать html, который могут понять другие приложения.

result=re.sub('&#(\d+);',lambda m: '&#{n};'.format(
    n=ord(unichr(int(m.group(1))).encode('cp1252'))),
            lh.tostring(doc))
print(result)
# <div><p>It&#146;s a spicy meatball!</p></div>
1 голос
/ 02 июня 2011

Вы также можете просто преобразовать строку utf8 в ascii с символами xml

result = result.decode('utf-8').encode('ascii', 'xmlcharrefreplace')
...