Как исправить ошибку UnicodeDecodeError: кодек ascii не может декодировать байт?

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128)

Это ошибка, которую я получаю при попытке очистить список имен, которые я извлекаю с помощью spaCy с html-страницы.

Мой код:

import urllib
import requests
from bs4 import BeautifulSoup
import spacy
from spacy.en import English
from __future__ import unicode_literals
nlp_toolkit = English()
nlp = spacy.load('en')

def get_text(url):
    r = requests.get(url)
    soup = BeautifulSoup(r.content, "lxml")

    # delete unwanted tags:
    for s in soup(['figure', 'script', 'style']):
        s.decompose()

    # use separator to separate paragraphs and subtitles!
    article_soup = [s.get_text(separator="\n", strip=True) for s in soup.find_all( 'div', {'class': 'story-body__inner'})]

    text = ''.join(article_soup)
    return text

# using spacy
def get_names(all_tags):
    names=[]
    for ent in all_tags.ents:
        if ent.label_=="PERSON":
            names.append(str(ent))
    return names

def cleaning_names(names):
    new_names = [s.strip("'s") for s in names] # remove 's' from names
    myset = list(set(new_names)) #remove duplicates
    return myset

def main():
    url = "http://www.bbc.co.uk/news/uk-politics-39784164"
    text=get_text(url)
    text=u"{}".format(text)
    all_tags = nlp(text)
    names = get_person(all_tags)
    print "names:"
    print names
    mynewlist = cleaning_names(names)
    print mynewlist

if __name__ == '__main__':
    main()

Для этого конкретного URL-адреса я получаю список имен, который включает такие символы, как £ или $:

['Ник Клегг', 'Брексит', '\xc2\xa359bn', 'Тереза ​​Мэй', 'Брексит', 'Брексит', 'Мистер Клегг', 'Мистер Клегг', 'Мистер Клегг', 'Брексит', ' Мистер Клегг», «Тереза ​​Мэй»]

А потом ошибка:

Traceback (most recent call last) <ipython-input-19-8582e806c94a> in <module>()
     47 
     48 if __name__ == '__main__':
---> 49     main()

<ipython-input-19-8582e806c94a> in main()
     43     print "names:"
     44     print names
---> 45     mynewlist = cleaning_names(names)
     46     print mynewlist
     47 

<ipython-input-19-8582e806c94a> in cleaning_names(names)
     31 
     32 def cleaning_names(names):
---> 33     new_names = [s.strip("'s") for s in names] # remove 's' from names
     34     myset = list(set(new_names)) #remove duplicates
     35     return myset

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128)

Пробовал разные способы исправления юникода (в том числе и sys.setdefaultencoding('utf8')), ничего не получалось. Я надеюсь, что у кого-то была такая же проблема раньше, и он сможет предложить решение. Благодарю вас!


person aviss    schedule 07.05.2017    source источник
comment
Очистите трассировку. Это нечитаемо.   -  person keepAlive    schedule 07.05.2017
comment
Не уверен, где возникает ошибка и не будет воспроизводиться из-за библиотек. Работает ли, если исправить список имен вручную?   -  person handle    schedule 07.05.2017
comment
Вы проверили Похожие вопросы, показанные справа?   -  person handle    schedule 07.05.2017
comment
Я проверил связанные вопросы и не смог найти решение для моего случая. Я также пытался манипулировать списком имен, прежде чем передать его в функцию очистки, но его декодирование и кодирование снова не помогло.   -  person aviss    schedule 07.05.2017
comment
Измените этот text=u"{}".format(text), чтобы использовать вместо него decode(...).   -  person stovfl    schedule 07.05.2017
comment
Я пробовал это до того, как получил эту ошибку: TypeError: Argument 'string' имеет неправильный тип (ожидаемый unicode, полученный str)   -  person aviss    schedule 07.05.2017
comment
Строка from __future___ import ... не будет работать, если она не находится в начале скрипта.   -  person alvas    schedule 10.05.2017
comment
Возможный дубликат Как исправить: UnicodeDecodeError: кодек 'ascii' может не декодировать байт   -  person alvas    schedule 17.05.2017


Ответы (3)


Когда вы получаете ошибку декодирования с кодеком 'ascii', это обычно указывает на то, что байтовая строка используется в контексте, где требуется строка Unicode (в Python 2, Python 3 вообще этого не допустит).

Поскольку вы импортировали from __future__ import unicode_literals, строка "'s" имеет кодировку Unicode. Это означает, что строка, которую вы пытаетесь strip, также должна быть строкой Unicode. Исправьте это, и вы больше не будете получать ошибку.

