Проблемы с кодировкой Python и BeautifulSoup
Я пишу сканер с Python с помощью BeautifulSoup, и все идет плавно, пока я не наткнулся на этот сайт:
http://www.elnorte.ec/
Я получаю содержимое с библиотекой запросов:
r = requests.get('http://www.elnorte.ec/')
content = r.content
Если я делаю печать переменной содержимого в этой точке, все испанские специальные символы, похоже, работают нормально. Однако, как только я попытаюсь передать переменную content в BeautifulSoup, все это перепуталось:
soup = BeautifulSoup(content)
print(soup)
...
<a class="blogCalendarToday" href="/component/blog_calendar/?year=2011&month=08&day=27&modid=203" title="1009 artÃculos en este dÃa">
...
Он, по-видимому, искажает все испанские специальные персонажи (акценты и еще много чего). Я попытался сделать content.decode('utf-8'), content.decode('latin-1'), также попытался перепутаться с параметром fromEncoding в BeautifulSoup, установив его с помощью Encoding = 'utf-8' и fromEncoding = 'latin-1', но до сих пор нет кубиков.
Любые указатели будут высоко оценены.
Ответы
Ответ 1
Вы могли бы попробовать:
r = urllib.urlopen('http://www.elnorte.ec/')
x = BeautifulSoup.BeautifulSoup(r.read)
r.close()
print x.prettify('latin-1')
Я получаю правильный вывод.
О, в этом специальном случае вы могли бы также x.__str__(encoding='latin1')
.
Я предполагаю, что это связано с тем, что контент находится в ISO-8859-1 (5), а мета-атрибут контента http-equiv неправильно говорит "UTF-8".
Не могли бы вы подтвердить?
Ответ 2
В вашем случае на этой странице неверные данные utf-8, которые смешивают BeautifulSoup и заставляют думать, что ваша страница использует windows-1252, вы можете сделать этот трюк:
soup = BeautifulSoup.BeautifulSoup(content.decode('utf-8','ignore'))
сделав это, вы отбросьте любые неправильные символы из источника страницы, а BeautifulSoup будет иметь право на кодировку гостя.
Вы можете заменить 'ignore' на 'replace' и проверить текст для '?' символы, чтобы увидеть, что было отброшено.
На самом деле очень сложно написать сканер, который может угадывать кодировку страниц каждый раз со 100% -ным шансом (в настоящее время браузеры очень хороши), вы можете использовать модули типа "chardet", но, например, в вашем случае это будет считать кодировку как ISO-8859-2, что тоже неверно.
Если вам действительно нужно иметь возможность получить кодировку для любой страницы, которую пользователь может предоставить, вы должны либо построить многоуровневую (попробовать utf-8, попробовать latin1, try и т.д.) функцию обнаружения (как мы это сделали в нашем проекте) или использовать код обнаружения из firefox или chromium в качестве модуля C.
Ответ 3
Первый ответ прав, эти функции несколько раз эффективны.
def __if_number_get_string(number):
converted_str = number
if isinstance(number, int) or \
isinstance(number, float):
converted_str = str(number)
return converted_str
def get_unicode(strOrUnicode, encoding='utf-8'):
strOrUnicode = __if_number_get_string(strOrUnicode)
if isinstance(strOrUnicode, unicode):
return strOrUnicode
return unicode(strOrUnicode, encoding, errors='ignore')
def get_string(strOrUnicode, encoding='utf-8'):
strOrUnicode = __if_number_get_string(strOrUnicode)
if isinstance(strOrUnicode, unicode):
return strOrUnicode.encode(encoding)
return strOrUnicode
Ответ 4
Я бы предложил использовать более методичный подход к доказательству дурака.
# 1. get the raw data
raw = urllib.urlopen('http://www.elnorte.ec/').read()
# 2. detect the encoding and convert to unicode
content = toUnicode(raw) # see my caricature for toUnicode below
# 3. pass unicode to beautiful soup.
soup = BeautifulSoup(content)
def toUnicode(s):
if type(s) is unicode:
return s
elif type(s) is str:
d = chardet.detect(s)
(cs, conf) = (d['encoding'], d['confidence'])
if conf > 0.80:
try:
return s.decode( cs, errors = 'replace' )
except Exception as ex:
pass
# force and return only ascii subset
return unicode(''.join( [ i if ord(i) < 128 else ' ' for i in s ]))
Вы можете рассуждать независимо от того, что вы бросаете на это, он всегда будет отправлять действительный unicode в bs.
В результате ваше дерево синтаксического анализа будет вести себя намного лучше и не подведет новые более интересные способы каждый раз, когда у вас появятся новые данные.
Trial и Error не работают в Code. Слишком много комбинаций: -)
Ответ 5
Вы можете попробовать это, которое работает для каждой кодировки
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
headers = {"User-Agent": USERAGENT}
resp = requests.get(url, headers=headers)
http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None
html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True)
encoding = html_encoding or http_encoding
soup = BeautifulSoup(resp.content, 'lxml', from_encoding=encoding)