TypeError: nie można użyć wzorca łańcuchowego na obiekcie podobnym do bajtów w funkcji re.findall ()

108

Próbuję się dowiedzieć, jak automatycznie pobierać adresy URL ze strony. W poniższym kodzie próbuję uzyskać tytuł strony internetowej:

import urllib.request
import re

url = "http://www.google.com"
regex = r'<title>(,+?)</title>'
pattern  = re.compile(regex)

with urllib.request.urlopen(url) as response:
   html = response.read()

title = re.findall(pattern, html)
print(title)

I otrzymuję ten nieoczekiwany błąd:

Traceback (most recent call last):
  File "path\to\file\Crawler.py", line 11, in <module>
    title = re.findall(pattern, html)
  File "C:\Python33\lib\re.py", line 201, in findall
    return _compile(pattern, flags).findall(string)
TypeError: can't use a string pattern on a bytes-like object

Co ja robię źle?

Inspired_Blue
źródło
1
możliwy duplikat Convert bytes to string Python
gnat

Odpowiedzi:

161

Chcesz przekonwertować html (obiekt typu bajtowego) na ciąg znaków używając .decodenp html = response.read().decode('utf-8').

Zobacz Konwertowanie bajtów na ciąg w języku Python

skalisty
źródło
To rozwiązało błąd, TypeError: cannot use a string pattern on a bytes-like objectale potem pojawiły się błędy takie jak UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb2 in position 1: invalid start byte. Naprawiłem to, używając .decode("utf-8", "ignore"): stackoverflow.com/questions/62170614/…
baptx
„ignoruj” ignoruje. Jeśli tego chcesz, wszystko jest w porządku. Czasami jednak tego rodzaju problem przeczy głębszemu problemowi, np. Że rzecz, którą chcesz zdekodować, naprawdę nie jest dekodowalna lub nie ma być, np. Tekst skompresowany lub zaszyfrowany. Lub może wymagać innego kodowania, na przykład utf-16. Caveat emptor.
kamienisty
28

Problem polega na tym, że twoje wyrażenie regularne jest łańcuchem, ale htmlzawiera bajty :

>>> type(html)
<class 'bytes'>

Ponieważ python nie wie, w jaki sposób te bajty są kodowane, zgłasza wyjątek, gdy próbujesz użyć na nich wyrażenia regularnego typu string.

Możesz zamienić decodebajty na ciąg:

html = html.decode('ISO-8859-1')  # encoding may vary!
title = re.findall(pattern, html)  # no more error

Lub użyj wyrażenia regularnego bajtów:

regex = rb'<title>(,+?)</title>'
#        ^

W tym konkretnym kontekście możesz pobrać kodowanie z nagłówków odpowiedzi:

with urllib.request.urlopen(url) as response:
    encoding = response.info().get_param('charset', 'utf8')
    html = response.read().decode(encoding)

Więcej informacji można znaleźć w urlopendokumentacji .

Aran-Fey
źródło