1Documentação Beautiful Soup
2============================
3
4.. image:: 6.1.jpg
5   :align: right
6   :alt: "O Lacaio-Peixe começou tirando debaixo do braço uma grande carta, quase tão grande quanto ele mesmo."
7
8
9`Beautiful Soup <http://www.crummy.com/software/BeautifulSoup/>`_ é uma biblioteca
10Python de extração de dados de arquivos HTML e XML. Ela funciona com o seu interpretador (parser) favorito
11a fim de prover maneiras mais intuitivas de navegar, buscar e modificar uma árvore de análise (parse tree).
12Ela geralmente economiza horas ou dias de trabalho de programadores ao redor do mundo.
13
14Estas instruções ilustram as principais funcionalidades do Beautiful Soup 4
15com exemplos. Mostro para o que a biblioteca é indicada, como funciona,
16como se usa e como fazer aquilo que você quer e o que fazer quando ela frustra suas
17expectativas.
18
19Os exemplos nesta documentação devem funcionar da mesma maneira em Python 2.7 e Python 3.2.
20
21`Você pode estar procurando pela documentação do Beautiful Soup 3
22<http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_.
23Se está, informo que o Beautiful Soup 3 não está mais sendo desenvolvido,
24e que o Beautiful Soup 4 é o recomendado para todos os novos projetos.
25Se você quiser saber as diferenças entre as versões 3 e 4, veja `Portabilidade de código para BS4`_.
26
27Esta documentação foi traduzida para outros idiomas pelos usuários do Beautiful Soup:
28
29* `这篇文档当然还有中文版. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/>`_
30* このページは日本語で利用できます(`外部リンク <http://kondou.com/BS4/>`_)
31* `이 문서는 한국어 번역도 가능합니다. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ko/>`_
32* `Эта документация доступна на русском языке. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ru/>`_
33
34Como conseguir ajuda:
35---------------------
36
37Se você tem perguntas sobre o Beautiful Soup ou está com dificuldades,
38`envie uma mensagem para nosso grupo de discussão
39<https://groups.google.com/forum/?fromgroups#!forum/beautifulsoup>`_. Se o seu
40problema envolve a interpretação de um documento HTML, não esqueça de mencionar
41:ref:`o que a função diagnose() diz <diagnose>` sobre seu documento.
42
43Início Rápido
44=============
45
46Este é o HTML que usarei como exemplo ao longo deste documento
47É um trecho de "Alice no País das Maravilhas"::
48
49 html_doc = """
50 <html><head><title>The Dormouse's story</title></head>
51 <body>
52 <p class="title"><b>The Dormouse's story</b></p>
53
54 <p class="story">Once upon a time there were three little sisters; and their names were
55 <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
56 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
57 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
58 and they lived at the bottom of a well.</p>
59
60 <p class="story">...</p>
61 """
62
63Executando o arquivo "three sisters" através do Beautiful Soup, ele nos
64retorna um objeto ``BeautifulSoup``, que apresenta o documento como uma estrutura
65de dados aninhada::
66
67 from bs4 import BeautifulSoup
68 soup = BeautifulSoup(html_doc, 'html.parser')
69
70 print(soup.prettify())
71 # <html>
72 #  <head>
73 #   <title>
74 #    The Dormouse's story
75 #   </title>
76 #  </head>
77 #  <body>
78 #   <p class="title">
79 #    <b>
80 #     The Dormouse's story
81 #    </b>
82 #   </p>
83 #   <p class="story">
84 #    Once upon a time there were three little sisters; and their names were
85 #    <a class="sister" href="http://example.com/elsie" id="link1">
86 #     Elsie
87 #    </a>
88 #    ,
89 #    <a class="sister" href="http://example.com/lacie" id="link2">
90 #     Lacie
91 #    </a>
92 #    and
93 #    <a class="sister" href="http://example.com/tillie" id="link2">
94 #     Tillie
95 #    </a>
96 #    ; and they lived at the bottom of a well.
97 #   </p>
98 #   <p class="story">
99 #    ...
100 #   </p>
101 #  </body>
102 # </html>
103
104Abaixo verificamos algumas maneiras simples de navegar na estrutura::
105
106 soup.title
107 # <title>The Dormouse's story</title>
108
109 soup.title.name
110 # u'title'
111
112 soup.title.string
113 # u'The Dormouse's story'
114
115 soup.title.parent.name
116 # u'head'
117
118 soup.p
119 # <p class="title"><b>The Dormouse's story</b></p>
120
121 soup.p['class']
122 # u'title'
123
124 soup.a
125 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
126
127 soup.find_all('a')
128 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
129 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
130 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
131
132 soup.find(id="link3")
133 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
134
135Uma tarefa comum é extratir todas as URLs encontradas nas tags <a> de uma página::
136
137 for link in soup.find_all('a'):
138     print(link.get('href'))
139 # http://example.com/elsie
140 # http://example.com/lacie
141 # http://example.com/tillie
142
143Outra tarefa comum é extrair todo o texto de uma página::
144
145 print(soup.get_text())
146 # The Dormouse's story
147 #
148 # The Dormouse's story
149 #
150 # Once upon a time there were three little sisters; and their names were
151 # Elsie,
152 # Lacie and
153 # Tillie;
154 # and they lived at the bottom of a well.
155 #
156 # ...
157
158Isso se parece com o que você precisa? Então vá em frente!
159
160Instalando o Beautiful Soup
161===========================
162
163Se você está usando uma versão recente das distribuições Linux Debian ou Ubuntu,
164você pode instalar o Beautiful Soup facilmente utilizando o gerenciador de pacotes
165
166:kbd:`$ apt-get install python-bs4` (for Python 2)
167
168:kbd:`$ apt-get install python3-bs4` (for Python 3)
169
170O Beautiful Soup 4 também está publicado no PyPi. Portanto, se
171você não conseguir instalá-lo através de seu gerenciador de pacotes, você
172pode fazer isso com ``easy_install`` ou ``pip``. O nome do pacote é ``beautifulsoup4``,
173e o mesmo pacote é válido tanto para Python 2 quanto Python 3. Tenha certeza de utilizar
174a versão correta de ``pip`` ou ``easy_install`` para sua versão do Python (estarão
175nomeados como ``pip3`` ou ``easy_install3`` ,respectivamente, se você estiver usando Python 3).
176
177
178:kbd:`$ easy_install beautifulsoup4`
179
180:kbd:`$ pip install beautifulsoup4`
181
182(O pacote ``BeautifulSoup`` provavelmente `não` é o que você quer. Esta
183é a versão anterior, `Beautiful Soup 3`_. Muitos softwares utilizam
184BS3, por isso ele ainda está disponível, mas se você está criando algo novo,
185você deve instalar o ``beautifulsoup4``.)
186
187Se você não possui o ``easy_install`` ou ``pip`` instalados, você pode fazer
188o download através do tarball do arquivo fonte do Beautiful Soup 4
189<http://www.crummy.com/software/BeautifulSoup/download/4.x/>`_ e
190instalar através do ``setup.py``.
191
192:kbd:`$ python setup.py install`
193
194Se tudo isso falhar, a licença do Beautiful Soup lhe permite empacotar
195toda a biblioteca em sua aplicação. Você pode fazer o download do arquivo
196tarball, copiar o diretório ``bs4`` do código-fonte para sua aplicação e
197utilizar o Beautiful Soup sem nenhum processo de instalação.
198
199Eu utilizo Python 2.7 e Python 3.2 para desenvolver o Beautiful Soup,
200mas ele também funcionará com outras versões recentes.
201
202Problemas após a instalação
203---------------------------
204
205O Beautiful Soup é empacotado em Python 2. Quando você o instala utilizando
206Python 3 ele é automaticamente convertido para esta versão. Se você não instalar o pacote, o
207código não será convertido. Também foi relatado versões erradas sendo instaladas em
208máquinas Windows.
209
210Se você receber um ``ImportError`` "No module named HTMLParser", seu problema
211é que você está utilizando o formato de código Python 2 sob Python 3.
212
213Se você receber um ``ImportError`` "No module named html.parser", seu problema
214é que você está utilizando o formato de código Python 3 sob Python 2.
215
216Em ambos os casos, sua melhor opção é remover completamente a
217instalação do Beautiful Soup do seu sistema (incluindo qualquer diretório
218criado quando o tarball foi descompactado) e realizar a instalação novamente.
219
220Se você receber um ``SyntaxError`` "Invalid syntax" na linha
221``ROOT_TAG_NAME = u'[document]'``, você terá que converter o Python 2
222em Python 3. Você pode fazer isso instalando o pacote:
223
224:kbd:`$ python3 setup.py install`
225
226ou manualmente executando o script de conversão ``2to3`` no
227diretório ``bs4``:
228
229:kbd:`$ 2to3-3.2 -w bs4`
230
231.. _parser-installation:
232
233
234Instalando um interpretador (parser)
235------------------------------------
236
237
238O Beautiful Soup não só suporta o parser HTML incluído na biblioteca
239padrão do Python como também inúmeros parsers de terceiros.
240Um deles é o `parser lxml <http://lxml.de/>`_. Dependendo de sua configuração,
241você podera instalar o lxml com algum dos seguintes comandos:
242
243:kbd:`$ apt-get install python-lxml`
244
245:kbd:`$ easy_install lxml`
246
247:kbd:`$ pip install lxml`
248
249Outra alternativa é o parser `html5lib
250<http://code.google.com/p/html5lib/>`_ do Python puro, o qual analisa o HTML
251da mesma maneira que o navegador o faz. Dependendo de sua configuração,
252você podera instalar o html5lib com algum dos seguintes comandos:
253
254:kbd:`$ apt-get install python-html5lib`
255
256:kbd:`$ easy_install html5lib`
257
258:kbd:`$ pip install html5lib`
259
260Esta tabela resume as vantagens e desvantagens de cada parser:-
261
262+----------------------+--------------------------------------------+--------------------------------+--------------------------+
263| Parser               | Uso Padrão                                 | Vantagens                      | Desvantagens             |
264+----------------------+--------------------------------------------+--------------------------------+--------------------------+
265|  html.parser (puro)  | ``BeautifulSoup(markup, "html.parser")``   | * Baterias inclusas            | * Não tão rápido quanto  |
266|                      |                                            | * Velocidade Decente           |   lxml, menos leniente   |
267|                      |                                            | * Leniente (Python 2.7.3       |   que html5lib.          |
268|                      |                                            |   e 3.2.)                      |                          |
269+----------------------+--------------------------------------------+--------------------------------+--------------------------+
270|     HTML (lxml)      | ``BeautifulSoup(markup, "lxml")``          | * Muito rápido                 | * Dependencia externa de |
271|                      |                                            | * Leniente                     |   C                      |
272+----------------------+--------------------------------------------+--------------------------------+--------------------------+
273|      XML (lxml)      | ``BeautifulSoup(markup, "lxml-xml")``      | * Muito rápido                 | * Dependência externa de |
274|                      | ``BeautifulSoup(markup, "xml")``           | * O único parser XML atualmente|   C                      |
275|                      |                                            |   suportado                    |                          |
276+----------------------+--------------------------------------------+--------------------------------+--------------------------+
277|      html5lib        | ``BeautifulSoup(markup, "html5lib")``      | * Extremamente leniente        | * Muito lento            |
278|                      |                                            | * Analisa as páginas da mesma  | * Dependência externa de |
279|                      |                                            |   maneira que o navegador o faz|   Python                 |
280|                      |                                            | * Cria HTML5 válidos           |                          |
281+----------------------+--------------------------------------------+--------------------------------+--------------------------+
282
283Se for possível recomendo que você instale e utilize o lxml pelo desempenho.
284Se você está utilizando o Python 2 anterior a 2.7.3 ou uma versão do Python 3
285anterior a 3.2.2, é `essencial` que você instale o lxml ou o html5lib. O parser
286HTML nativo do Python não é muito bom para versões mais antigas.
287
288Note que se um documento é inválido, diferentes parsers irão gerar
289diferentes árvores BeautifulSoup para isso. Veja
290:ref:`Diferenças entre os interpretadores (parsers) <differences-between-parsers>`
291para detalhes.
292
293
294Criando a "Sopa"
295================
296
297Para analisar um documento, passe-o como argumento dentro de um construtor ``BeautifulSoup``.
298Você pode passar este argumento como uma string ou manipulador da função open()::
299
300 from bs4 import BeautifulSoup
301
302 with open("index.html") as fp:
303     soup = BeautifulSoup(fp)
304
305 soup = BeautifulSoup("<html>data</html>")
306
307Primeiro, o documento é convertido para Unicode e as entidades HTML
308são convertidas para caracteres Unicode::
309
310 BeautifulSoup("Sacr&eacute; bleu!")
311 <html><head></head><body>Sacré bleu!</body></html>
312
313O Beautiful Soup então interpreta o documento usando o melhor parser disponível.
314Ele irá utilizar um parser HTML ao menos que você indique a ele que utilize um
315parser XML. (Veja `Analisando um XML`_.)
316
317Tipos de objetos
318================
319
320O Beautiful Soup transforma um documento HTML complexo em uma complexa árvore de objetos Python.
321Mas você terá apenas que lidar com quatro `tipos` de objetos: ``Tag``, ``NavigableString``, ``BeautifulSoup``,
322e ``Comment``.
323
324.. _Tag:
325
326``Tag``
327-------
328
329Um objeto ``Tag``  corresponde a uma tag XML ou HTML do documento original::
330
331 soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
332 tag = soup.b
333 type(tag)
334 # <class 'bs4.element.Tag'>
335
336As tags possuem muitos atributos e métodos que eu falarei mais sobre em
337`Navegando pela árvore`_ e `Buscando na árvore`_. Por agora, as características
338mais importantes da tag são seu nome e atributos.
339
340Nome
341^^^^
342
343Toda tag possui um nome, acessível através de ``.name``::
344
345 tag.name
346 # u'b'
347
348Se você mudar o nome de uma tag, a alteração será refletida em qualquer HTML gerado pelo
349Beautiful Soup::
350
351 tag.name = "blockquote"
352 tag
353 # <blockquote class="boldest">Extremely bold</blockquote>
354
355Atributos
356^^^^^^^^^^
357Uma tag pode ter inúmeros atributos. A tag ``<b id="boldest">``
358possui um atributo "id" que possui o valor "boldest". Você pode
359acessar um atributo de uma tag tratando-a como um dicionário::
360
361 tag['id']
362 # u'boldest'
363
364Você pode acessar este dicionário diretamente através de ``.attrs``::
365
366 tag.attrs
367 # {u'id': 'boldest'}
368
369Você pode adicionar, remover ou modificar os atributos de uma tag. Novamente, isso pode
370ser feito tratando a tag como um dicionário::
371
372 tag['id'] = 'verybold'
373 tag['another-attribute'] = 1
374 tag
375 # <b another-attribute="1" id="verybold"></b>
376
377 del tag['id']
378 del tag['another-attribute']
379 tag
380 # <b></b>
381
382 tag['id']
383 # KeyError: 'id'
384 print(tag.get('id'))
385 # None
386
387.. _multivalue:
388
389Atributos com múltiplos valores
390&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
391
392O HTML 4 define alguns atributos que podem ter múltiplos valores. O HTML 5
393removeu alguns deles, mas definiu alguns novos. O atributo mais comum
394que pode receber múltiplos valores é o ``class`` (ou seja, a tag pode ter mais de uma classe CSS).
395Outros são ``rel``, ``rev``, ``accept-charset``, ``headers``, e ``accesskey``.
396O Beautiful Soup apresenta o(s) valor(es) de um atributo deste tipo como uma lista::
397
398 css_soup = BeautifulSoup('<p class="body"></p>')
399 css_soup.p['class']
400 # ["body"]
401
402 css_soup = BeautifulSoup('<p class="body strikeout"></p>')
403 css_soup.p['class']
404 # ["body", "strikeout"]
405
406Se um atributo possui mais de um valor, mas não é um atributo
407que aceita múltiplos valores conforme definido por qualquer versão do
408padrão HTML, o Beautiful Soup retornará como um valor único::
409
410 id_soup = BeautifulSoup('<p id="my id"></p>')
411 id_soup.p['id']
412 # 'my id'
413
414Quando a tag é transformada novamente em string, os valores do atributo múltiplo
415são consolidados::
416
417 rel_soup = BeautifulSoup('<p>Back to the <a rel="index">homepage</a></p>')
418 rel_soup.a['rel']
419 # ['index']
420 rel_soup.a['rel'] = ['index', 'contents']
421 print(rel_soup.p)
422 # <p>Back to the <a rel="index contents">homepage</a></p>
423
424Você pode desabilitar esta opção passando ``multi_valued_attributes=None`` como argumento
425dentro do construtor ``BeautifulSoup`` ::
426
427  no_list_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html', multi_valued_attributes=None)
428  no_list_soup.p['class']
429  # u'body strikeout'
430
431Você pode utilizar ```get_attribute_list`` para retornar um valor no formato de lista, seja um atributo de
432múltiplos valores ou não::
433
434  id_soup.p.get_attribute_list('id')
435  # ["my id"]
436
437Se você analisar um documento como XML, nenhum atributo será tratado como de múltiplos valores::
438
439 xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml')
440 xml_soup.p['class']
441 # u'body strikeout'
442
443Novamente, você pode configurar isto usando o argumento ``multi_valued_attributes``::
444
445  class_is_multi= { '*' : 'class'}
446  xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml', multi_valued_attributes=class_is_multi)
447  xml_soup.p['class']
448  # [u'body', u'strikeout']
449
450Você provavelmente não precisará fazer isso, mas se fizer, use os padrões como guia.
451Eles implementam as regras descritas na especificação do HTML::
452
453  from bs4.builder import builder_registry
454  builder_registry.lookup('html').DEFAULT_CDATA_LIST_ATTRIBUTES
455
456
457``NavigableString``
458-------------------
459
460Uma string corresponde a um texto dentro de uma tag.
461O Beautiful Soup usa a classe ``NavigableString`` para armazenar este texto::
462
463 tag.string
464 # u'Extremely bold'
465 type(tag.string)
466 # <class 'bs4.element.NavigableString'>
467
468Uma ``NavigableString`` é como uma string Unicode do Python, exceto
469que ela também suporta algumas características descritas em `Navegando pela árvore`_
470e `Buscando na árvore`_. Você pode converter um
471``NavigableString`` em uma string Unicode utilizando ``unicode()``::
472
473 unicode_string = unicode(tag.string)
474 unicode_string
475 # u'Extremely bold'
476 type(unicode_string)
477 # <type 'unicode'>
478
479Você não pode editar uma string "in place", mas você pode substituir
480uma string por outra usando :ref:`replace_with()`::
481
482 tag.string.replace_with("No longer bold")
483 tag
484 # <blockquote>No longer bold</blockquote>
485
486``NavigableString`` suporta a maior parte das características descritas em
487`Navegando pela árvore`_ e `Buscando na árvore`_, mas não todas elas.
488Em particular, desde que uma string não pode conter de tudo (da maneira que
489uma tag pode conter uma string ou outra tag), as strings não suportam os
490atributos ``.contents`` ou ``.string`` ou o método ``find()``.
491
492Se você quer utilizar uma ``NavigableString`` fora do Beautiful Soup,
493você deve chamar o ``unicode()`` para transformá-la em uma string Unicode Python
494padrão. Se você não fizer isso, sua string irá carregar uma referência de toda sua
495árvore Beautiful Soup, mesmo que você já não esteja mais usando ela, o que é um grande
496desperdício de memória.
497
498``BeautifulSoup``
499-----------------
500
501O objeto ``BeautifulSoup`` em si representa o documento como um todo.
502Para maioria dos propósitos, você pode tratá-lo como um objeto :ref:`Tag`.
503Isso significa que irá suportar a maioria dos métodos descritos em
504`Navegando pela árvore`_ e `Buscando na árvore`_.
505
506Sabendo que o objeto ``BeautifulSoup`` não corresponde a uma tag
507HTML ou XML propriamente dita, ele não tem nome e nem atributos. Mas em alguns
508casos é útil observar seu ``.name``; então, foi dado o especial
509``.name`` "[document]"::
510
511 soup.name
512 # u'[document]'
513
514Comentários e outras strings especiais
515--------------------------------------
516
517``Tag``, ``NavigableString``, e ``BeautifulSoup`` abrangem quase
518tudo o que você encontrará em um arquivo HTML ou XML, mas há alguns
519pontos faltando. O único deles que você provavelmente precisará se preocupar
520é o comentário::
521
522 markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
523 soup = BeautifulSoup(markup)
524 comment = soup.b.string
525 type(comment)
526 # <class 'bs4.element.Comment'>
527
528O objeto ``Comment`` é apenas um tipo especial de ``NavigableString``::
529
530 comment
531 # u'Hey, buddy. Want to buy a used parser'
532
533Mas quando aparece como parte de um documento HTML, um ``Comment`` é
534exibido com uma formatação especial::
535
536 print(soup.b.prettify())
537 # <b>
538 #  <!--Hey, buddy. Want to buy a used parser?-->
539 # </b>
540
541O Beautiful Soup define classes para qualquer outra coisa que possa
542aparecer em um documento XML: ``CData``, ``ProcessingInstruction``,
543``Declaration`` e ``Doctype``. Assim como ``Comment``, estas classes
544são subclasses de ``NavigableString`` que adicionam algo a string.
545Aqui está um exemplo que substitui o comentário por um bloco CDATA::
546
547 from bs4 import CData
548 cdata = CData("A CDATA block")
549 comment.replace_with(cdata)
550
551 print(soup.b.prettify())
552 # <b>
553 #  <![CDATA[A CDATA block]]>
554 # </b>
555
556
557Navegando pela árvore
558=====================
559
560Aqui está o documento HTML "Three sisters" novamente::
561
562 html_doc = """
563 <html><head><title>The Dormouse's story</title></head>
564 <body>
565 <p class="title"><b>The Dormouse's story</b></p>
566
567 <p class="story">Once upon a time there were three little sisters; and their names were
568 <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
569 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
570 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
571 and they lived at the bottom of a well.</p>
572
573 <p class="story">...</p>
574 """
575
576 from bs4 import BeautifulSoup
577 soup = BeautifulSoup(html_doc, 'html.parser')
578
579Eu usarei este documento como exemplo para mostrar como navegar
580de uma parte para outra do documento.
581
582Descendo na Árvore
583------------------
584As tags podem conter strings e outras tags. Estes elementos são as tags
585`filhas` (children). O Beautiful Soup oferece diferentes atributos para
586navegar e iterar sobre as tags filhas.
587
588Note que as strings Beautiful Soup não suportam qualquer destes atributos,
589porque uma string não pode ter filhos.
590
591Navegar usando os nomes das tags
592^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
593A maneira mais simples de navegar pela árvore é utilizar
594o nome da tag que você quer. Se você quer a tag <head>,
595simplesmente use ``soup.head``::
596
597 soup.head
598 # <head><title>The Dormouse's story</title></head>
599
600 soup.title
601 # <title>The Dormouse's story</title>
602
603Você pode usar este truque de novo, e de novo, para focar em certa parte da
604árvore de análise. Este código retorna a primeira tag <b> abaixo da tag <body>::
605
606 soup.body.b
607 # <b>The Dormouse's story</b>
608
609Utilizando o nome da tag como atributo irá lhe retornar apenas a `primeira`
610tag com aquele nome::
611
612 soup.a
613 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
614
615Se você precisar retornar `todas` as tags <a>, ou algo mais complicado
616que a primeira tag com um certo nome, você precisará utilizar um dos
617métodos descritos em `Buscando na árvore`_, como `find_all()`::
618
619 soup.find_all('a')
620 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
621 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
622 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
623
624``.contents`` e ``.children``
625^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
626
627As tags filhas de uma outra tag estão disponíveis em uma lista chamada por ``.contents``::
628
629 head_tag = soup.head
630 head_tag
631 # <head><title>The Dormouse's story</title></head>
632
633 head_tag.contents
634 [<title>The Dormouse's story</title>]
635
636 title_tag = head_tag.contents[0]
637 title_tag
638 # <title>The Dormouse's story</title>
639 title_tag.contents
640 # [u'The Dormouse's story']
641
642O objeto ``BeautifulSoup`` em si possui filhos. Neste caso, a tag
643<html> é a filha do objeto ``BeautifulSoup``.::
644
645 len(soup.contents)
646 # 1
647 soup.contents[0].name
648 # u'html'
649
650Uma string não possui o atributo ``.contents``, porque ela não pode conter
651nada::
652
653 text = title_tag.contents[0]
654 text.contents
655 # AttributeError: 'NavigableString' object has no attribute 'contents'
656
657Ao invés de retorná-las como uma lista, você pode iterar sobre as
658tag's filhas usando o gerador ``.children``::
659
660 for child in title_tag.children:
661     print(child)
662 # The Dormouse's story
663
664``.descendants``
665^^^^^^^^^^^^^^^^
666
667Os atributos ``.contents`` e ``.children`` somente consideram tags que
668são `filhas diretas`. Por instância, a tag <head> tem apenas uma tag filha direta,
669a tag <title>::
670
671 head_tag.contents
672 # [<title>The Dormouse's story</title>]
673
674Mas a tag <title> em si possui uma filha: a string "The Dormouse's story".
675Existe uma percepção de que esta string também é filha da tag <head>.
676O atributo ``.descendants`` permite que você itere sobre `todas`
677as tags filhas, recursivamente: suas filhas diretas, as filhas de suas filhas, e assim por diante::
678
679 for child in head_tag.descendants:
680     print(child)
681 # <title>The Dormouse's story</title>
682 # The Dormouse's story
683
684A tag <head> possui apenas uma filha, mas também possui dois `descentendes`:
685a tag <title> e a filha da tag <title>. O objeto ``BeautifulSoup`` possui apenas
686uma filha direta (a tag <html>), mas ele possui vários descendentes::
687
688 len(list(soup.children))
689 # 1
690 len(list(soup.descendants))
691 # 25
692
693.. _.string:
694
695``.string``
696^^^^^^^^^^^
697
698Se uma tag possui apenas uma filha, e esta filha é uma ``NavigableString``,
699esta filha pode ser disponibilizada através de ``.string``::
700
701 title_tag.string
702 # u'The Dormouse's story'
703
704Se a filha única de uma tag é outra tag e esta tag possui uma
705``.string``, então considera-se que a tag mãe tenha a mesma
706``.string`` como sua filha::
707
708 head_tag.contents
709 # [<title>The Dormouse's story</title>]
710
711 head_tag.string
712 # u'The Dormouse's story'
713
714Se uma tag contém mais de uma coisa, então não fica claro a que
715``.string`` deve se referir, portanto ``.string`` será definida como
716``None``::
717
718 print(soup.html.string)
719 # None
720
721.. _string-generators:
722
723``.strings`` e ``stripped_strings``
724^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
725
726Se existe mais de alguma coisa dentro da tag, você pode continuar
727olhando apenas as strings. Use o gerador ``.strings``::
728
729 for string in soup.strings:
730     print(repr(string))
731 # u"The Dormouse's story"
732 # u'\n\n'
733 # u"The Dormouse's story"
734 # u'\n\n'
735 # u'Once upon a time there were three little sisters; and their names were\n'
736 # u'Elsie'
737 # u',\n'
738 # u'Lacie'
739 # u' and\n'
740 # u'Tillie'
741 # u';\nand they lived at the bottom of a well.'
742 # u'\n\n'
743 # u'...'
744 # u'\n'
745
746Estas strings tendem a ter muitos espaços em branco, os quais você
747pode remover utilizando o gerador ``.stripped_strings`` como alternativa::
748
749 for string in soup.stripped_strings:
750     print(repr(string))
751 # u"The Dormouse's story"
752 # u"The Dormouse's story"
753 # u'Once upon a time there were three little sisters; and their names were'
754 # u'Elsie'
755 # u','
756 # u'Lacie'
757 # u'and'
758 # u'Tillie'
759 # u';\nand they lived at the bottom of a well.'
760 # u'...'
761
762Aqui, strings formadas inteiramente por espaços em branco serão ignoradas,
763e espaços em branco no início e no fim das strings serão removidos.
764
765Subindo na Árvore
766-----------------
767
768Continuando a analogia da árvore como "família", toda tag e toda string possuem
769`tags mães (parents)`: a tag que as contém.
770
771.. _.parent:
772
773``.parent``
774^^^^^^^^^^^
775
776Você pode acessar o elemento mãe com o atributo ``.parent``. No
777exemplo "three sisters", a tag <head> é mãe da tag <title>::
778
779 title_tag = soup.title
780 title_tag
781 # <title>The Dormouse's story</title>
782 title_tag.parent
783 # <head><title>The Dormouse's story</title></head>
784
785A string de title tem uma mãe: a tag <title> que a contém::
786
787 title_tag.string.parent
788 # <title>The Dormouse's story</title>
789
790A tag mãe de todo documento (<html>) é um objeto ``BeautifulSoup`` em si::
791
792 html_tag = soup.html
793 type(html_tag.parent)
794 # <class 'bs4.BeautifulSoup'>
795
796E o ``.parent`` de um objeto ``BeautifulSoup`` é definido como None::
797
798 print(soup.parent)
799 # None
800
801.. _.parents:
802
803``.parents``
804^^^^^^^^^^^^
805Você pode iterar sobre todos os elementos pais com
806``.parents``. Este exemplo usa ``.parents`` para viajar de uma tag <a>
807profunda no documento, para o elemento mais ao topo da árvore do documento::
808
809 link = soup.a
810 link
811 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
812 for parent in link.parents:
813     if parent is None:
814         print(parent)
815     else:
816         print(parent.name)
817 # p
818 # body
819 # html
820 # [document]
821 # None
822
823Navegando para os lados:
824------------------------
825
826Considere um simples documento como este::
827
828 sibling_soup = BeautifulSoup("<a><b>text1</b><c>text2</c></b></a>")
829 print(sibling_soup.prettify())
830 # <html>
831 #  <body>
832 #   <a>
833 #    <b>
834 #     text1
835 #    </b>
836 #    <c>
837 #     text2
838 #    </c>
839 #   </a>
840 #  </body>
841 # </html>
842
843A tag <b> e a tag <c> estão no mesmo nível: ambas são filhas diretas
844da mesma tag. Nós podemos chamá-las irmãs (`siblings`).
845Quando um documento é pretty-printed, irmãs aparecem no mesmo nível de identação.
846Você pode utilizar esta relação nos códigos que você escrever.
847
848``.next_sibling`` e ``.previous_sibling``
849^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
850
851Você pode usar ``.next_sibling`` e ``.previous_sibling`` para navegar
852entre os elementos da página que estão no mesmo nível da árvore::
853
854 sibling_soup.b.next_sibling
855 # <c>text2</c>
856
857 sibling_soup.c.previous_sibling
858 # <b>text1</b>
859
860A tag <b> possui ``.next_sibling``, mas não ``.previous_sibling``,
861porque não há nada antes da tag <b> `no mesmo nível na árvore`.
862Pela mesma razão, a tag <c> possui ``.previous_sibling``
863mas não ``.next_sibling``::
864
865 print(sibling_soup.b.previous_sibling)
866 # None
867 print(sibling_soup.c.next_sibling)
868 # None
869
870As strings "text1" e "text2" `não` são irmãs, porque elas não tem a mesma tag mãe::
871
872 sibling_soup.b.string
873 # u'text1'
874
875 print(sibling_soup.b.string.next_sibling)
876 # None
877
878No mundo real, ``.next_sibling`` ou ``.previous_sibling`` de uma tag
879geralmente são strings contendo espaços em branco. Voltando ao documento
880"three sisters"::
881
882 <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>
883 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
884 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
885
886Você pode pensar que o ``.next_sibling`` da primeira tag <a> será a segunda tag <a>.
887Mas na verdade é uma string: a vírgula e um caracter de nova linha (\n) que separam
888a primeira da segunda tag <a>::
889
890 link = soup.a
891 link
892 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
893
894 link.next_sibling
895 # u',\n'
896
897A segunda tag <a> é, na verdade, a ``.next_sibling`` da vírgula::
898
899 link.next_sibling.next_sibling
900 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
901
902.. _sibling-generators:
903
904``.next_siblings`` e ``.previous_siblings``
905^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
906
907Você pode iterar sobre as tag's filhas com ``.next_siblings``
908ou ``.previous_siblings``::
909
910 for sibling in soup.a.next_siblings:
911     print(repr(sibling))
912 # u',\n'
913 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
914 # u' and\n'
915 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
916 # u'; and they lived at the bottom of a well.'
917 # None
918
919 for sibling in soup.find(id="link3").previous_siblings:
920     print(repr(sibling))
921 # ' and\n'
922 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
923 # u',\n'
924 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
925 # u'Once upon a time there were three little sisters; and their names were\n'
926 # None
927
928Indo e voltando
929----------------
930
931Dê uma olhada no início do documento "three sisters"::
932
933 <html><head><title>The Dormouse's story</title></head>
934 <p class="title"><b>The Dormouse's story</b></p>
935
936Um parser HTML transforma estas strings em uma série de eventos: "abrir
937uma tag <html>", "abrir uma tag <head>", "abrir uma tag <title>",
938"adicionar uma string", "fechar uma tag <title>,
939"abrir uma tag <p>", e daí por diante. O Beautiful Soup oferece ferramentas
940para reconstruir a análise inicial do documento.
941
942.. _element-generators:
943
944``.next_element`` e ``.previous_element``
945^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
946
947O atributo ``.next_element`` de uma string ou tag aponta para
948qualquer coisa que tenha sido interpretado posteriormente.
949Isso deveria ser o mesmo que ``.next_sibling``, mas é
950drasticamente diferente.
951
952Aqui está a tag <a> final no "three sisters". Sua
953``.next_sibling`` é uma string: a conclusão da sentença
954que foi interrompida pelo início da tag <a>.::
955
956 last_a_tag = soup.find("a", id="link3")
957 last_a_tag
958 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
959
960 last_a_tag.next_sibling
961 # '; and they lived at the bottom of a well.'
962
963Mas no ``.next_element`` da tag <a>, o que é analisado imediatamente
964depois da tag <a> `não` é o resto da sentença: é a palavra "Tillie".
965
966 last_a_tag.next_element
967 # u'Tillie'
968
969Isso porque na marcação original, a palavra "Tillie" apareceu
970antes do ponto e virgula. O parser encontrou uma tag <a>, então
971a palavra "Tillie", então fechando a tag </a>, então o ponto e vírgula e o
972resto da sentença. O ponto e vírgula estão no mesmo nível que a tag <a>,
973mas a palavra "Tillie" foi encontrada primeiro.
974
975O atributo ``.previous_element`` é exatamente o oposto de
976``.next_element``. Ele aponta para qualquer elemento que
977seja analisado antes do respectivo::
978
979 last_a_tag.previous_element
980 # u' and\n'
981 last_a_tag.previous_element.next_element
982 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
983
984``.next_elements`` e ``.previous_elements``
985^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
986
987Você deve ter entendido a idéia agora. Você pode usar estes iteradores
988para andar para frente e para atrás no documento quando ele for analisado::
989
990 for element in last_a_tag.next_elements:
991     print(repr(element))
992 # u'Tillie'
993 # u';\nand they lived at the bottom of a well.'
994 # u'\n\n'
995 # <p class="story">...</p>
996 # u'...'
997 # u'\n'
998 # None
999
1000Buscando na árvore
1001==================
1002
1003O Beautiful Soup define vários métodos para buscar na árvore que está sendo analisada,
1004mas eles são todos muito similares. Vou usar a maior parte do tempo para explicar os dois mais
1005populares métodos: ``find()`` e ``find_all()``. Os outros métodos recebem exatamente
1006os mesmos argumentos, portanto, vou cobrí-los apenas brevemente.
1007
1008
1009Mais uma vez, utilizarei o documento "three sisters" como exemplo::
1010
1011 html_doc = """
1012 <html><head><title>The Dormouse's story</title></head>
1013 <body>
1014 <p class="title"><b>The Dormouse's story</b></p>
1015
1016 <p class="story">Once upon a time there were three little sisters; and their names were
1017 <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
1018 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
1019 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
1020 and they lived at the bottom of a well.</p>
1021
1022 <p class="story">...</p>
1023 """
1024
1025 from bs4 import BeautifulSoup
1026 soup = BeautifulSoup(html_doc, 'html.parser')
1027
1028Utilizando em um filtro um argumento como ``find_all()``, você pode
1029"dar um zoom" nas partes do documento que você está interessado.
1030
1031Tipos de filtros
1032----------------
1033
1034Antes de entrar em detalhes sobre o ``find_all()`` e métodos similares,
1035quero mostrar exemplos de diferentes filtros que você pode passar dentro
1036destes métodos. Estes filtros aparecerão de novo e de novo por toda API
1037de pesquisa. Você pode usá-los para realizar filtros baseados nos nomes das tags,
1038nos seus atributos, no texto de uma strings ou em alguma combinação entre eles.
1039
1040.. _uma string:
1041
1042Uma string
1043^^^^^^^^^^
1044
1045O filtro mais simples é uma string. Passando uma string para um método de pesquisa,
1046o Beautiful Soup irá buscar uma correspondência a esta exata string. O seguinte código
1047encontrará todas as tags <b> no documento::
1048
1049 soup.find_all('b')
1050 # [<b>The Dormouse's story</b>]
1051
1052Se você passar uma byte string, o Beautiful Soup assumirá que a string
1053esta codificada como UTF-8. Você pode evitar isso passando ao invés disso
1054uma string Unicode.
1055
1056.. _uma expressão regular:
1057
1058Uma expressão regular (regex)
1059^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1060
1061Se você passar um objeto `regex`, o Beautiful Soup irá
1062realizar um filtro com ela utilizando seu método ``search()``.
1063O código seguinte buscará todas as tags as quais os nomes comecem com
1064a letra "b"; neste caso, a tag <body> e a tag <b>::
1065
1066 import re
1067 for tag in soup.find_all(re.compile("^b")):
1068     print(tag.name)
1069 # body
1070 # b
1071
1072Este código buscará todas as tags cujo nome contenha a letra "t"::
1073
1074 for tag in soup.find_all(re.compile("t")):
1075     print(tag.name)
1076 # html
1077 # title
1078
1079.. _uma lista:
1080
1081Uma lista
1082^^^^^^^^^
1083
1084Se você passar uma lista, o Beautiful Soup irá buscar
1085uma correspondência com qualquer item dessuma lista.
1086O código seguinte buscará todas as tags <a> e todas
1087as tags <b>::
1088
1089 soup.find_all(["a", "b"])
1090 # [<b>The Dormouse's story</b>,
1091 #  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1092 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1093 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1094
1095.. _the value True:
1096
1097``True``
1098^^^^^^^^
1099
1100O valor ``True`` irá corresponder com tudo.
1101O código abaixo encontrará ``todas`` as tags do documento,
1102mas nenhuma das strings::
1103
1104 for tag in soup.find_all(True):
1105     print(tag.name)
1106 # html
1107 # head
1108 # title
1109 # body
1110 # p
1111 # b
1112 # p
1113 # a
1114 # a
1115 # a
1116 # p
1117
1118.. _a function:
1119
1120Uma função
1121^^^^^^^^^^
1122
1123Se nenhuma das opções anteriores funcionar para você, defina uma
1124função que pegará um elemento como seu único argumento. A função
1125deverá retornar ``True`` se o argumento corresponder e ``False``
1126caso contrário.
1127
1128Aqui você tem uma função que irá retornar ``True`` se uma tag definir
1129o atributo `class`, mas não definir o atributo `id`::
1130
1131 def has_class_but_no_id(tag):
1132     return tag.has_attr('class') and not tag.has_attr('id')
1133
1134Passe esta função dentro de ``find_all()`` e você irá retornar todas
1135as tags <p>::
1136
1137 soup.find_all(has_class_but_no_id)
1138 # [<p class="title"><b>The Dormouse's story</b></p>,
1139 #  <p class="story">Once upon a time there were...</p>,
1140 #  <p class="story">...</p>]
1141
1142Esta função irá encontrar apenas as tags <p>. Não irá encontrar as tags <a>,
1143porque elas definem "class e "id" ao mesmo tempo. Ela não encontrará
1144as tags <html> e <title>, porque estas tags não definem um atributo
1145"class".
1146
1147Se você passar uma função para filtrar um atributo específico como
1148``href``, o argumento passado na função será o nome do atributo e
1149não toda a tag. Aqui vemos uma função que encontra todas as tags <a>
1150em que o atributo ``href`` não corresponde a expressão regular passada::
1151
1152 def not_lacie(href):
1153     return href and not re.compile("lacie").search(href)
1154 soup.find_all(href=not_lacie)
1155 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1156 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1157
1158A função pode ser tão complexa quanto você precise que seja.
1159Aqui temos uma função que retorna ``True`` se uma tag esta
1160cercada por objetos string::
1161
1162 from bs4 import NavigableString
1163 def surrounded_by_strings(tag):
1164     return (isinstance(tag.next_element, NavigableString)
1165             and isinstance(tag.previous_element, NavigableString))
1166
1167 for tag in soup.find_all(surrounded_by_strings):
1168     print tag.name
1169 # p
1170 # a
1171 # a
1172 # a
1173 # p
1174
1175Agora nós estamos prontos para olhar os métodos de busca em detalhes.
1176
1177``find_all()``
1178--------------
1179
1180Definição: find_all(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`recursive
1181<recursive>`, :ref:`string <string>`, :ref:`limit <limit>`, :ref:`**kwargs <kwargs>`)
1182
1183O método ``find_all()``  busca entre os decendentes de uma tag e retorna todos os decendentes
1184que correspondem a seus filtros. Dei diversos exemplos em `Tipos de filtros`_,
1185mas aqui estão mais alguns::
1186
1187 soup.find_all("title")
1188 # [<title>The Dormouse's story</title>]
1189
1190 soup.find_all("p", "title")
1191 # [<p class="title"><b>The Dormouse's story</b></p>]
1192
1193 soup.find_all("a")
1194 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1195 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1196 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1197
1198 soup.find_all(id="link2")
1199 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1200
1201 import re
1202 soup.find(string=re.compile("sisters"))
1203 # u'Once upon a time there were three little sisters; and their names were\n'
1204
1205Alguns podem parecer familiares, mas outros são novos.
1206O que significa passar um valor ``string`` ou ``id``? Por que
1207``find_all("p", "title")`` encontra uma tag <p> com a classe CSS "title"?
1208Vamos dar uma olhada nos argumentos de ``find_all()``.
1209
1210.. _name:
1211
1212O argumento ``name``
1213^^^^^^^^^^^^^^^^^^^^
1214
1215Passe um valor para ``name`` e você dirá para o Beautiful Soup
1216considerar apenas as tags com certos nomes. Strings de texto seão ignoradas,
1217assim como os nomes que não corresponderem ao argumento ``name``
1218
1219Este é o uso mais simples::
1220
1221 soup.find_all("title")
1222 # [<title>The Dormouse's story</title>]
1223
1224Lembre-se de `Tipos de filtros`_ que o valor para ``name`` pode ser `uma
1225string`_, `uma expressão regular`_, `uma lista`_, `uma função`_, ou
1226:ref:`o valor True <the value True>`.
1227
1228.. _kwargs:
1229
1230Os argumentos "palavras-chave"
1231^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1232
1233Qualquer argumento que não for reconhecido se tornará um filtro
1234de atributos da tag. Se você passar um valor para um argumento
1235chamado ``id``, o Beautiful Soup irá buscar correspondentes entre
1236todas tags ``id``::
1237
1238 soup.find_all(id='link2')
1239 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1240
1241Se você passar um valor para ``href``, o Beautiful Soup buscar correspondentes
1242em cada tag que possua o atributo ``href``::
1243
1244 soup.find_all(href=re.compile("elsie"))
1245 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1246
1247Você pode filtrar um atributo baseado em `uma string`_,
1248`uma expressão regular`_, `uma lista`_, `uma função`_, ou
1249:ref:`o valor True <the value True>`.
1250
1251Este código encontra todas as tags em que o atributo ``id``
1252possuem um valor, independente de qual valor seja::
1253
1254 soup.find_all(id=True)
1255 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1256 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1257 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1258
1259Você pode filtrar múltiplos atributos de uma vez passando mais de um argumento
1260palavra-chave::
1261
1262 soup.find_all(href=re.compile("elsie"), id='link1')
1263 # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
1264
1265Alguns atributos, como o atributo data-* do HTML5, possuem nomes que não
1266podem ser usados como argumentos palavra-chave:::
1267
1268 data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
1269 data_soup.find_all(data-foo="value")
1270 # SyntaxError: keyword can't be an expression
1271
1272Você pode usar estes atributos para realizar buscas, colocando-os
1273em um dicionário e passando o dicionário em ``find_all()``, como o argumento
1274``attrs``::
1275
1276 data_soup.find_all(attrs={"data-foo": "value"})
1277 # [<div data-foo="value">foo!</div>]
1278
1279Você não pode utilizar um argumento palavra-chave para buscar pelo elemento
1280HTML "name", porque o Beautiful Soup utiliza o argumento ``name`` para
1281conter o nome da própria tag. Ao invés disso, você pode passar o valor para
1282"name" no argumento ``attrs``::
1283
1284 name_soup = BeautifulSoup('<input name="email"/>')
1285 name_soup.find_all(name="email")
1286 # []
1287 name_soup.find_all(attrs={"name": "email"})
1288 # [<input name="email"/>]
1289
1290.. _attrs:
1291
1292Buscando por uma classe CSS
1293^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1294
1295É muito útil buscar por uma tag que tem uma certa classe CSS, mas
1296o nome do atributo CSS, "class", é uma palavra reservada no Python.
1297Utilizar ``class`` como um argumento palavra-chave lhe trará um erro
1298de sintaxe. A partir do Beautiful Soup 4.1.2, você pode buscar por uma
1299classe CSS utilizando o argumento palavra-chave ``class_``::
1300
1301 soup.find_all("a", class_="sister")
1302 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1303 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1304 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1305
1306Assim como qualquer argumento palavra-chave, você pode passar para ``class_``
1307uma string, uma expressão regular (regex), uma função ou ``True``::
1308
1309 soup.find_all(class_=re.compile("itl"))
1310 # [<p class="title"><b>The Dormouse's story</b></p>]
1311
1312 def has_six_characters(css_class):
1313     return css_class is not None and len(css_class) == 6
1314
1315 soup.find_all(class_=has_six_characters)
1316 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1317 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1318 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1319
1320:ref:`Lembre-se <multivalue>` que uma tag pode ter valores múltiplos
1321para seu atributo classe. Quando você buscar por uma tag que tenha
1322uma certa classe CSS, você esta buscando correspodência em `qualquer`
1323de suas classes CSS::
1324
1325 css_soup = BeautifulSoup('<p class="body strikeout"></p>')
1326 css_soup.find_all("p", class_="strikeout")
1327 # [<p class="body strikeout"></p>]
1328
1329 css_soup.find_all("p", class_="body")
1330 # [<p class="body strikeout"></p>]
1331
1332Você pode também buscar por uma string exata como valor de ``class``::
1333
1334 css_soup.find_all("p", class_="body strikeout")
1335 # [<p class="body strikeout"></p>]
1336
1337Mas ao procurar por variações de uma string, isso não irá funcionar::
1338
1339 css_soup.find_all("p", class_="strikeout body")
1340 # []
1341
1342Se voce quiser buscar por tags que correspondem a duas ou mais classes CSS,
1343você deverá utilizar um seletor CSS::
1344
1345 css_soup.select("p.strikeout.body")
1346 # [<p class="body strikeout"></p>]
1347
1348Em versões mais antigas do Beautiful Soup, as quais não possuem o atalho ``class_``
1349você pode utilizar o truque ``attrs`` conforme mencionado acima. Será criado um dicionário
1350do qual o valor para "class" seja uma string ( ou uma expressão regular, ou qualquer
1351outra coisa) que você queira procurar::
1352
1353 soup.find_all("a", attrs={"class": "sister"})
1354 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1355 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1356 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1357
1358.. _string:
1359
1360O argumento ``string``
1361^^^^^^^^^^^^^^^^^^^^^^^
1362
1363Com ``string`` você pode buscar por strings ao invés de tags. Assim como
1364``name`` e os argumentos palavras-chave, você pode passar `uma string`_, `uma
1365expressão regular`_, `uma lista`_, `uma função`_, ou
1366:ref:`o valor True <the value True>`. Aqui estão alguns exemplos::
1367
1368 soup.find_all(string="Elsie")
1369 # [u'Elsie']
1370
1371 soup.find_all(string=["Tillie", "Elsie", "Lacie"])
1372 # [u'Elsie', u'Lacie', u'Tillie']
1373
1374 soup.find_all(string=re.compile("Dormouse"))
1375 [u"The Dormouse's story", u"The Dormouse's story"]
1376
1377 def is_the_only_string_within_a_tag(s):
1378     """Return True if this string is the only child of its parent tag."""
1379     return (s == s.parent.string)
1380
1381 soup.find_all(string=is_the_only_string_within_a_tag)
1382 # [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']
1383
1384Mesmo que ``string`` seja para encontrar strings, você pode combiná-lo com argumentos
1385para encontrar tags: o Beautiful Soup encontrará todas as tags as quais
1386``.string`` corresponder seu valor em ``string``. O código seguinte encontra
1387a tag <a>, a qual a ``.string`` é "Elsie"::
1388
1389 soup.find_all("a", string="Elsie")
1390 # [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
1391
1392O argumento ``string`` é novo no Beautiful Soup 4.4.0. Em versões anteriores
1393ele era chamado de ``text``::
1394
1395 soup.find_all("a", text="Elsie")
1396 # [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
1397
1398.. _limit:
1399
1400O argumento ``limit``
1401^^^^^^^^^^^^^^^^^^^^^^
1402
1403``find_all()`` retorna todas as tags e strings que correspondem aos seus
1404filtros. Isso pode levar algum tmepo se o documento for extenso. Se você
1405não precisar de `todos` os resultados, você pode passar um número limite
1406(``limit``). Ele funciona assim como o parâmetro LIMIT utilizado em SQL.
1407Ele diz ao Beautiful Soup para parar de adquirir resultados assim que atingir
1408um certo número.
1409
1410Existem três links no documento "three sisters", mas este código encontra somente
1411os dois primeiros::
1412
1413 soup.find_all("a", limit=2)
1414 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1415 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1416
1417.. _recursive:
1418
1419O argumento ``recursive``
1420^^^^^^^^^^^^^^^^^^^^^^^^^^
1421
1422Se você chamar ``mytag.find_all()``, o Beautiful Soup irá examinar todos os descendentes
1423de ``mytag``: suas filhas, as filhas de suas filhas e daí em diante. Se você quer apenas que
1424o Beautiful Soup considere filhas diretas, você pode passar o parâmetro ``recursive=False``.
1425Veja a diferença aqui::
1426
1427 soup.html.find_all("title")
1428 # [<title>The Dormouse's story</title>]
1429
1430 soup.html.find_all("title", recursive=False)
1431 # []
1432
1433Aqui está o trecho do documento::
1434
1435 <html>
1436  <head>
1437   <title>
1438    The Dormouse's story
1439   </title>
1440  </head>
1441 ...
1442
1443O tag <title> esta abaixo da tag <html>, mas não está `diretamente`
1444abaixo de <html>: a tag <head> está no caminho entre elas. O Beautiful Soup encontra a tag
1445<title> quando é autorizado a olhar todos os descendentes de <html>, mas
1446quando ``recursive=False`` é restringido o acesso as filhas imediatas de <html>.
1447
1448O Beautiful Soup oferece diversos métodos de busca na árvore (como vimos acima), e a maioria
1449deles recebe os mesmos argumentos que ``find_all()``: ``name``,
1450``attrs``, ``string``, ``limit``, e os argumentos palavras-chave. Mas o
1451argumento ``recursive`` é diferente: ``find_all()`` e ``find()`` são
1452os únicos métodos que o suportam. Passar ``recursive=False`` em um método
1453como ``find_parents()`` não seria muito útil.
1454
1455Chamar uma tag é como chamar ``find_all()``
1456--------------------------------------------
1457
1458Por ``find_all()`` ser o método mais popular na API de busca do
1459Beautiful Soup, você pode usar um atalho para ele. Se você tratar
1460o objeto ``BeautifulSoup`` ou um objeto ``Tag`` como se fosse uma
1461função, então é o mesmo que chamar ``find_all()`` para aquele objeto.
1462Estas duas linhas de código são equivalentes::
1463
1464 soup.find_all("a")
1465 soup("a")
1466
1467Estas duas linhas também são equivalentes::
1468
1469 soup.title.find_all(string=True)
1470 soup.title(string=True)
1471
1472``find()``
1473----------
1474
1475Signature: find(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`recursive
1476<recursive>`, :ref:`string <string>`, :ref:`**kwargs <kwargs>`)
1477
1478O método ``find_all()`` varre todo o documento em busca de resultados,
1479mas algumas vezes você irá querer apenas um resultado. Se você sabe que
1480o documento possui apenas uma tag <body>, é perda de tempo varrer todo o
1481o documento procurando por outras. Ao invés de passar ``limit=1``
1482toda vez em que chamar ``find_all``, você pode usar o método ``find()``.
1483Estas duas linhas de código são `quase` equivalentes::
1484
1485 soup.find_all('title', limit=1)
1486 # [<title>The Dormouse's story</title>]
1487
1488 soup.find('title')
1489 # <title>The Dormouse's story</title>
1490
1491A única diferença é que ``find_all()`` retorna uma lista contendo apenas
1492um resuldado, enquanto ``find()`` retorna o resultado.
1493
1494Se ``find_all()`` não encontrar nada, ele retornará uma lista vazia. Se
1495``find()`` não encontrar nada, ele retornará ``None``::
1496
1497 print(soup.find("nosuchtag"))
1498 # None
1499
1500Lembre-se do truque ``soup.head.title`` de `Navegar usando os nomes das tags`_?
1501Aquele truque funciona chamando repetidamente ``find()``::
1502
1503 soup.head.title
1504 # <title>The Dormouse's story</title>
1505
1506 soup.find("head").find("title")
1507 # <title>The Dormouse's story</title>
1508
1509``find_parents()`` e ``find_parent()``
1510----------------------------------------
1511
1512Signature: find_parents(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`limit <limit>`, :ref:`**kwargs <kwargs>`)
1513
1514Signature: find_parent(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`**kwargs <kwargs>`)
1515
1516Levei muito tempo cobrindo ``find_all()`` e ``find()`` acima.
1517O API do Beautiful Soup define dez outros métodos
1518para buscas na árvore, mas não tenha medo! Cinco destes métodos são
1519basicamente o mesmo que ``find_all()``, e os outros cinco são basicamente
1520o mesmo que ``find()``. A única diferença está em qual parte da árvore
1521eles procuram.
1522
1523Primeiro vamos considerar ``find_parents()`` e
1524``find_parent()``. Lembre-se que ``find_all()`` e ``find()`` trabalham
1525de sua própria maneira descendo através da árvore, procurando pelos
1526descendentes de uma tag. Estes métodos fazem o contrário: eles trabalham
1527`subindo` a árvore, procurando pelas `mães` de uma tag (ou string).
1528Vamos experimentá-los: começando por uma string "enterrada" no documento
1529"three daughters"::
1530
1531  a_string = soup.find(string="Lacie")
1532  a_string
1533  # u'Lacie'
1534
1535  a_string.find_parents("a")
1536  # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1537
1538  a_string.find_parent("p")
1539  # <p class="story">Once upon a time there were three little sisters; and their names were
1540  #  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1541  #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
1542  #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
1543  #  and they lived at the bottom of a well.</p>
1544
1545  a_string.find_parents("p", class="title")
1546  # []
1547
1548Uma das três tags <a> é diretamente um nível superior da string em
1549questão, então nossa busca a encontra. Uma das três tags <p> é uma mãe
1550indireta da string e nossa busca também a encontra. Há uma tag <p> com
1551a classe CSS "title" em algum lugar no documento, mas não é nenhuma das tags mães
1552da string, portanto, não podemos encontrá-la com ``find_parents()``.
1553
1554Você já deve ter feito a conexão entre ``find_parent()`` e
1555``find_parents()``, e os atributos `.parent`_ e `.parents`_ mencionados
1556anteriormente. A conexão é muito forte. Estes métodos de busca utilizam ``.parents``
1557para iterar sobre todos as mãesS e compara cada um com o filtro passado
1558para verificar se preenche o requisito.
1559
1560``find_next_siblings()`` e ``find_next_sibling()``
1561----------------------------------------------------
1562
1563Signature: find_next_siblings(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`limit <limit>`, :ref:`**kwargs <kwargs>`)
1564
1565Signature: find_next_sibling(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`**kwargs <kwargs>`)
1566
1567Estes métodos utilizam :ref:`.next_siblings <sibling-generators>` para
1568iterar sobre o resto dos filhos de um elemento da árvore. O método
1569``find_next_siblings()`` retornará todos os filhos que atendem o
1570requisito ``find_next_sibling()`` retorna apenas o primeiro::
1571
1572 first_link = soup.a
1573 first_link
1574 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1575
1576 first_link.find_next_siblings("a")
1577 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1578 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1579
1580 first_story_paragraph = soup.find("p", "story")
1581 first_story_paragraph.find_next_sibling("p")
1582 # <p class="story">...</p>
1583
1584``find_previous_siblings()`` e ``find_previous_sibling()``
1585------------------------------------------------------------
1586
1587Signature: find_previous_siblings(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`limit <limit>`, :ref:`**kwargs <kwargs>`)
1588
1589Signature: find_previous_sibling(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`**kwargs <kwargs>`)
1590
1591Estes métodos utilizam :ref:`.previous_siblings <sibling-generators>` para iterar sobre os filhos de um elemento que
1592o precede na árvore. O método ``find_previous_siblings()``
1593retorna todos os filhos que atendem o requisito e
1594``find_previous_sibling()`` retorna apenas o primeiro::
1595
1596 last_link = soup.find("a", id="link3")
1597 last_link
1598 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
1599
1600 last_link.find_previous_siblings("a")
1601 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1602 #  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1603
1604 first_story_paragraph = soup.find("p", "story")
1605 first_story_paragraph.find_previous_sibling("p")
1606 # <p class="title"><b>The Dormouse's story</b></p>
1607
1608
1609``find_all_next()`` e ``find_next()``
1610---------------------------------------
1611
1612Signature: find_all_next(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`limit <limit>`, :ref:`**kwargs <kwargs>`)
1613
1614Signature: find_next(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`**kwargs <kwargs>`)
1615
1616Estes métodos utilizam :ref:`.next_elements <element-generators>` para
1617iterar sobre qualquer tag e string que aparecer depois da atual no documento.
1618O método ``find_all_next()`` retorna todos os casos que atendem, e
1619``find_next()`` retorna somente o primeiro caso::
1620
1621 first_link = soup.a
1622 first_link
1623 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1624
1625 first_link.find_all_next(string=True)
1626 # [u'Elsie', u',\n', u'Lacie', u' and\n', u'Tillie',
1627 #  u';\nand they lived at the bottom of a well.', u'\n\n', u'...', u'\n']
1628
1629 first_link.find_next("p")
1630 # <p class="story">...</p>
1631
1632No primeiro exemplo, a string "Elsie" foi encontrada, mesmo estando
1633dentro da tag <a>. No segundo exemplo, a última tag <p> do documento foi
1634encontrada, mesmo que não esteja na mesma parte da árvore que <a> onde começamos.
1635Para estes métodos, o que importa é que um elemento corresponda ao filtro e esteja
1636depois do elemento de início no documento.
1637
1638``find_all_previous()`` e ``find_previous()``
1639-----------------------------------------------
1640
1641Signature: find_all_previous(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`limit <limit>`, :ref:`**kwargs <kwargs>`)
1642
1643Signature: find_previous(:ref:`name <name>`, :ref:`attrs <attrs>`, :ref:`string <string>`, :ref:`**kwargs <kwargs>`)
1644
1645Estes métodos utilizam :ref:`.previous_elements <element-generators>` para
1646iterar sobre  as tags e strings que aparecem antes do elemento indicado no argumento.
1647O método ``find_all_previous()`` retorna todos que correspondem a busca e o método
1648``find_previous()`` apenas a primeira correspondência::
1649
1650 first_link = soup.a
1651 first_link
1652 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1653
1654 first_link.find_all_previous("p")
1655 # [<p class="story">Once upon a time there were three little sisters; ...</p>,
1656 #  <p class="title"><b>The Dormouse's story</b></p>]
1657
1658 first_link.find_previous("title")
1659 # <title>The Dormouse's story</title>
1660
1661Quando se chama ``find_all_previous("p")`` é encontrado não só o
1662primeiro parágrafo do documento (o que possui class="title"), mas também o
1663segundo parágrafo, a tag <p> que contém a tag <a> por onde começamos.
1664Isso não deveria ser tão surpreendente: nós estamos olhando para todas as tags
1665que apareceram anteriormente no documento incluindo aquela onde começamos. Uma
1666tag <p> que contenha uma tag <a> deve aparecer antes da tag <a> que ela contém.
1667
1668Seletores CSS
1669-------------
1670
1671A partir da versão 4.7.0, o Beautiful Soup suporta a maior parte dos seletores CSS4
1672através do projeto `SoupSieve <https://facelessuser.github.io/soupsieve/>`_. Se você
1673instalou o Beautiful Soup através do ``pip``,o SoupSieve foi instalado ao mesmo tempo,
1674portanto você não precisará realizar nenhuma etapa adicional.
1675
1676``BeautifulSoup`` possui um método ``.select()`` o qual utiliza o SoupSieve para
1677executar um seletor CSS selector sobre um documento a ser analisado e retorna todos os
1678elementos correspondentes. ``Tag`` possui um método similar que executa um seletor CSS
1679sobre o conteúdo de uma única tag.
1680
1681(Versões anteriores do Beautiful Soup também possuem o método ``.select()``,
1682 mas somente os seletores CSS mais populares são suportados.
1683
1684A `documentação <https://facelessuser.github.io/soupsieve/>`_ SoupSieve
1685lista todos os seletores suportados atualmente, mas aqui estão alguns dos
1686básicos:
1687
1688Você pode encontrar tags::
1689
1690 soup.select("title")
1691 # [<title>The Dormouse's story</title>]
1692
1693 soup.select("p:nth-of-type(3)")
1694 # [<p class="story">...</p>]
1695
1696Encontrar tags aninhadas com outras::
1697 soup.select("body a")
1698 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1699 #  <a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
1700 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1701
1702 soup.select("html head title")
1703 # [<title>The Dormouse's story</title>]
1704
1705Encontrar tags `diretamente` abaixo de outras tags no aninhamento::
1706
1707 soup.select("head > title")
1708 # [<title>The Dormouse's story</title>]
1709
1710 soup.select("p > a")
1711 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1712 #  <a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
1713 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1714
1715 soup.select("p > a:nth-of-type(2)")
1716 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1717
1718 soup.select("p > #link1")
1719 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1720
1721 soup.select("body > a")
1722 # []
1723
1724Encontrar as irmãs de alguma tag::
1725
1726 soup.select("#link1 ~ .sister")
1727 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1728 #  <a class="sister" href="http://example.com/tillie"  id="link3">Tillie</a>]
1729
1730 soup.select("#link1 + .sister")
1731 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1732
1733Encontrar tags pela classe CSS::
1734
1735 soup.select(".sister")
1736 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1737 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1738 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1739
1740 soup.select("[class~=sister]")
1741 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1742 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1743 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1744
1745Encontrar tags pelo ID::
1746
1747 soup.select("#link1")
1748 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1749
1750 soup.select("a#link2")
1751 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1752
1753Encontrar tags que se relacionam com qualquer seletor em uma lista de seletores::
1754
1755 soup.select("#link1,#link2")
1756 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1757 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1758
1759Testar a existência de um atributo::
1760
1761 soup.select('a[href]')
1762 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1763 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1764 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1765
1766Encontrar tags pelo valor do atributo::
1767
1768 soup.select('a[href="http://example.com/elsie"]')
1769 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1770
1771 soup.select('a[href^="http://example.com/"]')
1772 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1773 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1774 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1775
1776 soup.select('a[href$="tillie"]')
1777 # [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1778
1779 soup.select('a[href*=".com/el"]')
1780 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1781
1782Há outro método chamado ``select_one()``, o qual encontra somente
1783a primeira tag que combina com um seletor::
1784
1785 soup.select_one(".sister")
1786 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1787
1788Se você analisou um XML que define namespaces, você pode
1789utilizar nos seletores CSS::
1790
1791 from bs4 import BeautifulSoup
1792 xml = """<tag xmlns:ns1="http://namespace1/" xmlns:ns2="http://namespace2/">
1793  <ns1:child>I'm in namespace 1</ns1:child>
1794  <ns2:child>I'm in namespace 2</ns2:child>
1795 </tag> """
1796 soup = BeautifulSoup(xml, "xml")
1797
1798 soup.select("child")
1799 # [<ns1:child>I'm in namespace 1</ns1:child>, <ns2:child>I'm in namespace 2</ns2:child>]
1800
1801 soup.select("ns1|child", namespaces=namespaces)
1802 # [<ns1:child>I'm in namespace 1</ns1:child>]
1803
1804Quando manipulando um seletor CSS que utiliza
1805namespaces,o Beautiful Soup utiliza a abreviação do namespace
1806que encontrou quando estava analisando o documento. Você pode evitar isso
1807passando um dicionário com suas próprias abreviações::
1808
1809 namespaces = dict(first="http://namespace1/", second="http://namespace2/")
1810 soup.select("second|child", namespaces=namespaces)
1811 # [<ns1:child>I'm in namespace 2</ns1:child>]
1812
1813Todo este negócio de seletor CSS é conveniente
1814para pessoas que já sabem a sintaxe do seletor CSS.
1815Você pode fazer tudo isso com a API do BeautifulSoup.
1816E se os seletores CSS são tudo o que você precisa,
1817você deveria analisar o documento com lxml: é mais rápido. Mas isso deixa você `combinar`
1818seletores CSS com a API do Beautiful Soup.
1819
1820Modificando a árvore
1821====================
1822
1823O principal poder do Beautiful Soup está na busca pela árvore, mas você
1824pode também modificar a árvore e escrever suas modificações como um novo
1825documento HTML ou XML.
1826
1827Alterando nomes de tags e atributos
1828-----------------------------------
1829
1830Cobri este assunto anteriormente em `Atributos`_, mas vale a pena repetir. Você
1831pode renomear uma tag, alterar o valor de algum de seus atributos, adicionar novos
1832atributos e deletar qualquer um deles::
1833
1834 soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
1835 tag = soup.b
1836
1837 tag.name = "blockquote"
1838 tag['class'] = 'verybold'
1839 tag['id'] = 1
1840 tag
1841 # <blockquote class="verybold" id="1">Extremely bold</blockquote>
1842
1843 del tag['class']
1844 del tag['id']
1845 tag
1846 # <blockquote>Extremely bold</blockquote>
1847
1848Modificando ``.string``
1849-----------------------
1850
1851Se você definir o um atributo ``.string`` de uma tag, o conteúdo da
1852tag será substituido pela string que foi passada::
1853
1854  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
1855  soup = BeautifulSoup(markup)
1856
1857  tag = soup.a
1858  tag.string = "New link text."
1859  tag
1860  # <a href="http://example.com/">New link text.</a>
1861
1862Cuidado: se a tag conter outra(s) tag(s), ela(s) e todo seu conteúdo
1863serão destruídos.
1864
1865``append()``
1866------------
1867
1868Você pode adicionar algo no conteúdo de uma tag com ``Tag.append()``. Funciona
1869da mesma maneira que ``.append()`` de uma lista::
1870
1871   soup = BeautifulSoup("<a>Foo</a>")
1872   soup.a.append("Bar")
1873
1874   soup
1875   # <html><head></head><body><a>FooBar</a></body></html>
1876   soup.a.contents
1877   # [u'Foo', u'Bar']
1878
1879``extend()``
1880------------
1881
1882Com início no Beautiful Soup 4.7.0,  ``Tag`` também suporta um método chamado
1883``.extend()``, o qual funciona da mesma maneira que chamando ``.extend()`` em
1884uma lista::
1885
1886   soup = BeautifulSoup("<a>Soup</a>")
1887   soup.a.extend(["'s", " ", "on"])
1888
1889   soup
1890   # <html><head></head><body><a>Soup's on</a></body></html>
1891   soup.a.contents
1892   # [u'Soup', u''s', u' ', u'on']
1893
1894``NavigableString()`` e ``.new_tag()``
1895-------------------------------------------------
1896
1897Se você precisar adicionar uma string a um documento, sem problema -- você
1898pode passar uma string Python através de ``append()``, ou você pode chamar
1899o construtor ``NavigableString``::
1900
1901   soup = BeautifulSoup("<b></b>")
1902   tag = soup.b
1903   tag.append("Hello")
1904   new_string = NavigableString(" there")
1905   tag.append(new_string)
1906   tag
1907   # <b>Hello there.</b>
1908   tag.contents
1909   # [u'Hello', u' there']
1910
1911Se você quiser criar um comentário ou alguma outra subclasse de
1912``NavigableString``, apenas chame o construtor::
1913
1914   from bs4 import Comment
1915   new_comment = Comment("Nice to see you.")
1916   tag.append(new_comment)
1917   tag
1918   # <b>Hello there<!--Nice to see you.--></b>
1919   tag.contents
1920   # [u'Hello', u' there', u'Nice to see you.']
1921
1922(Esta é uma funcionalidade nova no Beautiful Soup 4.4.0.)
1923
1924E se você precisar criar uma nova tag? A melhor solução
1925é chamar o método ``BeautifulSoup.new_tag()``::
1926
1927   soup = BeautifulSoup("<b></b>")
1928   original_tag = soup.b
1929
1930   new_tag = soup.new_tag("a", href="http://www.example.com")
1931   original_tag.append(new_tag)
1932   original_tag
1933   # <b><a href="http://www.example.com"></a></b>
1934
1935   new_tag.string = "Link text."
1936   original_tag
1937   # <b><a href="http://www.example.com">Link text.</a></b>
1938
1939Somente o primeiro argumento (o nome da tag) é obrigatório.
1940
1941``insert()``
1942------------
1943
1944``Tag.insert()`` funciona assim como ``Tag.append()``, exceto que o novo elemento
1945não será inserido ao final do ``.contents`` de sua tag mãe. Ele será inserido em qualquer posição
1946numérica que você informar. Funciona assim como ``.insert()`` em uma lista::
1947
1948  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
1949  soup = BeautifulSoup(markup)
1950  tag = soup.a
1951
1952  tag.insert(1, "but did not endorse ")
1953  tag
1954  # <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a>
1955  tag.contents
1956  # [u'I linked to ', u'but did not endorse', <i>example.com</i>]
1957
1958``insert_before()`` e ``insert_after()``
1959------------------------------------------
1960
1961O método ``insert_before()`` insere tags ou strings imediatamente antes de algo
1962na árvore::
1963
1964   soup = BeautifulSoup("<b>stop</b>")
1965   tag = soup.new_tag("i")
1966   tag.string = "Don't"
1967   soup.b.string.insert_before(tag)
1968   soup.b
1969   # <b><i>Don't</i>stop</b>
1970
1971O método ``insert_after()`` insere tags ou strings imediatamente após algo
1972na árvore::
1973
1974   div = soup.new_tag('div')
1975   div.string = 'ever'
1976   soup.b.i.insert_after(" you ", div)
1977   soup.b
1978   # <b><i>Don't</i> you <div>ever</div> stop</b>
1979   soup.b.contents
1980   # [<i>Don't</i>, u' you', <div>ever</div>, u'stop']
1981
1982``clear()``
1983-----------
1984
1985O ``Tag.clear()`` remove o conteúdo de uma tag::
1986
1987  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
1988  soup = BeautifulSoup(markup)
1989  tag = soup.a
1990
1991  tag.clear()
1992  tag
1993  # <a href="http://example.com/"></a>
1994
1995``extract()``
1996-------------
1997
1998O ``PageElement.extract()`` remove uma tag ou string da árvore. Ele retorna
1999a tag ou string que foi extraída::
2000
2001  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2002  soup = BeautifulSoup(markup)
2003  a_tag = soup.a
2004
2005  i_tag = soup.i.extract()
2006
2007  a_tag
2008  # <a href="http://example.com/">I linked to</a>
2009
2010  i_tag
2011  # <i>example.com</i>
2012
2013  print(i_tag.parent)
2014  None
2015
2016Neste ponto você efetivamente tem duas árvores de análise: uma baseada no objeto
2017``BeautifulSoup`` que você usou para analisar o documento, e outra baseada na tag que foi
2018extraída. Você pode também chamar ``extract`` em um filho do elemento que você extraiu::
2019
2020  my_string = i_tag.string.extract()
2021  my_string
2022  # u'example.com'
2023
2024  print(my_string.parent)
2025  # None
2026  i_tag
2027  # <i></i>
2028
2029
2030``decompose()``
2031---------------
2032
2033O ``Tag.decompose()`` remove uma tag da árvore, então destrói `completamente` ela
2034e seu conteúdo::
2035
2036  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2037  soup = BeautifulSoup(markup)
2038  a_tag = soup.a
2039
2040  soup.i.decompose()
2041
2042  a_tag
2043  # <a href="http://example.com/">I linked to</a>
2044
2045
2046.. _replace_with():
2047
2048``replace_with()``
2049------------------
2050
2051Um ``PageElement.replace_with()`` remove uma tag ou string da árvore e
2052substitui pela tag ou string que você escolher::
2053
2054  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2055  soup = BeautifulSoup(markup)
2056  a_tag = soup.a
2057
2058  new_tag = soup.new_tag("b")
2059  new_tag.string = "example.net"
2060  a_tag.i.replace_with(new_tag)
2061
2062  a_tag
2063  # <a href="http://example.com/">I linked to <b>example.net</b></a>
2064
2065``replace_with()`` retorna a tag ou string que foi substituída, então você pode
2066examiná-la ou adicioná-la novamente em outra parte da árvore.
2067
2068``wrap()``
2069----------
2070
2071O ``PageElement.wrap()`` envelopa um elemento na tag que você especificar. Ele
2072retornará o novo empacotador::
2073
2074 soup = BeautifulSoup("<p>I wish I was bold.</p>")
2075 soup.p.string.wrap(soup.new_tag("b"))
2076 # <b>I wish I was bold.</b>
2077
2078 soup.p.wrap(soup.new_tag("div")
2079 # <div><p><b>I wish I was bold.</b></p></div>
2080
2081Este método é novo no Beautiful Soup 4.0.5.
2082
2083``unwrap()``
2084---------------------------
2085
2086O ``Tag.unwrap()`` é o oposto de ``wrap()``. Ele substitui uma tag pelo
2087que estiver dentro dela. É uma boa maneira de remover marcações::
2088
2089  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2090  soup = BeautifulSoup(markup)
2091  a_tag = soup.a
2092
2093  a_tag.i.unwrap()
2094  a_tag
2095  # <a href="http://example.com/">I linked to example.com</a>
2096
2097Assim como ``replace_with()``, ``unwrap()`` retorna a tag que foi
2098substituída.
2099
2100``smooth()``
2101---------------------------
2102
2103Após chamar vários métodos que modificam a árvore, você pode acabar com um ou dois objetos ``NavigableString`` próximos um ao outro. O Beautiful Soup não tem nenhum problema com isso, mas como isso não pode acontecer em um documento que acabou de ser analisado, você não deve esperar um comportamento como o seguinte::
2104
2105  soup = BeautifulSoup("<p>A one</p>")
2106  soup.p.append(", a two")
2107
2108  soup.p.contents
2109  # [u'A one', u', a two']
2110
2111  print(soup.p.encode())
2112  # <p>A one, a two</p>
2113
2114  print(soup.p.prettify())
2115  # <p>
2116  #  A one
2117  #  , a two
2118  # </p>
2119
2120Você pode chamar ``Tag.smooth()`` para limpar a árvore analisada, consolidando strings adjacentes::
2121
2122 soup.smooth()
2123
2124 soup.p.contents
2125 # [u'A one, a two']
2126
2127 print(soup.p.prettify())
2128 # <p>
2129 #  A one, a two
2130 # </p>
2131
2132O método ``smooth()`` é novo no Beautiful Soup 4.8.0.
2133
2134Saída
2135======
2136
2137.. _.prettyprinting:
2138
2139Pretty-printing
2140---------------
2141
2142O método ``prettify()`` irá transformar uma árvore do Beautiful Soup em
2143uma string Unicode devidamente formatada, com uma linha para cada tag e cada string::
2144
2145  markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2146  soup = BeautifulSoup(markup)
2147  soup.prettify()
2148  # '<html>\n <head>\n </head>\n <body>\n  <a href="http://example.com/">\n...'
2149
2150  print(soup.prettify())
2151  # <html>
2152  #  <head>
2153  #  </head>
2154  #  <body>
2155  #   <a href="http://example.com/">
2156  #    I linked to
2157  #    <i>
2158  #     example.com
2159  #    </i>
2160  #   </a>
2161  #  </body>
2162  # </html>
2163
2164Você pode chamar ``prettify()`` no top-level do objeto ``BeautifulSoup``,
2165ou em qualquer de seus objetos ``Tag``::
2166
2167  print(soup.a.prettify())
2168  # <a href="http://example.com/">
2169  #  I linked to
2170  #  <i>
2171  #   example.com
2172  #  </i>
2173  # </a>
2174
2175Non-pretty printing
2176-------------------
2177
2178Se você quer apenas uma string, sem nenhuma formatação, você pode chamar
2179``unicode()`` ou ``str()`` para o objeto ``BeautifulSoup`` ou uma ``Tag``
2180dentro dele::
2181
2182 str(soup)
2183 # '<html><head></head><body><a href="http://example.com/">I linked to <i>example.com</i></a></body></html>'
2184
2185 unicode(soup.a)
2186 # u'<a href="http://example.com/">I linked to <i>example.com</i></a>'
2187
2188A função ``str()`` retorna uma string codificada em UTF-8. Veja
2189`Codificação (Encoding)`_ para outras opções.
2190
2191Você também pode chamar ``encode()`` para ter uma bytestring, e ``decode()``
2192para ter Unicode.
2193
2194.. _output_formatters:
2195
2196Output formatters
2197-----------------
2198
2199Se você der para o Beautiful Soup um documento que contém entidades HTML como
2200"&lquot;", elas serão convertidades em caracteres Unicode::
2201
2202 soup = BeautifulSoup("&ldquo;Dammit!&rdquo; he said.")
2203 unicode(soup)
2204 # u'<html><head></head><body>\u201cDammit!\u201d he said.</body></html>'
2205
2206Se você converter o documento em uma string, os caracteres Unicode
2207serão codificados como UTF-8. Você não irá ter suas entidades HTML de volta::
2208
2209 str(soup)
2210 # '<html><head></head><body>\xe2\x80\x9cDammit!\xe2\x80\x9d he said.</body></html>'
2211
2212Por padrão, os únicos caracteres que escapam desta saída são o & e os sinais de <>.
2213Eles são convertidos em "&amp;", "&lt;",
2214e "&gt;", com isso o Beautiful Soup não gera HTML e XML inválidos de maneira inadvertida.
2215
2216 soup = BeautifulSoup("<p>The law firm of Dewey, Cheatem, & Howe</p>")
2217 soup.p
2218 # <p>The law firm of Dewey, Cheatem, &amp; Howe</p>
2219
2220 soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>')
2221 soup.a
2222 # <a href="http://example.com/?foo=val1&amp;bar=val2">A link</a>
2223
2224Você pode alterar este comportamento informando um valor para o argumento de
2225``formatter`` para ``prettify()``, ``encode()``, ou
2226``decode()``. Beautiful Soup reconhece cinco possiveis valores para ``formatter``.
2227
2228O padrão é ``formatter="minimal"``. Strings sempre serão processadas de maneira a garantir que o Beautiful Soup gere HTML/XML válidos::
2229
2230 french = "<p>Il a dit &lt;&lt;Sacr&eacute; bleu!&gt;&gt;</p>"
2231 soup = BeautifulSoup(french)
2232 print(soup.prettify(formatter="minimal"))
2233 # <html>
2234 #  <body>
2235 #   <p>
2236 #    Il a dit &lt;&lt;Sacré bleu!&gt;&gt;
2237 #   </p>
2238 #  </body>
2239 # </html>
2240
2241Se você passar ``formatter="html"``, Beautiful Soup irá converter caracteres
2242Unicode para entidades HTML sempre que possível::
2243
2244 print(soup.prettify(formatter="html"))
2245 # <html>
2246 #  <body>
2247 #   <p>
2248 #    Il a dit &lt;&lt;Sacr&eacute; bleu!&gt;&gt;
2249 #   </p>
2250 #  </body>
2251 # </html>
2252
2253Se você passar um ``formatter="html5"``, é o mesmo que ``formatter="html"``,
2254mas o Beautiful Soup irá omitir a barra de fechamento HTML::
2255
2256 soup = BeautifulSoup("<br>")
2257
2258 print(soup.encode(formatter="html"))
2259 # <html><body><br/></body></html>
2260
2261 print(soup.encode(formatter="html5"))
2262 # <html><body><br></body></html>
2263
2264Se você passar ``formatter=None``, Beautiful Soup não irá modificar
2265as strings na saída. Esta é a opção mais rápida, mas permitirá que o
2266Beautiful Soup gere HTML/XML inválidos, como nestes exemplos::
2267
2268 print(soup.prettify(formatter=None))
2269 # <html>
2270 #  <body>
2271 #   <p>
2272 #    Il a dit <<Sacré bleu!>>
2273 #   </p>
2274 #  </body>
2275 # </html>
2276
2277 link_soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>')
2278 print(link_soup.a.encode(formatter=None))
2279 # <a href="http://example.com/?foo=val1&bar=val2">A link</a>
2280
2281Se você precisar de controles mais sofisticados sobre sua saída,
2282você pode usar a classe ``Formatter`` do Beautiful Soup. Aqui você pode ver um
2283formatter que converte strings para uppercase, quando elas ocorrem em um nó de texto
2284ou em um valor de algum atributo::
2285
2286 from bs4.formatter import HTMLFormatter
2287 def uppercase(str):
2288     return str.upper()
2289 formatter = HTMLFormatter(uppercase)
2290
2291 print(soup.prettify(formatter=formatter))
2292 # <html>
2293 #  <body>
2294 #   <p>
2295 #    IL A DIT <<SACRÉ BLEU!>>
2296 #   </p>
2297 #  </body>
2298 # </html>
2299
2300 print(link_soup.a.prettify(formatter=formatter))
2301 # <a href="HTTP://EXAMPLE.COM/?FOO=VAL1&BAR=VAL2">
2302 #  A LINK
2303 # </a>
2304
2305Dividindo em subclasses ``HTMLFormatter`` ou ``XMLFormatter`` darão a você ainda
2306mais controle sobre a saída. Por exemplo, o Beautiful Soup ordena os atributos em toda
2307tag por padrão::
2308
2309 attr_soup = BeautifulSoup(b'<p z="1" m="2" a="3"></p>')
2310 print(attr_soup.p.encode())
2311 # <p a="3" m="2" z="1"></p>
2312
2313Para desabilitar esta opção, você pode criar uma subclasse do método ``Formatter.attributes()``,
2314o qual controla qual atributo será usado na saída e em que ordem. Esta
2315implementação também filtra o atributido chamado "m" quando ele aparece::
2316
2317 class UnsortedAttributes(HTMLFormatter):
2318     def attributes(self, tag):
2319         for k, v in tag.attrs.items():
2320             if k == 'm':
2321	         continue
2322             yield k, v
2323 print(attr_soup.p.encode(formatter=UnsortedAttributes()))
2324 # <p z="1" a="3"></p>
2325
2326Um último conselho: se você criar um objeto ``CDATA``, o texto dentro deste objeto
2327sempre estará presente `exatamente como aparenta, com nenhuma formatação`.
2328O Beautiful Soup irá chamar sua função de substituição da entidade, apenas
2329no caso de você ter escrito uma função personalizada que conta todas as strings
2330que existem no documento ou algo do tipo, mas ele irá ignorar o valor de retorno::
2331
2332 from bs4.element import CData
2333 soup = BeautifulSoup("<a></a>")
2334 soup.a.string = CData("one < three")
2335 print(soup.a.prettify(formatter="xml"))
2336 # <a>
2337 #  <![CDATA[one < three]]>
2338 # </a>
2339
2340
2341``get_text()``
2342--------------
2343
2344Se você quer apenas o texto contido no documento ou em um par de tags, você
2345pode utilizar o método ``get_text()``. Ele retornará todo texto em um documento
2346ou dentro das tags como uma string Unicode::
2347
2348  markup = '<a href="http://example.com/">\nI linked to <i>example.com</i>\n</a>'
2349  soup = BeautifulSoup(markup)
2350
2351  soup.get_text()
2352  u'\nI linked to example.com\n'
2353  soup.i.get_text()
2354  u'example.com'
2355
2356Você pode especificar uma string a ser usada para unir as partes do texto::
2357
2358 # soup.get_text("|")
2359 u'\nI linked to |example.com|\n'
2360
2361Você pode dizer ao Beautiful Soup para excluir espaços em branco do início
2362e fim de cada parte de texto::
2363
2364 # soup.get_text("|", strip=True)
2365 u'I linked to|example.com'
2366
2367Contudo para isso, você pode querer utilizar o gerador :ref:`.stripped_strings <string-generators>`
2368e processar o texto você mesmo::
2369
2370 [text for text in soup.stripped_strings]
2371 # [u'I linked to', u'example.com']
2372
2373Especificando um interpretador (parser) para uso
2374================================================
2375
2376Se você precisa analisar um pequeno HTML, você pode passá-lo no construtor do
2377``BeautifulSoup`` e será o suficiente. O Beautiful Soup irá escolher um parser
2378para você e irá interpretar o dado. Mas existem alguns argumentos adicionais que você
2379pode passar no construtor para alterar qual parser será usado.
2380
2381O primeiro argumento do construtor ``BeautifulSoup`` é uma string ou uma variável contendo o
2382conteúdo do que você quer analisar. O segundo argumento é `como` você quer interpretar aquele
2383conteúdo.
2384
2385Se você não especificar nada, você irá utilizar o melhor analisador HTML instalado.
2386O Beautiful Soup classifica o lxml's como sendo o melhor, logo em seguida o html5lib,
2387e então o parser nativo do Python. Você pode substituí-lo, especificando de acordo
2388com as seguintes características:
2389
2390* O tipo de marcação que você quer analisar. Atualmente são suportados
2391  "html", "xml", and "html5".
2392* O nome do parser que você quer utilizar. Atualmente são suportadas
2393  as opções "lxml", "html5lib", e "html.parser" (parser nativo do Python).
2394
2395A seção `Instalando um interpretador (parser)` compara os parsers suportados.
2396
2397Se você não tem um parser apropriado instalado, o Beautiful Soup irá
2398ignorar sua solicitação e escolher um diferente. Atualmente, o único parser
2399XML suportado é o lxml. Se você não possui o lxml instalado, pedir um parser
2400XML não trará um e pedir por "lxml" não funcionará também.
2401
2402
2403.. _differences-between-parsers:
2404
2405Diferenças entre os interpretadores (parsers)
2406---------------------------------------------
2407
2408O Beautiful Soup apresenta a mesma interface para diferentes parsers,
2409mas cada um é diferente. Diferentes parsers irão criar diferentes análises da árvore
2410do mesmo documento. As maiores diferenças estão entre os parsers HTML e XML.
2411Aqui está um pequeno documento analisado como HTML::
2412
2413 BeautifulSoup("<a><b /></a>")
2414 # <html><head></head><body><a><b></b></a></body></html>
2415
2416Como uma tag <b /> vazia não é um HTML válido, o analisador a transforma
2417em um par <b></b>.
2418
2419Aqui está o mesmo documento analisado como XML (partindo do princípio
2420que você tenha o lxml instalado). Note que o a tag vazia <b /> é deixada sozinha,
2421e que é dada ao documento uma declaração XML ao invés de ser colocada dentro de uma tag <html>.::
2422
2423 BeautifulSoup("<a><b /></a>", "xml")
2424 # <?xml version="1.0" encoding="utf-8"?>
2425 # <a><b/></a>
2426
2427Há também diferenças entre analisadores HTML. Se você der ao Beautiful
2428Soup um documento HTML perfeitamente formatado, estas diferenças não irão
2429importar. Um analisador será mais rápido que outro, mas todos irão lhe
2430retornar uma estrutura de dados que se parece exatamente como o HTML original.
2431
2432Mas se o documento não estiver perfeitamente formatado, diferentes analisadores
2433irão retornar diferentes resultados. Aqui está um pequeno e inválido documento
2434analisado utilizando o analisador lxml HTML. Note que a tag pendente </p> é
2435simplesmente ignorada::
2436
2437 BeautifulSoup("<a></p>", "lxml")
2438 # <html><body><a></a></body></html>
2439
2440Aqui está o mesmo documento analisado utilizando html5lib::
2441
2442 BeautifulSoup("<a></p>", "html5lib")
2443 # <html><head></head><body><a><p></p></a></body></html>
2444
2445Ao invés de ignorar a tag </p> pendente, o html5lib a equipara a uma tag
2446<p> aberta. Este parser também adiciona uma tag <head> vazia ao documento.
2447
2448Aqui está o mesmo documento analisado com o parser HTML nativo do Python::
2449
2450 BeautifulSoup("<a></p>", "html.parser")
2451 # <a></a>
2452
2453Assim como html5lib, este parser ignora a tag de fechamento </p>.
2454Este parser também não realiza nenhuma tentatida de criar um HTML bem
2455formatado adicionando uma tag <body>. Como lxml, ele nem se importa em
2456adicionar uma tag <html>.
2457
2458Sendo o documento "<a></p>" inválido, nenhuma dessas técnicas é a maneira
2459"correta" de lidar com isso. O html5lib utiliza técnicas que são parte
2460do padrão HTML5, portanto vendo sendo definido como a maneira "mais correta",
2461mas todas as três técnicas são legítimas.
2462
2463Diferenças entre analisadores podem afetar o seu script. Se você está
2464planejando distribuir seu script para outras pessoas, ou rodá-lo em
2465múltiplas máquinas, você deve especificar o analisador no construtor
2466``BeautifulSoup``. Isso irá reduzir as chances de que seus usuários
2467analisem um documento de forma diferente da maneira como você analisou.
2468
2469
2470Codificação (Encoding)
2471======================
2472
2473Todo documento HTML ou XML é escrito em uma codificação (encoding) específica como ASCII
2474ou UTF-8. Mas quando você carrega um documento no BeautifulSoup, você irá descobrir
2475que ele foi convertido para Unicode::
2476
2477 markup = "<h1>Sacr\xc3\xa9 bleu!</h1>"
2478 soup = BeautifulSoup(markup)
2479 soup.h1
2480 # <h1>Sacré bleu!</h1>
2481 soup.h1.string
2482 # u'Sacr\xe9 bleu!'
2483
2484Não é mágica (Seria bem legal que fosse). O BeautifulSoup utiliza uma
2485sub-biblioteca chamada `Unicode, Dammit`_ para detectar a codificação de
2486um documento e convertê-lo para Unicode. A codificação detectada automaticamente está
2487disponível como objeto ``.original_encoding`` atributo do objeto ``BeautifulSoup`` ::
2488
2489 soup.original_encoding
2490 'utf-8'
2491
2492`Unicode, Dammit` acerta na maioria das vezes, mas pode errar em algumas.
2493Outras vezes acerta, porém somente após uma busca byte a byte no documento,
2494o leva muito tempo. Se você souber com antecedência a codificação, você poderá
2495evitar erros ou demora passando-o para o contrutor do ``BeautifulSoup``
2496através de ``from_encoding``.
2497
2498Abaixo você tem um documento escrito em ISO-8859-8. O documento é tão
2499pequeno que o `Unicode, Dammit` não consegue verificar sua codificação
2500e acaba fazendo a identificação como ISO-8859-7::
2501
2502 markup = b"<h1>\xed\xe5\xec\xf9</h1>"
2503 soup = BeautifulSoup(markup)
2504 soup.h1
2505 <h1>νεμω</h1>
2506 soup.original_encoding
2507 'ISO-8859-7'
2508
2509Podemos consertar isso passando a codificação correta com ``from_encoding``::
2510
2511 soup = BeautifulSoup(markup, from_encoding="iso-8859-8")
2512 soup.h1
2513 <h1>םולש</h1>
2514 soup.original_encoding
2515 'iso8859-8'
2516
2517Se você não sabe qual a codificação correta, mas você sabe que o
2518`Unicode, Dammit` está errado, você pode passar as opções excluentes
2519como ``exclude_encodings``::
2520
2521 soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])
2522 soup.h1
2523 <h1>םולש</h1>
2524 soup.original_encoding
2525 'WINDOWS-1255'
2526
2527Windows-1255 não é 100% correto, mas é um superconjunto compatível com
2528ISO-8859-8, portanto é mais próximo do ideal. (``exclude_encodings``
2529é uma opção nova no Beautiful Soup 4.4.0.)
2530
2531Em casos raros (geralmente quando um documento UTF-8 contém texto escrito
2532em uma codificação completamente diferente), a única maneira de ser convertido para
2533Unicode é convertendo alguns caracteres com o caractere especial Unicode
2534"REPLACEMENT CHARACTER" (U+FFFD, �). Se o `Unicode, Dammit` precisar utilizá-lo,
2535ele será armazenado no atributo ``.contains_replacement_characters`` como
2536``True`` no ``UnicodeDammit`` ou objeto ``BeautifulSoup``. Isso deixa você ciente
2537que a representação Unicode não é uma representação exata do original - algum dado
2538foi perdido. Se um documento possui �, mas ``.contains_replacement_characters`` é ``False``,
2539você poderá concluir então que o � já estava ali originalmente e não representa dados
2540perdidos.
2541
2542Codificação de Saída
2543--------------------
2544
2545Quando um documento é gerado pelo Beautiful Soup, ele é gerado como UTF-8,
2546mesmo que o documento não for um UTF-8 de início. Aqui está um documento gerado
2547com codificação Latin-1::
2548
2549 markup = b'''
2550  <html>
2551   <head>
2552    <meta content="text/html; charset=ISO-Latin-1" http-equiv="Content-type" />
2553   </head>
2554   <body>
2555    <p>Sacr\xe9 bleu!</p>
2556   </body>
2557  </html>
2558 '''
2559
2560 soup = BeautifulSoup(markup)
2561 print(soup.prettify())
2562 # <html>
2563 #  <head>
2564 #   <meta content="text/html; charset=utf-8" http-equiv="Content-type" />
2565 #  </head>
2566 #  <body>
2567 #   <p>
2568 #    Sacré bleu!
2569 #   </p>
2570 #  </body>
2571 # </html>
2572
2573Note que a tag <meta> foi reescrita para refletir o fato que o documento
2574é agora um UTF-8.
2575
2576Se você não quiser um UTF-8, você pode passar a codificação desejada como parâmetro de
2577``prettify()``::
2578
2579 print(soup.prettify("latin-1"))
2580 # <html>
2581 #  <head>
2582 #   <meta content="text/html; charset=latin-1" http-equiv="Content-type" />
2583 # ...
2584
2585Você também pode chamar encode() no objeto ``BeautifulSoup``  ou em qualquer elemento
2586do objeto, assim como se faz em uma string Python::
2587
2588 soup.p.encode("latin-1")
2589 # '<p>Sacr\xe9 bleu!</p>'
2590
2591 soup.p.encode("utf-8")
2592 # '<p>Sacr\xc3\xa9 bleu!</p>'
2593
2594Qualquer caractere que não pode ser representado na codificação escolhida
2595irá ser convertida para uma entidade de referência numérica XML. Abaixo você
2596tem um documento que inclui o caractere Unicode SNOWMAN::
2597
2598 markup = u"<b>\N{SNOWMAN}</b>"
2599 snowman_soup = BeautifulSoup(markup)
2600 tag = snowman_soup.b
2601
2602O caractere SNOWMAN faz parte da documentação UTF-8 (algo como
2603☃), mas não possui representação para este caractere em ISO-latin-1 ou
2604ASCII, portanto ele é convertido para "&#9731" para as essas codificações::
2605
2606 print(tag.encode("utf-8"))
2607 # <b>☃</b>
2608
2609 print tag.encode("latin-1")
2610 # <b>&#9731;</b>
2611
2612 print tag.encode("ascii")
2613 # <b>&#9731;</b>
2614
2615Unicode, Dammit
2616---------------
2617
2618Você pode usar o `Unicode, Dammit` fora do Beautiful Soup. É útil
2619quando você possui dados em uma codificação desconhecida e quer
2620simplesmente convertê-la para Unicode::
2621
2622 from bs4 import UnicodeDammit
2623 dammit = UnicodeDammit("Sacr\xc3\xa9 bleu!")
2624 print(dammit.unicode_markup)
2625 # Sacré bleu!
2626 dammit.original_encoding
2627 # 'utf-8'
2628
2629
2630As respostas do `Unicode, Dammit` serão um pouco mais precisas se você
2631instalar as bibliotecas ``chardet`` ou ``cchardet``. Quanto maior a quantidade
2632de dados no arquivo que você passar para o `Unicode, Dammit`, mais precisas serão
2633as conversões. Se você possui suas suspeitas sobre qual a codificação original,
2634você pode passar as opções em uma lista::
2635
2636 dammit = UnicodeDammit("Sacr\xe9 bleu!", ["latin-1", "iso-8859-1"])
2637 print(dammit.unicode_markup)
2638 # Sacré bleu!
2639 dammit.original_encoding
2640 # 'latin-1'
2641
2642`Unicode, Dammit` possui duas características que o Beautiful Soup não utiliza.
2643
2644Smart quotes
2645^^^^^^^^^^^^
2646
2647Você pode utilizar `Unicode, Dammit` para converter Microsoft smart quotes para
2648entidades HTML ou XML::
2649
2650 markup = b"<p>I just \x93love\x94 Microsoft Word\x92s smart quotes</p>"
2651
2652 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="html").unicode_markup
2653 # u'<p>I just &ldquo;love&rdquo; Microsoft Word&rsquo;s smart quotes</p>'
2654
2655 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="xml").unicode_markup
2656 # u'<p>I just &#x201C;love&#x201D; Microsoft Word&#x2019;s smart quotes</p>'
2657
2658Você também pode converter Microsoft smart quotes para ASCII::
2659
2660 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="ascii").unicode_markup
2661 # u'<p>I just "love" Microsoft Word\'s smart quotes</p>'
2662
2663Espero que você ache estas características úteis, mas o Beautiful Soup não
2664as usa.O Beautiful Soup dá preferência ao comportamento padrão, que é
2665converter para caracteres Unicode::
2666
2667 UnicodeDammit(markup, ["windows-1252"]).unicode_markup
2668 # u'<p>I just \u201clove\u201d Microsoft Word\u2019s smart quotes</p>'
2669
2670Codificação Inconsistente
2671^^^^^^^^^^^^^^^^^^^^^^^^^
2672
2673Algumas vezes um documento é em sua maioria UTF-8, mas contém  caracteres
2674Windows-1252 assim como (de novo) Microsoft smart quotes. Isso pode acontecer
2675quando um website compostos de dados de muitas fontes diferentes. Você pode
2676utilizar ``UnicodeDammit.detwingle()`` para transformar este documento em um
2677UTF-8 puro. Aqui está um exemplo::
2678
2679 snowmen = (u"\N{SNOWMAN}" * 3)
2680 quote = (u"\N{LEFT DOUBLE QUOTATION MARK}I like snowmen!\N{RIGHT DOUBLE QUOTATION MARK}")
2681 doc = snowmen.encode("utf8") + quote.encode("windows_1252")
2682
2683Este documento é uma bagunça. O snowmen é um UTF-8 e as aspas são Windows-1252.
2684Você pode exibir o snowmen ou as aspas, mas não os dois ao mesmo tempo::
2685
2686 print(doc)
2687 # ☃☃☃�I like snowmen!�
2688
2689 print(doc.decode("windows-1252"))
2690 # ☃☃☃“I like snowmen!”
2691
2692Decodificar um documento como UTF-8 gera um ``UnicodeDecodeError``, e
2693como um Windows-1252 lhe tras algo sem sentido. Felizmente,
2694``UnicodeDammit.detwingle()`` irá converter a string para UTF-8 puro,
2695permitindo a você decodificá-la para Unicode e exibir o snowmen e as
2696aspas simultaneamente::
2697
2698 new_doc = UnicodeDammit.detwingle(doc)
2699 print(new_doc.decode("utf8"))
2700 # ☃☃☃“I like snowmen!”
2701
2702``UnicodeDammit.detwingle()`` sabe apenas como trabalhar com Windows-1252
2703contido em UTF-8 (ou vice versa, eu suponho), mas este é o caso mais comum.
2704
2705Note que você deve chamar ``UnicodeDammit.detwingle()`` em seu dado
2706antes de passá-lo para ``BeautifulSoup`` ou para o construtor ``UnicodeDammit``.
2707O Beautiful Soup assume que um documento possui apenas uma codificação,
2708independente de qual ela seja. Se você passar um documento que
2709contém ambos UTF-8 e Windows-1252, é provável que ele pense que todo
2710o documento seja Windows-1252, e o documento parecerá ``☃☃☃“I like snowmen!”``.
2711
2712``UnicodeDammit.detwingle()`` é novo no Beautiful Soup 4.1.0.
2713
2714Linhas numeradas
2715================
2716
2717Os interpretadores ``html.parser` e ``html5lib`` podem rastrear onde, no
2718documento original, cada tag foi encontrada. Você pode acessar esta
2719informação através de ``Tag.sourceline`` (número da linha) e ``Tag.sourcepos``
2720(posição do início da tag na linha)::
2721
2722   markup = "<p\n>Paragraph 1</p>\n    <p>Paragraph 2</p>"
2723   soup = BeautifulSoup(markup, 'html.parser')
2724   for tag in soup.find_all('p'):
2725       print(tag.sourceline, tag.sourcepos, tag.string)
2726   # (1, 0, u'Paragraph 1')
2727   # (2, 3, u'Paragraph 2')
2728
2729Note que os dois interpretadores significam coisas levemente diferentes por
2730``sourceline`` e ``sourcepos``. Para html.parser, estes números representam
2731a posição do sinal `menor que`inicial. Para html5lib, representa a posição
2732do sinal `maior que` final::
2733
2734   soup = BeautifulSoup(markup, 'html5lib')
2735   for tag in soup.find_all('p'):
2736       print(tag.sourceline, tag.sourcepos, tag.string)
2737   # (2, 1, u'Paragraph 1')
2738   # (3, 7, u'Paragraph 2')
2739
2740Você pode desabilitar esta característica passando ``store_line_numbers=False`
2741no construtor ``BeautifulSoup``::
2742
2743   markup = "<p\n>Paragraph 1</p>\n    <p>Paragraph 2</p>"
2744   soup = BeautifulSoup(markup, 'html.parser', store_line_numbers=False)
2745   soup.p.sourceline
2746   # None
2747
2748Esta característica é nova no 4.8.1 e os analisadores baseados no lxml
2749não a suportam.
2750
2751Comparando objetos por igualdade
2752================================
2753
2754O Beautiful Soup diz que dois objetos ``NavigableString`` ou ``Tag`` são
2755iguais quando eles apresentam as mesma marcação HTML ou XML. No exemplo
2756abaixo, as duas tags <b> são tratadas como iguais, mesmo estando em partes
2757diferentes da árvore do objeto, porque ambas estão como "<b>pizza</b>"::
2758
2759 markup = "<p>I want <b>pizza</b> and more <b>pizza</b>!</p>"
2760 soup = BeautifulSoup(markup, 'html.parser')
2761 first_b, second_b = soup.find_all('b')
2762 print first_b == second_b
2763 # True
2764
2765 print first_b.previous_element == second_b.previous_element
2766 # False
2767
2768Se você quiser verificar se duas variáveis se referem exatamente ao
2769mesmo objeto, use `is`::
2770
2771 print first_b is second_b
2772 # False
2773
2774Copiando objetos Beautiful Soup
2775===============================
2776
2777Você pode utilizar ``copy.copy()`` para criar uma cópia de qualquer ``Tag`` ou
2778``NavigableString``::
2779
2780 import copy
2781 p_copy = copy.copy(soup.p)
2782 print p_copy
2783 # <p>I want <b>pizza</b> and more <b>pizza</b>!</p>
2784
2785
2786A cópia será considerada igual ao original, desde que ela apresente a mesma
2787marcação que o original, mas não será o mesmo objeto::
2788
2789 print soup.p == p_copy
2790 # True
2791
2792 print soup.p is p_copy
2793 # False
2794
2795A única diferença real é que a cópia é completamente separada da árvore
2796original do Beautiful Soup, como se ``extract()`` fosse chamado para ela::
2797
2798 print p_copy.parent
2799 # None
2800
2801Isso acontece porque dois objetos ``Tag`` diferentes não podem ocupar o mesmo
2802espaço ao mesmo tempo.
2803
2804
2805Analisando apenas parte de um documento
2806=======================================
2807
2808Suponhamos que você queira que o Beautiful Soup olhe apenas para as
2809tags <a> de um documento. É um desperdício de tempo e memória analisar
2810todo o documento e, posteriormente, analisar novamente apenas para buscar
2811as tags <a>. Seria muito mais rápido ignorar tudo o que não for <a> em
2812primeiro lugar. A classe ``SoupStrainer`` permite que você escolha
2813qual partes do documento serão analisadas. Você deverá penas criar uma
2814instância de ``SoupStrainer`` e passá-la ao construtor ``BeautifulSoup``
2815no argumento ``parse_only``.
2816
2817(Note que *esta característica não funcionará se você estiver utilizando
2818o html5lib*. Se você utilizar o html5lib, todo o documento será analisado.
2819Isso acontece porque html5lib constantemente reorganiza a árvore de análise
2820e se alguma parte do documento realmente não fizer parte dela, ela irá quebrar.
2821Para evitar confusão, no exemplo abaixo, forçarei o Beautiful Soup a usar o
2822analisador nativo do Python).
2823
2824``SoupStrainer``
2825----------------
2826
2827A classe ``SoupStrainer`` recebe os mesmos argumentos que qualquer método em `Buscando na árvore`_: :ref:`name <name>`, :ref:`attrs
2828<attrs>`, :ref:`string <string>`, e :ref:`**kwargs <kwargs>`. Aqui temos três objetos ``SoupStrainer`` ::
2829
2830 from bs4 import SoupStrainer
2831
2832 only_a_tags = SoupStrainer("a")
2833
2834 only_tags_with_id_link2 = SoupStrainer(id="link2")
2835
2836 def is_short_string(string):
2837     return len(string) < 10
2838
2839 only_short_strings = SoupStrainer(string=is_short_string)
2840
2841Irei trazer de volta o documento "three sisters" mais uma vez e veremos
2842como o documento se parece quando é analisado com estes três objetos ``SoupStrainer``
2843diferentes::
2844
2845 html_doc = """
2846 <html><head><title>The Dormouse's story</title></head>
2847 <body>
2848 <p class="title"><b>The Dormouse's story</b></p>
2849
2850 <p class="story">Once upon a time there were three little sisters; and their names were
2851 <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
2852 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
2853 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
2854 and they lived at the bottom of a well.</p>
2855
2856 <p class="story">...</p>
2857 """
2858
2859 print(BeautifulSoup(html_doc, "html.parser", parse_only=only_a_tags).prettify())
2860 # <a class="sister" href="http://example.com/elsie" id="link1">
2861 #  Elsie
2862 # </a>
2863 # <a class="sister" href="http://example.com/lacie" id="link2">
2864 #  Lacie
2865 # </a>
2866 # <a class="sister" href="http://example.com/tillie" id="link3">
2867 #  Tillie
2868 # </a>
2869
2870 print(BeautifulSoup(html_doc, "html.parser", parse_only=only_tags_with_id_link2).prettify())
2871 # <a class="sister" href="http://example.com/lacie" id="link2">
2872 #  Lacie
2873 # </a>
2874
2875 print(BeautifulSoup(html_doc, "html.parser", parse_only=only_short_strings).prettify())
2876 # Elsie
2877 # ,
2878 # Lacie
2879 # and
2880 # Tillie
2881 # ...
2882 #
2883
2884Você pode também passar um ``SoupStrainer`` em qualquer método coberto em `Buscando na árvore`_.
2885Este uso provavelmente não seja muito útil, mas pensei que deveria mencioná-lo::
2886
2887 soup = BeautifulSoup(html_doc)
2888 soup.find_all(only_short_strings)
2889 # [u'\n\n', u'\n\n', u'Elsie', u',\n', u'Lacie', u' and\n', u'Tillie',
2890 #  u'\n\n', u'...', u'\n']
2891
2892Solucionando Problemas
2893======================
2894
2895.. _diagnose:
2896
2897``diagnose()``
2898--------------
2899
2900Se você está tendo problemas em entender o que o Beautiful Soup está
2901fazendo com um documento, passe o documento pela função ``diagnose()``. (Nova no Beautiful Soup 4.2.0.)
2902O Beautiful Soup irá retornar um relatório mostrando como diferentes parsers
2903lidam com o documento e irá lhe dizer o Beautiful Soup poderia estar utilizando outro parser::
2904
2905 from bs4.diagnose import diagnose
2906 with open("bad.html") as fp:
2907     data = fp.read()
2908 diagnose(data)
2909
2910 # Diagnostic running on Beautiful Soup 4.2.0
2911 # Python version 2.7.3 (default, Aug  1 2012, 05:16:07)
2912 # I noticed that html5lib is not installed. Installing it may help.
2913 # Found lxml version 2.3.2.0
2914 #
2915 # Trying to parse your data with html.parser
2916 # Here's what html.parser did with the document:
2917 # ...
2918
2919Olhando para o que diagnose() retorna, poderá lhe dizer como resolver
2920o seu problema. Mesmo que não consiga, você poderá colar a saída de ``diagnose()``
2921quando solicitar ajuda.
2922
2923Erros enquanto se analisa um documento
2924--------------------------------------
2925
2926Existem dois tipos diferentes de erros de análise. Existem quebras
2927quando você passa para o Beautiful Soup um documento e ele retorna uma
2928exceção, geralmente um ``HTMLParser.HTMLParseError``. E existe o comportamento
2929inesperado, quando uma árvore de análise parece um pouco diferente do
2930documento usado para criá-la.
2931
2932Quase nenhum destes problemas são parte do Beautiful Soup. Não é
2933porque o Beautiful Soup é maravilhosamente um software bem escrito. É
2934porque o Beautiful Soup não inclui nenhum código de análise. Ao invés disso,
2935ele depende de analisadores externos. Se um analisador não funciona com
2936certo documento, a melhor solução é tentar um analisador diferente. Veja
2937:ref:`Instalando um interpretador <parser-installation>` para detalhes e uma comparação entre eles.
2938
2939Os erros de interpretação mais comuns são ``HTMLParser.HTMLParseError:
2940malformed start tag`` e ``HTMLParser.HTMLParseError: bad end
2941tag``. Existem dois parsers gerados para o parser built in do Python
2942e a solução é  :ref:`install lxml ou html5lib. <parser-installation>`
2943
2944Os tipos de erros de comportamento inesperado mais comuns acontecem
2945quando não é encontrada a tag buscada no documento. Você vê a busca
2946sendo executada, mas ``find_all()`` retorna ``[]`` ou ``find()`` retorna ``None``.
2947Este é um problema comum com o analisador HTML nativo do Python que algumas
2948vezes pula tags que ele não entende. Novamente, a solução é
2949:ref:`instalar o lxml ou html5lib.<parser-installation>`
2950
2951Problemas de incompatibilidade de versões
2952-----------------------------------------
2953
2954* ``SyntaxError: Invalid syntax`` (on the line ``ROOT_TAG_NAME =
2955  u'[document]'``): Causado por rodar a versão Python 2 do
2956  Beautiful Soup no Python 3, sem converter o código.
2957
2958* ``ImportError: No module named HTMLParser`` - Causado por rodar a
2959  versão Python 2  do Beautiful Soup no Python 3.
2960
2961* ``ImportError: No module named html.parser`` - Causado por rodar a
2962  versão Python 3 do Beautiful Soup no Python 2.
2963
2964* ``ImportError: No module named BeautifulSoup`` - Causado por rodar
2965  código do Beautiful Soup 3 em um sistema que não possui o BS3
2966  instalado. Ou por escrever código Beautiful Soup 4 sem saber que
2967  o nome do pacote é diferente no ``bs4``.
2968
2969* ``ImportError: No module named bs4`` - Causado por rodar código Beautiful
2970  Soup 4 em um sistema que não possui o BS4 instalado.
2971
2972.. _parsing-xml:
2973
2974Analisando um XML
2975-----------------
2976
2977Por padrão, o Beautiful Soup analisa documento como HTML. Para analisar um documento
2978como XML, passe "xml" como um segundo argumento ao construtor ``BeautifulSoup`` ::
2979
2980 soup = BeautifulSoup(markup, "xml")
2981
2982Você precisará ter :ref:` lxml instalado <parser-installation>`.
2983
2984Outros problemas com analisadores
2985---------------------------------
2986
2987* Se seu script funciona em um computador, mas não em outro,
2988  ou em um ambiente virtual mas não em outro, ou fora do ambiente
2989  virtual mas não dentro dele, provavelmente porque ambos os ambientes
2990  possuem bibliotecas de analisadores difererentes. Por exemplo, você pode
2991  ter desenvolvido um script em um computador que possui lxml instalado,
2992  e então estar tentando rodá-lo no seu computador que possui apenas html5lib
2993  instalado. Veja :ref:`Diferenças entre os interpretadores (parsers) <differences-between-parsers>` para entender porque isso importa,
2994  e corrija o problema mencionando uma biblioteca específica no construtor ``BeautifulSoup``.
2995
2996* Por tags `HTML e atributos serem case-insensitive
2997  <http://www.w3.org/TR/html5/syntax.html#syntax>`_, todos os três
2998  parsers HTML convertem tags e atributos para lowercase. Isso é,
2999  a marcação <TAG></TAG> é convertida para <tag></tag>. Se você quiser
3000  preservar a formatação anterior das tags e atributos, você precisará
3001  :ref:`analisar o documento como XML. <parsing-xml>`
3002
3003.. _misc:
3004
3005Diversos
3006--------
3007
3008* ``UnicodeEncodeError: 'charmap' codec can't encode character
3009  u'\xfoo' in position bar`` (ou qualquer outro
3010  ``UnicodeEncodeError``) - Este não é um problema do Beautiful Soup.
3011  Este problema poderá surgir em duas situações: a primeira quando você
3012  tentar imprimir um caractere Unicode que seu console não sabe como
3013  exibir. (Veja `Esta página na wiki do Python
3014  <http://wiki.python.org/moin/PrintFails>`_ para saber mais.). A segunda,
3015  quando você está gravando um arquivo e passa um caractere Unicode que
3016  não é suportado pelo seu codificador padrão. Neste caso, a solução mais
3017  simples é explicitamente converter a string Unicode em UTF-8 com
3018  ``u.encode("utf8")``.
3019
3020* ``KeyError: [attr]`` - Caused by accessing ``tag['attr']`` quando a
3021  tag em questão não define o atributo ``attr``. Os erros mais comuns são
3022  ``KeyError: 'href'`` e ``KeyError:
3023  'class'``. Use ``tag.get('attr')`` se você não tem certeza se ``attr`` está
3024  definido, assim como você faria em um dicionário Python.
3025
3026* ``AttributeError: 'ResultSet' object has no attribute 'foo'`` - Isso
3027  geralmente ocorre quando você espera que ``find_all()`` retorne
3028  uma única tag ou string. Mas ``find_all()`` retorn uma _lista_ de tags
3029  e strings--um objeto ``ResultSet``. Você precisa iterar sobre a lista e
3030  buscar ``.foo`` para cada um. Ou, se você realmente quiser apenas um resultado,
3031  deverá usar ``find()`` ao invés de ``find_all()``.
3032
3033* ``AttributeError: 'NoneType' object has no attribute 'foo'`` - Isso
3034  geralmente acontece quando é chamado ``find()`` e então se tenta acessar
3035  o atributo `.foo`` o resultado. Mas no seu caso, ``find()`` não encontra nada,
3036  então retorna ``None`` ao invés de retornar uma tag ou uma string. Você precisa
3037  descobrir porque ``find()`` não está retornando nada.
3038
3039Melhorando a performance
3040------------------------
3041
3042O Beautiful Soup nunca será tão rápido quanto os parsers em que
3043ele foi construido em cima. Se o tempo de resposta se tornar crítico,
3044se você estiver pagando por hora de uso de um computador ou se há
3045qualquer outra razão para que o tempo de processamento seja mais
3046valioso que o tempo de programação, você deve esquecer o Beautiful Soup
3047e trabalhar diretamente em cima do `lxml <http://lxml.de/>`_.
3048
3049Dito isso, existem algumas coisas que você pode fazer para acelerar o
3050Beautiful Soup. Se você não está utilizando o lxml como seu parser,
3051meu conselho é que o faça :ref:`start <parser-installation>`.
3052O Beautiful Soup analisa documentos significativamente mais rápido
3053utilizando o lxml do que usando o html.parser ou html5lib.
3054
3055Você pode acelerar a detecção da codificação significativamente instalando
3056a biblioteca `cchardet <http://pypi.python.org/pypi/cchardet/>`_ .
3057
3058`Analisando apenas parte de um documento`_ não irá lhe poupar muito tempo de
3059análise, mas irá poupar muita memória e fará a `busca` no documento muito
3060mais rápida.
3061
3062Beautiful Soup 3
3063================
3064
3065O Beautiful Soup 3 é a versão anterior e não é mais desenvolvida
3066ativamente. Ela atualmente faz parte da maioria das distribuições
3067Linux:
3068
3069:kbd:`$ apt-get install python-beautifulsoup`
3070
3071Também está publicada no PyPi como ``BeautifulSoup``.:
3072
3073:kbd:`$ easy_install BeautifulSoup`
3074
3075:kbd:`$ pip install BeautifulSoup`
3076
3077Você também pode fazer o `download de um tarball do Beautiful Soup 3.2.0
3078<http://www.crummy.com/software/BeautifulSoup/bs3/download/3.x/BeautifulSoup-3.2.0.tar.gz>`_.
3079
3080Se você rodar ``easy_install beautifulsoup`` ou ``easy_install
3081BeautifulSoup``, mas seu código não funcionar, você instalou o Beautiful
3082Soup 3 por engano. Você precisa executar ``easy_install beautifulsoup4``.
3083
3084`A documentação do Beautiful Soup 3 está arquivada online
3085<http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_.
3086
3087Portabilidade de código para BS4
3088--------------------------------
3089
3090A maioria dos códigos escritos em Beautiful Soup 3 irá funcionar no
3091Beautiful Soup 4 com uma pequena alteração. Tudo que você precisa
3092fazer é alterar o nome do pacote de ``BeautifulSoup`` para ``bs4``. Então::
3093
3094  from BeautifulSoup import BeautifulSoup
3095
3096deverá ser assim::
3097
3098  from bs4 import BeautifulSoup
3099
3100* Se for gerado um ``ImportError`` "No module named BeautifulSoup", o
3101  problema é que você está tentando executar um código Beautiful Soup 3,
3102  mas possui apenas o Beautiful Soup 4 instalado.
3103
3104* Se for gerado um ``ImportError`` "No module named bs4", o problema
3105  é que você está tentando executar um código Beautiful Soup 4, mas
3106  possui apenas o Beautiful Soup 3 instalado.
3107
3108Apesar do BS4 ser quase totalmente compativel com BS3, a maioria de seus
3109métodos foram depreciados e renomeados para atender o padrão `PEP 8
3110<http://www.python.org/dev/peps/pep-0008/>`_. Existem muitas outras
3111renomeações e alterações, e algumas delas quebram esta compatibilidade.
3112
3113Aqui está o que você irá precisar saber para converter seu código BS3 para BS4:
3114
3115Você precisa de um interpretador (parser)
3116^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3117
3118O Beautiful Soup 3 utilizava o ``SGMLParser`` do Python, um módulo que
3119foi depreciado e removido no Python 3.0. O Beautiful Soup 4 utiliza o
3120``html.parser`` por padrão, mas você pode adicionar o lxml ou html5lib
3121e utilizá-los como alternativa. Veja :ref:`Instalando um interpretador <parser-installation>` para
3122comparação.
3123
3124Como o ``html.parser`` não é o mesmo analisador que ``SGMLParser``, é possível
3125que o Beautiful Soup 4 retorne uma árvore de análise diferente da
3126gerada pelo Beautiful Soup 3 para as mesmas marcações. Se você trocar
3127``html.parser`` por lxml ou html5lib, você poderá descorbrir que a árvore também
3128mudará. Se isso acontecer, você precisará atualizar seu código para lidar com a
3129nova árvore.
3130
3131Nomes dos Métodos
3132^^^^^^^^^^^^^^^^^
3133
3134* ``renderContents`` -> ``encode_contents``
3135* ``replaceWith`` -> ``replace_with``
3136* ``replaceWithChildren`` -> ``unwrap``
3137* ``findAll`` -> ``find_all``
3138* ``findAllNext`` -> ``find_all_next``
3139* ``findAllPrevious`` -> ``find_all_previous``
3140* ``findNext`` -> ``find_next``
3141* ``findNextSibling`` -> ``find_next_sibling``
3142* ``findNextSiblings`` -> ``find_next_siblings``
3143* ``findParent`` -> ``find_parent``
3144* ``findParents`` -> ``find_parents``
3145* ``findPrevious`` -> ``find_previous``
3146* ``findPreviousSibling`` -> ``find_previous_sibling``
3147* ``findPreviousSiblings`` -> ``find_previous_siblings``
3148* ``getText`` -> ``get_text``
3149* ``nextSibling`` -> ``next_sibling``
3150* ``previousSibling`` -> ``previous_sibling``
3151
3152Alguns argumentos do construtor do Beautiful Soup foram renomeados pelas
3153mesmas razões:
3154
3155* ``BeautifulSoup(parseOnlyThese=...)`` -> ``BeautifulSoup(parse_only=...)``
3156* ``BeautifulSoup(fromEncoding=...)`` -> ``BeautifulSoup(from_encoding=...)``
3157
3158Eu renomeei um método para compatibilidade com Python 3:
3159
3160* ``Tag.has_key()`` -> ``Tag.has_attr()``
3161
3162Eu renomeei um atributo para utilizar uma terminologia mais precisa:
3163
3164* ``Tag.isSelfClosing`` -> ``Tag.is_empty_element``
3165
3166Eu renomeei três atributos para evitar utilizar palavras reservadas do
3167Python. Ao contrário das outras, estas alterações *não são compativeis com
3168versões anteriores.* Se você utilizar estes atributos no BS3, seu código
3169irá quebrar no BS4 até você corrigí-los.
3170
3171* ``UnicodeDammit.unicode`` -> ``UnicodeDammit.unicode_markup``
3172* ``Tag.next`` -> ``Tag.next_element``
3173* ``Tag.previous`` -> ``Tag.previous_element``
3174
3175Geradores
3176^^^^^^^^^
3177
3178Eu dei nomes aos geradores de acordo com o PEP-8 e transformei-os
3179em propriedades:
3180
3181* ``childGenerator()`` -> ``children``
3182* ``nextGenerator()`` -> ``next_elements``
3183* ``nextSiblingGenerator()`` -> ``next_siblings``
3184* ``previousGenerator()`` -> ``previous_elements``
3185* ``previousSiblingGenerator()`` -> ``previous_siblings``
3186* ``recursiveChildGenerator()`` -> ``descendants``
3187* ``parentGenerator()`` -> ``parents``
3188
3189Então, ao invés de::
3190
3191 for parent in tag.parentGenerator():
3192     ...
3193
3194Você pode escrever::
3195
3196 for parent in tag.parents:
3197     ...
3198
3199(Mas a versão antiga ainda funcionará.)
3200
3201Alguns dos geradores eram utilizados para gerar ``None`` após
3202finalizado e então parar. Isso era um bug. Agora os geradores
3203apenas param.
3204
3205Existem dois novos geradores, :ref:`.strings e
3206.stripped_strings <string-generators>`. ``.strings`` gera objetos
3207NavigableString, e ``.stripped_strings`` gera strings Python com
3208espaços em branco removidos.
3209
3210XML
3211^^^
3212Não existe mais uma classe ``BeautifulStoneSoup`` para analisar XML. Para
3213analisar XML você deverá passar "xml" como segundo argumento ao construtor
3214``BeautifulSoup``. Pela mesma razão, o construtor ``BeautifulSoup`` não
3215reconhece mais o argumento ``isHTML``.
3216
3217A manipulação do Beautiful Soup's de tags XML vazias foi melhorada.
3218Anteriormente, quando você analisava um XML, deveria explicitamente
3219dizer quais tags seriam consideradas elementos de tag vazios. O
3220argumento ``selfClosingTags`` não é mais reconhecido. Ao invés disso,
3221o Beautiful Soup considera qualquer tag vazia como um elemento de tag vazia.
3222Se você adicionar uma filha a um elemento de tag vazia, ela deixará de ser vazia.
3223
3224Entidades
3225^^^^^^^^^
3226
3227Uma entidade HTML ou XML de entrada é sempre convertida em
3228seu caractere Unicode correspondente. O Beautiful Soup 3 possuia
3229inúmeras maneiras redundantes de lidar com entidades, as quais foram
3230removidas. O construtor ``BeautifulSoup`` não reconhece mais os argumentos
3231``smartQuotesTo`` ou ``convertEntities``. (`Unicode,
3232Dammit`_ ainda possui ``smart_quotes_to``, mas seu padrão agora é converter
3233smart quotes em Unicode.) As constantes ``HTML_ENTITIES``,
3234``XML_ENTITIES``, e ``XHTML_ENTITIES``  foram removidas, desde que elas
3235se referiam a uma feature (transformar algumas, mas não todas as entidades
3236em caracteres Unicode) que não existe mais.
3237Se você quiser transformar caracteres Unicode novamente em entidades HTML
3238na saída, ao invés de transformá-las em caracteres UTF-8, você precisará
3239utilizar um :ref:`output formatter <output_formatters>`.
3240
3241Variados
3242^^^^^^^^
3243
3244:ref:`Tag.string <.string>` agora opera recursivamente. Se a tag A
3245contém apenas uma tag B e nada mais, então A.string é o mesmo que
3246B.string. (Anteriormente era None)
3247
3248`Atributos com múltiplos valores`_ como ``class`` possuem listas de strings
3249como valores e não strings. Isso deverá afetar a maneira que você buscará
3250por classes CSS.
3251
3252Se você passar um dos métodos ``find*``, ambos :ref:`string <string>` `e`
3253um argumento específico de uma tag como :ref:`name <name>`, o Beautiful Soup
3254irá buscar por tags que atentem o seu critério de argumento específico e que
3255:ref:`Tag.string <.string>` atenda o valor para :ref:`string <string>`. Isso
3256`não` irá encontrar as strings por si. Anteriormente, Beautiful Soup ignorava
3257o argumento específico  de uma tag e olhava apenas para as strings.
3258
3259O construtor ``BeautifulSoup`` não reconhece mais o argumento `markupMassage`.
3260É agora responsabilidade do parser de manipular a marcação corretamente.
3261
3262As classes raramente usadas do analisador como
3263``ICantBelieveItsBeautifulSoup`` e ``BeautifulSOAP`` foram removidas.
3264é agora decisão do analisador como manipular marcações ambiguas.
3265
3266O método ``prettify()`` agora retorna uma string Unicode, e não bytestring.
3267