Décoder les entités HTML en chaîne Python?

266

J'analyse du HTML avec Beautiful Soup 3, mais il contient des entités HTML que Beautiful Soup 3 ne décode pas automatiquement pour moi:

>>> from BeautifulSoup import BeautifulSoup

>>> soup = BeautifulSoup("<p>&pound;682m</p>")
>>> text = soup.find("p").string

>>> print text
&pound;682m

Comment puis-je décoder les entités HTML textpour obtenir à la "£682m"place de "&pound;682m".

jkp
la source

Réponses:

521

Python 3.4+

Utilisation html.unescape():

import html
print(html.unescape('&pound;682m'))

FYI html.parser.HTMLParser.unescapeest obsolète et devait être supprimé en 3.5 , bien qu'il ait été laissé par erreur. Il sera bientôt supprimé de la langue.


Python 2.6-3.3

Vous pouvez utiliser à HTMLParser.unescape()partir de la bibliothèque standard:

>>> try:
...     # Python 2.6-2.7 
...     from HTMLParser import HTMLParser
... except ImportError:
...     # Python 3
...     from html.parser import HTMLParser
... 
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m

Vous pouvez également utiliser la sixbibliothèque de compatibilité pour simplifier l'importation:

>>> from six.moves.html_parser import HTMLParser
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m
luc
la source
9
cette méthode ne semble pas échapper aux caractères comme "& # 8217;" sur le moteur d'application Google, bien qu'il fonctionne localement sur python2.6. Il décode toujours les entités (comme
``
Comment une API non documentée peut-elle être déconseillée? Modifié la réponse.
Markus Unterwaditzer
@MarkusUnterwaditzer il n'y a aucune raison pour qu'une méthode non documentée ne puisse pas être dépréciée. Celui-ci lance des avertissements de dépréciation - voir ma modification de la réponse.
Mark Amery
Il semblerait plus logique que, plutôt que juste la unescapeméthode, le HTMLParsermodule entier soit déconseillé en faveur de html.parser.
Tom Russell
À noter pour Python 2: les caractères spéciaux sont remplacés par leurs homologues de codage Latin-1 (ISO-8859-1). Par exemple, il peut être nécessaire de le faire h.unescape(s).encode("utf-8"). Les documents: "" "La définition fournie ici contient toutes les entités définies par XHTML 1.0 qui peuvent être gérées en utilisant une simple substitution textuelle dans le jeu de caractères Latin-1 (ISO-8859-1)" ""
lâche anonyme
65

Beautiful Soup gère la conversion d'entité. Dans Beautiful Soup 3, vous devrez spécifier l' convertEntitiesargument au BeautifulSoupconstructeur (voir la section 'Entity Conversion' des documents archivés). Dans Beautiful Soup 4, les entités sont décodées automatiquement.

Belle soupe 3

>>> from BeautifulSoup import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>", 
...               convertEntities=BeautifulSoup.HTML_ENTITIES)
<p682m</p>

Belle soupe 4

>>> from bs4 import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>")
<html><body><p682m</p></body></html>
Ben James
la source
+1. Aucune idée de la façon dont j'ai raté cela dans les documents: merci pour l'info. Je vais accepter la réponse de luc car il utilise la bibliothèque standard que j'ai spécifiée dans la question (pas importante pour moi) et son utilisation probablement plus générale pour d'autres personnes.
jkp
5
BeautifulSoup4utilise HTMLParser, principalement. Voir la source
scharfmn
4
Comment obtenir la conversion dans Beautiful Soup 4 sans tout le HTML superflu qui ne faisait pas partie de la chaîne d'origine? (ie <html> et <body>)
Praxiteles
@Praxiteles: BeautifulSoup ('& pound; 682m', "html.parser") stackoverflow.com/a/14822344/4376342
Soitje
13

Vous pouvez utiliser replace_entities à partir de la bibliothèque w3lib.html

In [202]: from w3lib.html import replace_entities

In [203]: replace_entities("&pound;682m")
Out[203]: u'\xa3682m'

In [204]: print replace_entities("&pound;682m")
£682m
Corvax
la source
2

Beautiful Soup 4 vous permet de définir un formateur à votre sortie

Si vous passez formatter=None, Beautiful Soup ne modifiera pas du tout les chaînes en sortie. Il s'agit de l'option la plus rapide, mais elle peut conduire à la création de Beautiful Soup HTML / XML non valide, comme dans ces exemples:

print(soup.prettify(formatter=None))
# <html>
#  <body>
#   <p>
#    Il a dit <<Sacré bleu!>>
#   </p>
#  </body>
# </html>

link_soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>')
print(link_soup.a.encode(formatter=None))
# <a href="http://example.com/?foo=val1&bar=val2">A link</a>
LoicUV
la source
Cela ne répond pas à la question. (De plus, je n'ai aucune idée de ce que les documents disent n'est pas valide sur le dernier bit de HTML ici.)
Mark Amery
<< Sacré bleu! >> est la partie invalide, car elle a échappé <et> et va casser le html autour d'elle. Je sais que c'est un message tardif de ma part, mais au cas où quelqu'un regarderait et se demanderait ...
GMasucci
0

J'ai eu un problème d'encodage similaire. J'ai utilisé la méthode normalize (). J'obtenais une erreur Unicode en utilisant la méthode pandas .to_html () lors de l'exportation de mon bloc de données vers un fichier .html dans un autre répertoire. J'ai fini par faire ça et ça a marché ...

    import unicodedata 

L'objet dataframe peut être ce que vous voulez, appelons-le table ...

    table = pd.DataFrame(data,columns=['Name','Team','OVR / POT'])
    table.index+= 1

encoder les données de la table afin que nous puissions les exporter vers un fichier .html dans le dossier des modèles (cela peut être l'emplacement que vous souhaitez :))

     #this is where the magic happens
     html_data=unicodedata.normalize('NFKD',table.to_html()).encode('ascii','ignore')

exporter une chaîne normalisée dans un fichier html

    file = open("templates/home.html","w") 

    file.write(html_data) 

    file.close() 

Référence: documentation unicodedata

Alex
la source
-4

Ce n'est probablement pas pertinent ici. Mais pour éliminer ces entites html d'un document entier, vous pouvez faire quelque chose comme ceci: (Supposons que document = page et veuillez pardonner le code bâclé, mais si vous avez des idées sur la façon de l'améliorer, je suis toutes les oreilles - je suis nouveau pour ce).

import re
import HTMLParser

regexp = "&.+?;" 
list_of_html = re.findall(regexp, page) #finds all html entites in page
for e in list_of_html:
    h = HTMLParser.HTMLParser()
    unescaped = h.unescape(e) #finds the unescaped value of the html entity
    page = page.replace(e, unescaped) #replaces html entity with unescaped value
Neil Aggarwal
la source
7
Non! Vous n'avez pas besoin de faire correspondre les entités HTML vous-même et de les boucler; .unescape()fait ça pour vous . Je ne comprends pas pourquoi vous et Rob avez publié ces solutions trop compliquées qui roulent leur propre correspondance d'entité alors que la réponse acceptée montre déjà clairement que .unescape()peut trouver des entités dans la chaîne.
Mark Amery