TypeError: impossible d'utiliser un modèle de chaîne sur un objet de type octets dans re.findall ()

108

J'essaie d'apprendre à récupérer automatiquement les URL d'une page. Dans le code suivant, j'essaie d'obtenir le titre de la page Web:

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)

Et j'obtiens cette erreur inattendue:

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

Qu'est-ce que je fais mal?

Inspiré_Bleu
la source
1
duplication possible de Convert bytes to a Python string
gnat

Réponses:

161

Vous voulez convertir html (un objet de type octet) en une chaîne en utilisant .decode, par exemple html = response.read().decode('utf-8').

Voir Convertir des octets en une chaîne Python

rocheux
la source
Cela a résolu l'erreur, TypeError: cannot use a string pattern on a bytes-like objectmais j'ai eu des erreurs comme UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb2 in position 1: invalid start byte. Je l'ai corrigé en utilisant .decode("utf-8", "ignore"): stackoverflow.com/questions/62170614/…
baptx
"ignorer" ignore. Si c'est ce que vous voulez, alors tout va bien. Cependant, parfois, ce genre de problème cache un problème plus profond, par exemple le fait que la chose que vous voulez décoder n'est vraiment pas décodable ou censée être, par exemple du texte compressé ou chiffré. Ou il peut avoir besoin d'un autre encodage comme utf-16. Caveat emptor.
rocailleux
28

Le problème est que votre regex est une chaîne, mais htmlest octets :

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

Puisque python ne sait pas comment ces octets sont encodés, il lève une exception lorsque vous essayez d'utiliser une chaîne regex sur eux.

Vous pouvez soit decodeles octets en une chaîne:

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

Ou utilisez une expression régulière d'octets:

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

Dans ce contexte particulier, vous pouvez obtenir le codage à partir des en-têtes de réponse:

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

Consultez la urlopendocumentation pour plus de détails.

Aran-Fey
la source