Подтвердить что ты не робот

Проблемы с кодировкой 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&amp;month=08&amp;day=27&amp;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', но до сих пор нет кубиков.

Любые указатели будут высоко оценены.

4b9b3361

Ответ 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)