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é 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("“Dammit!” 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 "&", "<", 2214e ">", 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, & 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&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 <<Sacré bleu!>></p>" 2231 soup = BeautifulSoup(french) 2232 print(soup.prettify(formatter="minimal")) 2233 # <html> 2234 # <body> 2235 # <p> 2236 # Il a dit <<Sacré bleu!>> 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 <<Sacré bleu!>> 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 "☃" para as essas codificações:: 2605 2606 print(tag.encode("utf-8")) 2607 # <b>☃</b> 2608 2609 print tag.encode("latin-1") 2610 # <b>☃</b> 2611 2612 print tag.encode("ascii") 2613 # <b>☃</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 “love” Microsoft Word’s smart quotes</p>' 2654 2655 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="xml").unicode_markup 2656 # u'<p>I just “love” Microsoft Word’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