Decodificar entidades HTML na string Python?

266

Estou analisando um pouco de HTML com o Beautiful Soup 3, mas ele contém entidades HTML que o Beautiful Soup 3 não decodifica automaticamente para mim:

>>> from BeautifulSoup import BeautifulSoup

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

>>> print text
&pound;682m

Como decodificar as entidades HTML textpara obter em "£682m"vez de "&pound;682m".

jkp
fonte

Respostas:

521

Python 3.4 ou superior

Use html.unescape():

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

O FYI html.parser.HTMLParser.unescapeestá obsoleto e deveria ser removido no 3.5 , embora tenha sido deixado por engano. Ele será removido do idioma em breve.


Python 2.6-3.3

Você pode usar HTMLParser.unescape()na biblioteca padrão:

>>> 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

Você também pode usar a sixbiblioteca de compatibilidade para simplificar a importação:

>>> from six.moves.html_parser import HTMLParser
>>> h = HTMLParser()
>>> print(h.unescape('&pound;682m'))
£682m
luc
fonte
9
esse método não parece escapar de caracteres como "& # 8217;" no google app engine, embora funcione localmente em python2.6. Ele ainda decodifica entidades (como "pelo menos")
gfxmonk
Como uma API não documentada pode ser preterida? Editou a resposta.
Markus Unterwaditzer
@MarkusUnterwaditzer, não há razão para que um método não documentado não possa ser preterido. Este lança avisos de descontinuação - veja minha edição da resposta.
Mark Amery
Parece mais lógico que, em vez de apenas o unescapemétodo, todo o HTMLParsermódulo foi preterido em favor de html.parser.
Tom Russell
Vale destacar o Python 2: caracteres especiais são substituídos por seus equivalentes de codificação Latin-1 (ISO-8859-1). Por exemplo, pode ser necessário h.unescape(s).encode("utf-8"). Os documentos: "" "A definição fornecida aqui contém todas as entidades definidas pelo XHTML 1.0 que podem ser manipuladas usando simples substituição de texto no conjunto de caracteres Latin-1 (ISO-8859-1)" ""
covarde anônimo
65

Beautiful Soup trata da conversão de entidades. No Beautiful Soup 3, você precisará especificar o convertEntitiesargumento para o BeautifulSoupconstrutor (consulte a seção 'Conversão de entidade' dos documentos arquivados). No Beautiful Soup 4, as entidades são decodificadas automaticamente.

Sopa bonita 3

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

Sopa bonita 4

>>> from bs4 import BeautifulSoup
>>> BeautifulSoup("<p>&pound;682m</p>")
<html><body><p682m</p></body></html>
Ben James
fonte
+1. Não faço ideia de como eu perdi isso nos documentos: obrigado pela informação. Vou aceitar a resposta de luc, porque ele usa a biblioteca padrão que especifiquei na pergunta (que não é importante para mim) e provavelmente é de uso geral para outras pessoas.
jkp
5
BeautifulSoup4usa HTMLParser, principalmente. Veja a fonte
scharfmn
4
Como obtemos a conversão no Beautiful Soup 4 sem todo o HTML estranho que não fazia parte da string original? (ou seja, <html> e <body>)
Praxiteles
@Praxiteles: BeautifulSoup ('& pound; 682m', "html.parser") stackoverflow.com/a/14822344/4376342
Soitje
13

Você pode usar replace_entities da biblioteca 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
fonte
2

Beautiful Soup 4 permite que você defina um formatador para sua saída

Se você passar formatter=None, o Beautiful Soup não modificará as strings na saída. Essa é a opção mais rápida, mas pode levar a Beautiful Soup a gerar HTML / XML inválido, como nestes exemplos:

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
fonte
Isso não responde à pergunta. (Além disso, eu não tenho nenhuma idéia do que os documentos estão dizendo é inválido sobre o bit final de HTML aqui.)
Mark Amery
<< Sacré bleu! >> é a parte inválida, pois tem escapado <e> e quebrará o html ao seu redor. Eu sei que este é um post atrasado de mim, mas no caso de alguém estar olhando e se perguntando ...
GMasucci
0

Eu tive um problema de codificação semelhante. Eu usei o método normalize (). Eu estava recebendo um erro Unicode usando o método pandas .to_html () ao exportar meu quadro de dados para um arquivo .html em outro diretório. Acabei fazendo isso e funcionou ...

    import unicodedata 

O objeto dataframe pode ser o que você quiser, vamos chamá-lo de tabela ...

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

codifique os dados da tabela para que possamos exportá-los para o arquivo .html na pasta de modelos (este pode ser o local que você desejar :))

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

exportar string normalizada para arquivo html

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

    file.write(html_data) 

    file.close() 

Referência: documentação unicodedata

Alex
fonte
-4

Provavelmente isso não é relevante aqui. Mas, para eliminar essas entidades html de um documento inteiro, você pode fazer algo assim: (Assuma document = page e perdoe o código incorreto, mas se você tiver idéias de como torná-lo melhor, sou todo ouvidos - sou novo em isto).

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
fonte
7
Não! Você não precisa corresponder as entidades HTML e fazer um loop sobre elas; .unescape()faz isso por você . Não entendo por que você e Rob postaram essas soluções complicadas demais que rolam a correspondência de suas próprias entidades quando a resposta aceita já mostra claramente que é .unescape()possível encontrar entidades na string.
Mark Amery