person Mark Ransom    schedule 07.05.2017
comment
Это именно то, что я пытаюсь исправить. - person aviss; 08.05.2017
comment
@aviss, после удаления у вас был один ответ, в котором точно говорилось, как это исправить. Я недостаточно знаю о requests или BeautifulSoup, чтобы вдаваться в подробности. - person Mark Ransom; 08.05.2017

Как прокомментировал @MarkRansom, игнорирование символов, отличных от ascii, укусит вас.

Сначала взгляните на

Также обратите внимание, что это анти-шаблон: Почему НЕ следует использовать sys.setdefaultencoding(utf-8) в скрипте py?

Самое простое решение - просто использовать Python3, и это уменьшит некоторую боль.

>>> import requests
>>> from bs4 import BeautifulSoup
>>> import spacy
>>> nlp = spacy.load('en')

>>> url = "http://www.bbc.co.uk/news/uk-politics-39784164"
>>> html = requests.get(url).content
>>> bsoup = BeautifulSoup(html, 'html.parser')
>>> text = '\n'.join(p.text for d in bsoup.find_all( 'div', {'class': 'story-body__inner'}) for p in d.find_all('p') if p.text.strip())

>>> import spacy
>>> nlp = spacy.load('en')
>>> doc = nlp(text)
>>> names = [ent for ent in doc.ents if ent.ent_type_ == 'PERSON']

person alvas    schedule 17.05.2017

Наконец-то я исправил свой код. Я удивлен, как легко это выглядит, но мне потребовалось так много времени, чтобы добраться туда, и я видел так много людей, озадаченных одной и той же проблемой, поэтому я решил опубликовать свой ответ.

Добавление этой небольшой функции перед передачей имен для дальнейшей очистки решило мою проблему.

def decode(names):        
    decodednames = []
    for name in names:
        decodednames.append(unicode(name, errors='ignore'))
    return decodednames

SpaCy по-прежнему считает, что 59 миллиардов фунтов стерлингов — это ЧЕЛОВЕК, но я не возражаю, я могу разобраться с этим позже в своем коде.

Рабочий код:

import urllib
import requests
from bs4 import BeautifulSoup
import spacy
from spacy.en import English
from __future__ import unicode_literals
nlp_toolkit = English()
nlp = spacy.load('en')

def get_text(url):
    r = requests.get(url)
    soup = BeautifulSoup(r.content, "lxml")

    # delete unwanted tags:
    for s in soup(['figure', 'script', 'style']):
        s.decompose()

    # use separator to separate paragraphs and subtitles!
    article_soup = [s.get_text(separator="\n", strip=True) for s in soup.find_all( 'div', {'class': 'story-body__inner'})]

    text = ''.join(article_soup)
    return text

# using spacy
def get_names(all_tags):
    names=[]
    for ent in all_tags.ents:
        if ent.label_=="PERSON":
            names.append(str(ent))
    return names

def decode(names):        
    decodednames = []
    for name in names:
        decodednames.append(unicode(name, errors='ignore'))
    return decodednames

def cleaning_names(names):
    new_names = [s.strip("'s") for s in names] # remove 's' from names
    myset = list(set(new_names)) #remove duplicates
    return myset

def main():
    url = "http://www.bbc.co.uk/news/uk-politics-39784164"
    text=get_text(url)
    text=u"{}".format(text)
    all_tags = nlp(text)
    names = get_person(all_tags)
    print "names:"
    print names
    decodednames = decode(names)
    mynewlist = cleaning_names(decodednames)
    print mynewlist

if __name__ == '__main__':
    main()

который дает мне это без ошибок:

имена: ['Ник Клегг', 'Брексит', '\xc2\xa359bn', 'Тереза ​​Мэй', 'Брексит', 'Брексит', 'Мистер Клегг', 'Мистер Клегг', 'Мистер Клегг', 'Брексит' , 'Мистер Клегг', 'Тереза ​​Мэй'] [u'Мистер Клегг', u'Брексит', u'Ник Клегг', u'59bn', u'Тереза ​​Мэй']

person aviss    schedule 09.05.2017
comment
Конечно, вы можете просто игнорировать все символы, которые не являются ASCII, это просто. Хотя, возможно, позже он вернется, чтобы укусить вас. Правильный способ выполнить преобразование — позволить библиотекам сделать это за вас, потому что они знают соответствующую кодировку, а вы — нет. - person Mark Ransom; 09.05.2017