Вопрос по unicode, python, character-encoding, beautifulsoup, content-type – Декодирование HTML-объектов с помощью Python

18

Я пытаюсь декодировать HTML-записи отсюдаNYTimes.com и я не могу понять, что я делаю неправильно.

Взять, к примеру:

"U.S. Adviser’s Blunt Memo on Iraq: Time ‘to Go Home’"

Я попробовал BeautifulSoup, decode ('iso-8859-1') и django.utils.encoding's smart_str без какого-либо успеха.

@Triptych: естьunescape(). jfs
Эти вопросы, кажется, часто возникают без хорошего решения. Заставляет меня хотеть написать что-то свое ... Triptych
Я думаю, что это лучшее решение, которое я нашел до сих пор. Я мог бы попытаться сделать это сам. Если я это сделаю, я опубликую свое решение. KeyboardInterrupt

Ваш Ответ

3   ответа
18

Это работает:

from BeautifulSoup import BeautifulStoneSoup
s = "U.S. Adviser’s Blunt Memo on Iraq: Time ‘to Go Home’"
decoded = BeautifulStoneSoup(s, convertEntities=BeautifulStoneSoup.HTML_ENTITIES)

Если вы хотите получить строку вместо объекта Unicode, вам нужно будет декодировать ее в кодировку, которая поддерживает используемые символы; ISO-8859-1 не включает:

result = decoded.encode("UTF-8")

К сожалению, вам нужен внешний модуль для чего-то подобного; простое декодирование сущностей HTML / XML должно находиться в стандартной библиотеке и не требовать от меня использования библиотеки с бессмысленными именами классов, такими как «BeautifulStoneSoup». (Имена классов и функций не должны быть «креативными», они должны быть осмысленными.)

& APOS; имена не должны быть "креативными" & APOS; это каменное правило холода или просто личный выбор?
Также работает с BeautifulSoup вместо BeautifulStoneSoup - на шаг меньше & quot; креативно & quot; :)
Поддержка декодирования сущностей находится в стандартной библиотеке (модуль htmlentitydefs). У OP есть (десятичные) цифровые ссылки на символы, а не сущности.
lxml, увы, также не входящий в стандартную библиотеку, также предоставляет синтаксический анализатор Beautiful Soup (и многое другое) с несколько меньшим «креативом». имена.
@TankorSmash: Нет никаких полномочий - кроме компилятора - заставлять вас вообще следовать любым стандартам кодирования, но мне это кажется здравым смыслом.
20

На самом деле то, что у вас есть, не является сущностями HTML. Есть ТРИ разновидности этих & amp; .....; штуковины - например      все означает U + 00A0 без разрывов пространства.

  (тип, который у вас есть) является "числовой символьной ссылкой"; (десятичный).
  является «числовой ссылкой на символ»; (Шестнадцатеричное).
  это сущность.

Дальнейшее чтение:http://htmlhelp.com/reference/html40/entities/

Здесь вы найдете код для Python2.x, который выполняет все три в одном сканировании:http://effbot.org/zone/re-sub.htm#unescape-html

+1 за ссылку на effbot.org, очень ценно!
6

Try this:

import re

def _callback(matches):
    id = matches.group(1)
    try:
        return unichr(int(id))
    except:
        return id

def decode_unicode_references(data):
    return re.sub("&#(\d+)(;|(?=\s))", _callback, data)

data = "U.S. Adviser’s Blunt Memo on Iraq: Time ‘to Go Home’"
print decode_unicode_references(data)
UnicodeEncodeError: «charmap»; кодек не может кодировать символ u & gt; u2019 & gt; в позиции 12: символы отображаются на & lt; undefined & gt; Кажется, это ошибка, которую я продолжаю получать независимо от того, что я пытаюсь сделать. KeyboardInterrupt
Не могли бы вы предоставить больше кода? Я только что попробовал это с функцией, которую я написал, и персонаж 2019 работает отлично. Он отображается как: & # x7E3;
Несколько вопросов по вашему регулярному выражению: (1) Разве это не \ d вместо \ w? Регулярное выражение будет соответствовать  а также  но затем он завершится с ошибкой в int () (2), позволяя символьной ссылке (она НЕ является сущностью) оканчиваться пробелом вместо ";" кажется очень терпимым - разве вы не упомянули об этом? (3) Разве последняя часть не будет лучше написана как [; \ s]?
Джон, ты был прав в первом пунктеpartially, Он не будет соответствовать & nbsp; поскольку это не начинается с&#, но да, это должно было быть\d, Что касается пункта два, позволяющего ему заканчиваться пробелами, следует отметить, что, хотя он и не симпатичный, он все же поддерживается. Я обновил код следующим образом: (1) Изменил его на\d, (2) сделал обратный вызов немного сильнее, и (3) использовал предварительное утверждение для завершения пробела вместо того, чтобы поглощать его, как это было.
Эван, спасибо за просвещение, особенно за переносимость пробелов, о которой я не знал. Я получил еще несколько подсказок, посмотрев спецификации HTML 4.01 и 2.0. Они ссылались на стандарт SGML (ISO 8879). Стоимость = 238 швейцарских франков (!), Поэтому я ее не читал, но HTML 2.0 прокомментировал, что ";" требуется только в том случае, если символ, следующий за ссылкой, будет частью имени. Эксперименты с FF, IE и Opera с использованием пробела - / X A и& вместо ; все дали один и тот же результат: они прекращают обращение и не проглатываются. Я с нетерпением жду вашего обновленного решения ;-)

Похожие вопросы