Documentação Beautiful Soup
============================
.. image:: 6.1.jpg
:align: right
:alt: "O Lacaio-Peixe começou tirando debaixo do braço uma grande carta, quase tão grande quanto ele mesmo."
`Beautiful Soup The Dormouse's story Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well. ...
#
# The Dormouse's story
#
#
# Once upon a time there were three little sisters; and their names were
#
# Elsie
#
# ,
#
# Lacie
#
# and
#
# Tillie
#
# ; and they lived at the bottom of a well.
#
# ...
# The Dormouse's story Back to the homepage Back to the homepage The Dormouse's story Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well. ... The Dormouse's story ", e daí por diante. O Beautiful Soup oferece ferramentas
para reconstruir a análise inicial do documento.
.. _element-generators:
``.next_element`` e ``.previous_element``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
O atributo ``.next_element`` de uma string ou tag aponta para
qualquer coisa que tenha sido interpretado posteriormente.
Isso deveria ser o mesmo que ``.next_sibling``, mas é
drasticamente diferente.
Aqui está a tag final no "three sisters". Sua
``.next_sibling`` é uma string: a conclusão da sentença
que foi interrompida pelo início da tag .::
last_a_tag = soup.find("a", id="link3")
last_a_tag
# Tillie
last_a_tag.next_sibling
# '; and they lived at the bottom of a well.'
Mas no ``.next_element`` da tag , o que é analisado imediatamente
depois da tag `não` é o resto da sentença: é a palavra "Tillie".
last_a_tag.next_element
# u'Tillie'
Isso porque na marcação original, a palavra "Tillie" apareceu
antes do ponto e virgula. O parser encontrou uma tag , então
a palavra "Tillie", então fechando a tag , então o ponto e vírgula e o
resto da sentença. O ponto e vírgula estão no mesmo nível que a tag ,
mas a palavra "Tillie" foi encontrada primeiro.
O atributo ``.previous_element`` é exatamente o oposto de
``.next_element``. Ele aponta para qualquer elemento que
seja analisado antes do respectivo::
last_a_tag.previous_element
# u' and\n'
last_a_tag.previous_element.next_element
# Tillie
``.next_elements`` e ``.previous_elements``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Você deve ter entendido a idéia agora. Você pode usar estes iteradores
para andar para frente e para atrás no documento quando ele for analisado::
for element in last_a_tag.next_elements:
print(repr(element))
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# ... The Dormouse's story Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well. ... ::
soup.find_all(has_class_but_no_id)
# [ The Dormouse's story Once upon a time there were... ... . Não irá encontrar as tags ,
porque elas definem "class e "id" ao mesmo tempo. Ela não encontrará
as tags e The Dormouse's story com a classe CSS "title"?
Vamos dar uma olhada nos argumentos de ``find_all()``.
.. _name:
O argumento ``name``
^^^^^^^^^^^^^^^^^^^^
Passe um valor para ``name`` e você dirá para o Beautiful Soup
considerar apenas as tags com certos nomes. Strings de texto seão ignoradas,
assim como os nomes que não corresponderem ao argumento ``name``
Este é o uso mais simples::
soup.find_all("title")
# [ The Dormouse's story Once upon a time there were three little sisters; and their names were
# Elsie,
# Lacie and
# Tillie;
# and they lived at the bottom of a well. é uma mãe
indireta da string e nossa busca também a encontra. Há uma tag com
a classe CSS "title" em algum lugar no documento, mas não é nenhuma das tags mães
da string, portanto, não podemos encontrá-la com ``find_parents()``.
Você já deve ter feito a conexão entre ``find_parent()`` e
``find_parents()``, e os atributos `.parent`_ e `.parents`_ mencionados
anteriormente. A conexão é muito forte. Estes métodos de busca utilizam ``.parents``
para iterar sobre todos as mãesS e compara cada um com o filtro passado
para verificar se preenche o requisito.
``find_next_siblings()`` e ``find_next_sibling()``
----------------------------------------------------
Signature: find_next_siblings(:ref:`name ... The Dormouse's story ... do documento foi
encontrada, mesmo que não esteja na mesma parte da árvore que onde começamos.
Para estes métodos, o que importa é que um elemento corresponda ao filtro e esteja
depois do elemento de início no documento.
``find_all_previous()`` e ``find_previous()``
-----------------------------------------------
Signature: find_all_previous(:ref:`name Once upon a time there were three little sisters; ... The Dormouse's story que contém a tag por onde começamos.
Isso não deveria ser tão surpreendente: nós estamos olhando para todas as tags
que apareceram anteriormente no documento incluindo aquela onde começamos. Uma
tag que contenha uma tag deve aparecer antes da tag que ela contém.
Seletores CSS
-------------
A partir da versão 4.7.0, o Beautiful Soup suporta a maior parte dos seletores CSS4
através do projeto `SoupSieve ... I wish I was bold. I wish I was bold. A one A one, a two
# A one
# , a two
#
# A one, a two
# The law firm of Dewey, Cheatem, & Howe The law firm of Dewey, Cheatem, & Howe Il a dit <<Sacré bleu!>>
# Il a dit <<Sacré bleu!>>
#
# Il a dit <<Sacré bleu!>>
#
# Il a dit <
# IL A DIT <Extremely bold
Atributos
^^^^^^^^^^
Uma tag pode ter inúmeros atributos. A tag ````
possui um atributo "id" que possui o valor "boldest". Você pode
acessar um atributo de uma tag tratando-a como um dicionário::
tag['id']
# u'boldest'
Você pode acessar este dicionário diretamente através de ``.attrs``::
tag.attrs
# {u'id': 'boldest'}
Você pode adicionar, remover ou modificar os atributos de uma tag. Novamente, isso pode
ser feito tratando a tag como um dicionário::
tag['id'] = 'verybold'
tag['another-attribute'] = 1
tag
#
del tag['id']
del tag['another-attribute']
tag
#
tag['id']
# KeyError: 'id'
print(tag.get('id'))
# None
.. _multivalue:
Atributos com múltiplos valores
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
O HTML 4 define alguns atributos que podem ter múltiplos valores. O HTML 5
removeu alguns deles, mas definiu alguns novos. O atributo mais comum
que pode receber múltiplos valores é o ``class`` (ou seja, a tag pode ter mais de uma classe CSS).
Outros são ``rel``, ``rev``, ``accept-charset``, ``headers``, e ``accesskey``.
O Beautiful Soup apresenta o(s) valor(es) de um atributo deste tipo como uma lista::
css_soup = BeautifulSoup('')
css_soup.p['class']
# ["body"]
css_soup = BeautifulSoup('')
css_soup.p['class']
# ["body", "strikeout"]
Se um atributo possui mais de um valor, mas não é um atributo
que aceita múltiplos valores conforme definido por qualquer versão do
padrão HTML, o Beautiful Soup retornará como um valor único::
id_soup = BeautifulSoup('')
id_soup.p['id']
# 'my id'
Quando a tag é transformada novamente em string, os valores do atributo múltiplo
são consolidados::
rel_soup = BeautifulSoup('No longer bold
``NavigableString`` suporta a maior parte das características descritas em
`Navegando pela árvore`_ e `Buscando na árvore`_, mas não todas elas.
Em particular, desde que uma string não pode conter de tudo (da maneira que
uma tag pode conter uma string ou outra tag), as strings não suportam os
atributos ``.contents`` ou ``.string`` ou o método ``find()``.
Se você quer utilizar uma ``NavigableString`` fora do Beautiful Soup,
você deve chamar o ``unicode()`` para transformá-la em uma string Unicode Python
padrão. Se você não fizer isso, sua string irá carregar uma referência de toda sua
árvore Beautiful Soup, mesmo que você já não esteja mais usando ela, o que é um grande
desperdício de memória.
``BeautifulSoup``
-----------------
O objeto ``BeautifulSoup`` em si representa o documento como um todo.
Para maioria dos propósitos, você pode tratá-lo como um objeto :ref:`Tag`.
Isso significa que irá suportar a maioria dos métodos descritos em
`Navegando pela árvore`_ e `Buscando na árvore`_.
Sabendo que o objeto ``BeautifulSoup`` não corresponde a uma tag
HTML ou XML propriamente dita, ele não tem nome e nem atributos. Mas em alguns
casos é útil observar seu ``.name``; então, foi dado o especial
``.name`` "[document]"::
soup.name
# u'[document]'
Comentários e outras strings especiais
--------------------------------------
``Tag``, ``NavigableString``, e ``BeautifulSoup`` abrangem quase
tudo o que você encontrará em um arquivo HTML ou XML, mas há alguns
pontos faltando. O único deles que você provavelmente precisará se preocupar
é o comentário::
markup = ""
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
# Extremely bold
del tag['class']
del tag['id']
tag
# Extremely bold
Modificando ``.string``
-----------------------
Se você definir o um atributo ``.string`` de uma tag, o conteúdo da
tag será substituido pela string que foi passada::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
tag = soup.a
tag.string = "New link text."
tag
# New link text.
Cuidado: se a tag conter outra(s) tag(s), ela(s) e todo seu conteúdo
serão destruídos.
``append()``
------------
Você pode adicionar algo no conteúdo de uma tag com ``Tag.append()``. Funciona
da mesma maneira que ``.append()`` de uma lista::
soup = BeautifulSoup("Foo")
soup.a.append("Bar")
soup
# FooBar
soup.a.contents
# [u'Foo', u'Bar']
``extend()``
------------
Com início no Beautiful Soup 4.7.0, ``Tag`` também suporta um método chamado
``.extend()``, o qual funciona da mesma maneira que chamando ``.extend()`` em
uma lista::
soup = BeautifulSoup("Soup")
soup.a.extend(["'s", " ", "on"])
soup
# Soup's on
soup.a.contents
# [u'Soup', u''s', u' ', u'on']
``NavigableString()`` e ``.new_tag()``
-------------------------------------------------
Se você precisar adicionar uma string a um documento, sem problema -- você
pode passar uma string Python através de ``append()``, ou você pode chamar
o construtor ``NavigableString``::
soup = BeautifulSoup("")
tag = soup.b
tag.append("Hello")
new_string = NavigableString(" there")
tag.append(new_string)
tag
# Hello there.
tag.contents
# [u'Hello', u' there']
Se você quiser criar um comentário ou alguma outra subclasse de
``NavigableString``, apenas chame o construtor::
from bs4 import Comment
new_comment = Comment("Nice to see you.")
tag.append(new_comment)
tag
# Hello there
tag.contents
# [u'Hello', u' there', u'Nice to see you.']
(Esta é uma funcionalidade nova no Beautiful Soup 4.4.0.)
E se você precisar criar uma nova tag? A melhor solução
é chamar o método ``BeautifulSoup.new_tag()``::
soup = BeautifulSoup("")
original_tag = soup.b
new_tag = soup.new_tag("a", href="http://www.example.com")
original_tag.append(new_tag)
original_tag
#
new_tag.string = "Link text."
original_tag
# Link text.
Somente o primeiro argumento (o nome da tag) é obrigatório.
``insert()``
------------
``Tag.insert()`` funciona assim como ``Tag.append()``, exceto que o novo elemento
não será inserido ao final do ``.contents`` de sua tag mãe. Ele será inserido em qualquer posição
numérica que você informar. Funciona assim como ``.insert()`` em uma lista::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
tag = soup.a
tag.insert(1, "but did not endorse ")
tag
# I linked to but did not endorse example.com
tag.contents
# [u'I linked to ', u'but did not endorse', example.com]
``insert_before()`` e ``insert_after()``
------------------------------------------
O método ``insert_before()`` insere tags ou strings imediatamente antes de algo
na árvore::
soup = BeautifulSoup("stop")
tag = soup.new_tag("i")
tag.string = "Don't"
soup.b.string.insert_before(tag)
soup.b
# Don'tstop
O método ``insert_after()`` insere tags ou strings imediatamente após algo
na árvore::
div = soup.new_tag('div')
div.string = 'ever'
soup.b.i.insert_after(" you ", div)
soup.b
# Don't you
")
print(soup.encode(formatter="html"))
#
print(soup.encode(formatter="html5"))
#
Se você passar ``formatter=None``, Beautiful Soup não irá modificar
as strings na saída. Esta é a opção mais rápida, mas permitirá que o
Beautiful Soup gere HTML/XML inválidos, como nestes exemplos::
print(soup.prettify(formatter=None))
#
#
#
aberta. Este parser também adiciona uma tag
vazia ao documento. Aqui está o mesmo documento analisado com o parser HTML nativo do Python:: BeautifulSoup("", "html.parser") # Assim como html5lib, este parser ignora a tag de fechamento . Este parser também não realiza nenhuma tentatida de criar um HTML bem formatado adicionando uma tag . Como lxml, ele nem se importa em adicionar uma tag . Sendo o documento "" inválido, nenhuma dessas técnicas é a maneira "correta" de lidar com isso. O html5lib utiliza técnicas que são parte do padrão HTML5, portanto vendo sendo definido como a maneira "mais correta", mas todas as três técnicas são legítimas. Diferenças entre analisadores podem afetar o seu script. Se você está planejando distribuir seu script para outras pessoas, ou rodá-lo em múltiplas máquinas, você deve especificar o analisador no construtor ``BeautifulSoup``. Isso irá reduzir as chances de que seus usuários analisem um documento de forma diferente da maneira como você analisou. Codificação (Encoding) ====================== Todo documento HTML ou XML é escrito em uma codificação (encoding) específica como ASCII ou UTF-8. Mas quando você carrega um documento no BeautifulSoup, você irá descobrir que ele foi convertido para Unicode:: markup = "Sacr\xe9 bleu!
''' soup = BeautifulSoup(markup) print(soup.prettify()) # # # # # ## Sacré bleu! #
# # Note que a tag foi reescrita para refletir o fato que o documento é agora um UTF-8. Se você não quiser um UTF-8, você pode passar a codificação desejada como parâmetro de ``prettify()``:: print(soup.prettify("latin-1")) # # # # ... Você também pode chamar encode() no objeto ``BeautifulSoup`` ou em qualquer elemento do objeto, assim como se faz em uma string Python:: soup.p.encode("latin-1") # 'Sacr\xe9 bleu!
' soup.p.encode("utf-8") # 'Sacr\xc3\xa9 bleu!
' Qualquer caractere que não pode ser representado na codificação escolhida irá ser convertida para uma entidade de referência numérica XML. Abaixo você tem um documento que inclui o caractere Unicode SNOWMAN:: markup = u"\N{SNOWMAN}" snowman_soup = BeautifulSoup(markup) tag = snowman_soup.b O caractere SNOWMAN faz parte da documentação UTF-8 (algo como ☃), mas não possui representação para este caractere em ISO-latin-1 ou ASCII, portanto ele é convertido para "☃" para as essas codificações:: print(tag.encode("utf-8")) # ☃ print tag.encode("latin-1") # ☃ print tag.encode("ascii") # ☃ Unicode, Dammit --------------- Você pode usar o `Unicode, Dammit` fora do Beautiful Soup. É útil quando você possui dados em uma codificação desconhecida e quer simplesmente convertê-la para Unicode:: from bs4 import UnicodeDammit dammit = UnicodeDammit("Sacr\xc3\xa9 bleu!") print(dammit.unicode_markup) # Sacré bleu! dammit.original_encoding # 'utf-8' As respostas do `Unicode, Dammit` serão um pouco mais precisas se você instalar as bibliotecas ``chardet`` ou ``cchardet``. Quanto maior a quantidade de dados no arquivo que você passar para o `Unicode, Dammit`, mais precisas serão as conversões. Se você possui suas suspeitas sobre qual a codificação original, você pode passar as opções em uma lista:: dammit = UnicodeDammit("Sacr\xe9 bleu!", ["latin-1", "iso-8859-1"]) print(dammit.unicode_markup) # Sacré bleu! dammit.original_encoding # 'latin-1' `Unicode, Dammit` possui duas características que o Beautiful Soup não utiliza. Smart quotes ^^^^^^^^^^^^ Você pode utilizar `Unicode, Dammit` para converter Microsoft smart quotes para entidades HTML ou XML:: markup = b"I just \x93love\x94 Microsoft Word\x92s smart quotes
" UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="html").unicode_markup # u'I just “love” Microsoft Word’s smart quotes
' UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="xml").unicode_markup # u'I just “love” Microsoft Word’s smart quotes
' Você também pode converter Microsoft smart quotes para ASCII:: UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="ascii").unicode_markup # u'I just "love" Microsoft Word\'s smart quotes
' Espero que você ache estas características úteis, mas o Beautiful Soup não as usa.O Beautiful Soup dá preferência ao comportamento padrão, que é converter para caracteres Unicode:: UnicodeDammit(markup, ["windows-1252"]).unicode_markup # u'I just \u201clove\u201d Microsoft Word\u2019s smart quotes
' Codificação Inconsistente ^^^^^^^^^^^^^^^^^^^^^^^^^ Algumas vezes um documento é em sua maioria UTF-8, mas contém caracteres Windows-1252 assim como (de novo) Microsoft smart quotes. Isso pode acontecer quando um website compostos de dados de muitas fontes diferentes. Você pode utilizar ``UnicodeDammit.detwingle()`` para transformar este documento em um UTF-8 puro. Aqui está um exemplo:: snowmen = (u"\N{SNOWMAN}" * 3) quote = (u"\N{LEFT DOUBLE QUOTATION MARK}I like snowmen!\N{RIGHT DOUBLE QUOTATION MARK}") doc = snowmen.encode("utf8") + quote.encode("windows_1252") Este documento é uma bagunça. O snowmen é um UTF-8 e as aspas são Windows-1252. Você pode exibir o snowmen ou as aspas, mas não os dois ao mesmo tempo:: print(doc) # ☃☃☃�I like snowmen!� print(doc.decode("windows-1252")) # ☃☃☃“I like snowmen!” Decodificar um documento como UTF-8 gera um ``UnicodeDecodeError``, e como um Windows-1252 lhe tras algo sem sentido. Felizmente, ``UnicodeDammit.detwingle()`` irá converter a string para UTF-8 puro, permitindo a você decodificá-la para Unicode e exibir o snowmen e as aspas simultaneamente:: new_doc = UnicodeDammit.detwingle(doc) print(new_doc.decode("utf8")) # ☃☃☃“I like snowmen!” ``UnicodeDammit.detwingle()`` sabe apenas como trabalhar com Windows-1252 contido em UTF-8 (ou vice versa, eu suponho), mas este é o caso mais comum. Note que você deve chamar ``UnicodeDammit.detwingle()`` em seu dado antes de passá-lo para ``BeautifulSoup`` ou para o construtor ``UnicodeDammit``. O Beautiful Soup assume que um documento possui apenas uma codificação, independente de qual ela seja. Se você passar um documento que contém ambos UTF-8 e Windows-1252, é provável que ele pense que todo o documento seja Windows-1252, e o documento parecerá ``☃☃☃“I like snowmen!”``. ``UnicodeDammit.detwingle()`` é novo no Beautiful Soup 4.1.0. Linhas numeradas ================ Os interpretadores ``html.parser` e ``html5lib`` podem rastrear onde, no documento original, cada tag foi encontrada. Você pode acessar esta informação através de ``Tag.sourceline`` (número da linha) e ``Tag.sourcepos`` (posição do início da tag na linha):: markup = "Paragraph 1
\nParagraph 2
" soup = BeautifulSoup(markup, 'html.parser') for tag in soup.find_all('p'): print(tag.sourceline, tag.sourcepos, tag.string) # (1, 0, u'Paragraph 1') # (2, 3, u'Paragraph 2') Note que os dois interpretadores significam coisas levemente diferentes por ``sourceline`` e ``sourcepos``. Para html.parser, estes números representam a posição do sinal `menor que`inicial. Para html5lib, representa a posição do sinal `maior que` final:: soup = BeautifulSoup(markup, 'html5lib') for tag in soup.find_all('p'): print(tag.sourceline, tag.sourcepos, tag.string) # (2, 1, u'Paragraph 1') # (3, 7, u'Paragraph 2') Você pode desabilitar esta característica passando ``store_line_numbers=False` no construtor ``BeautifulSoup``:: markup = "Paragraph 1
\nParagraph 2
" soup = BeautifulSoup(markup, 'html.parser', store_line_numbers=False) soup.p.sourceline # None Esta característica é nova no 4.8.1 e os analisadores baseados no lxml não a suportam. Comparando objetos por igualdade ================================ O Beautiful Soup diz que dois objetos ``NavigableString`` ou ``Tag`` são iguais quando eles apresentam as mesma marcação HTML ou XML. No exemplo abaixo, as duas tags são tratadas como iguais, mesmo estando em partes diferentes da árvore do objeto, porque ambas estão como "pizza":: markup = "I want pizza and more pizza!
" soup = BeautifulSoup(markup, 'html.parser') first_b, second_b = soup.find_all('b') print first_b == second_b # True print first_b.previous_element == second_b.previous_element # False Se você quiser verificar se duas variáveis se referem exatamente ao mesmo objeto, use `is`:: print first_b is second_b # False Copiando objetos Beautiful Soup =============================== Você pode utilizar ``copy.copy()`` para criar uma cópia de qualquer ``Tag`` ou ``NavigableString``:: import copy p_copy = copy.copy(soup.p) print p_copy #I want pizza and more pizza!
A cópia será considerada igual ao original, desde que ela apresente a mesma marcação que o original, mas não será o mesmo objeto:: print soup.p == p_copy # True print soup.p is p_copy # False A única diferença real é que a cópia é completamente separada da árvore original do Beautiful Soup, como se ``extract()`` fosse chamado para ela:: print p_copy.parent # None Isso acontece porque dois objetos ``Tag`` diferentes não podem ocupar o mesmo espaço ao mesmo tempo. Analisando apenas parte de um documento ======================================= Suponhamos que você queira que o Beautiful Soup olhe apenas para as tags de um documento. É um desperdício de tempo e memória analisar todo o documento e, posteriormente, analisar novamente apenas para buscar as tags . Seria muito mais rápido ignorar tudo o que não for em primeiro lugar. A classe ``SoupStrainer`` permite que você escolha qual partes do documento serão analisadas. Você deverá penas criar uma instância de ``SoupStrainer`` e passá-la ao construtor ``BeautifulSoup`` no argumento ``parse_only``. (Note que *esta característica não funcionará se você estiver utilizando o html5lib*. Se você utilizar o html5lib, todo o documento será analisado. Isso acontece porque html5lib constantemente reorganiza a árvore de análise e se alguma parte do documento realmente não fizer parte dela, ela irá quebrar. Para evitar confusão, no exemplo abaixo, forçarei o Beautiful Soup a usar o analisador nativo do Python). ``SoupStrainer`` ---------------- A classe ``SoupStrainer`` recebe os mesmos argumentos que qualquer método em `Buscando na árvore`_: :ref:`nameThe Dormouse's story
Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.
...
""" print(BeautifulSoup(html_doc, "html.parser", parse_only=only_a_tags).prettify()) # # Elsie # # # Lacie # # # Tillie # print(BeautifulSoup(html_doc, "html.parser", parse_only=only_tags_with_id_link2).prettify()) # # Lacie # print(BeautifulSoup(html_doc, "html.parser", parse_only=only_short_strings).prettify()) # Elsie # , # Lacie # and # Tillie # ... # Você pode também passar um ``SoupStrainer`` em qualquer método coberto em `Buscando na árvore`_. Este uso provavelmente não seja muito útil, mas pensei que deveria mencioná-lo:: soup = BeautifulSoup(html_doc) soup.find_all(only_short_strings) # [u'\n\n', u'\n\n', u'Elsie', u',\n', u'Lacie', u' and\n', u'Tillie', # u'\n\n', u'...', u'\n'] Solucionando Problemas ====================== .. _diagnose: ``diagnose()`` -------------- Se você está tendo problemas em entender o que o Beautiful Soup está fazendo com um documento, passe o documento pela função ``diagnose()``. (Nova no Beautiful Soup 4.2.0.) O Beautiful Soup irá retornar um relatório mostrando como diferentes parsers lidam com o documento e irá lhe dizer o Beautiful Soup poderia estar utilizando outro parser:: from bs4.diagnose import diagnose with open("bad.html") as fp: data = fp.read() diagnose(data) # Diagnostic running on Beautiful Soup 4.2.0 # Python version 2.7.3 (default, Aug 1 2012, 05:16:07) # I noticed that html5lib is not installed. Installing it may help. # Found lxml version 2.3.2.0 # # Trying to parse your data with html.parser # Here's what html.parser did with the document: # ... Olhando para o que diagnose() retorna, poderá lhe dizer como resolver o seu problema. Mesmo que não consiga, você poderá colar a saída de ``diagnose()`` quando solicitar ajuda. Erros enquanto se analisa um documento -------------------------------------- Existem dois tipos diferentes de erros de análise. Existem quebras quando você passa para o Beautiful Soup um documento e ele retorna uma exceção, geralmente um ``HTMLParser.HTMLParseError``. E existe o comportamento inesperado, quando uma árvore de análise parece um pouco diferente do documento usado para criá-la. Quase nenhum destes problemas são parte do Beautiful Soup. Não é porque o Beautiful Soup é maravilhosamente um software bem escrito. É porque o Beautiful Soup não inclui nenhum código de análise. Ao invés disso, ele depende de analisadores externos. Se um analisador não funciona com certo documento, a melhor solução é tentar um analisador diferente. Veja :ref:`Instalando um interpretador