No último tutorial, você aprendeu o básico da biblioteca Beautiful Soup. Além de navegar na árvore DOM, você também pode pesquisar por elementos com um determinado class
ou id
. Você também pode modificar a árvore DOM usando esta biblioteca.
Neste tutorial, você aprenderá sobre diferentes métodos que o ajudarão na busca e modificações. Estaremos raspando a mesma página da Wikipedia sobre Python do nosso último tutorial.
Filtros para pesquisar na árvore
Beautiful Soup tem muitos métodos para pesquisar a árvore DOM. Esses métodos são muito semelhantes e usam os mesmos tipos de filtros como argumentos. Portanto, faz sentido entender adequadamente os diferentes filtros antes de ler sobre os métodos. vou usar o mesmo find_all()
método para explicar a diferença entre os diferentes filtros.
O filtro mais simples que você pode passar para qualquer método de pesquisa é uma string. O Beautiful Soup irá então pesquisar no documento para encontrar uma tag que corresponda exatamente à string.
for heading in soup.find_all('h2'): print(heading.text) # Contents # History[edit] # Features and philosophy[edit] # Syntax and semantics[edit] # Libraries[edit] # Development environments[edit] # ... and so on.
Você também pode passar um objeto de expressão regular para o find_all()
método. Desta vez, o Beautiful Soup filtrará a árvore combinando todas as tags com uma determinada expressão regular.
import re for heading in soup.find_all(re.compile("^h[1-6]")): print(heading.name + ' ' + heading.text.strip()) # h1 Python (programming language) # h2 Contents # h2 History[edit] # h2 Features and philosophy[edit] # h2 Syntax and semantics[edit] # h3 Indentation[edit] # h3 Statements and control flow[edit] # ... an so on.
O código procurará todas as tags que começam com “h” e são seguidas por um dígito de 1 a 6. Em outras palavras, ele procurará todas as tags de cabeçalho no documento.
Em vez de usar regex, você pode obter o mesmo resultado passando uma lista de todas as tags que deseja que o Beautiful Soup corresponda ao documento.
for heading in soup.find_all(["h1", "h2", "h3", "h4", "h5", "h6"]): print(heading.name + ' ' + heading.text.strip())
Você também pode passar True
como parâmetro para o find_all()
método. O código retornará todas as tags no documento. A saída abaixo significa que existem atualmente 4.339 tags na página da Wikipedia que estamos analisando.
len(soup.find_all(True)) # 4339
Se você ainda não conseguir encontrar o que está procurando com nenhum dos filtros acima, você pode definir sua própria função que recebe um elemento como seu único argumento. A função também precisa retornar True
se houver uma correspondência e False
por outro lado. Dependendo do que você precisa, você pode tornar a função tão complicada quanto precisa ser para fazer o trabalho. Aqui está um exemplo muito simples:
def big_lists(tag): return len(tag.contents) > 20 and tag.name == 'ul' len(soup.find_all(big_lists)) # 13
A função acima está passando pela mesma página Python da Wikipedia e procurando listas não ordenadas que tenham mais de 20 filhos.
Pesquisando na árvore DOM usando funções integradas
Pesquisando com find_all()
Um dos métodos mais populares para pesquisar no DOM é find_all()
. Ele passará por todos os descendentes da tag e retornará uma lista de todos os descendentes que correspondem aos seus critérios de pesquisa. Este método tem a seguinte assinatura:
find_all(name, attrs, recursive, string, limit, **kwargs)
o name
argumento é o nome da tag que você deseja que esta função procure enquanto percorre a árvore. Você pode fornecer uma string, uma lista, uma expressão regular, uma função ou o valor True
como um nome.
Filtrando por Atributo
Você também pode filtrar os elementos na árvore DOM com base em diferentes atributos como id
, href
etc. Você também pode obter todos os elementos com um atributo específico, independentemente de seu valor, usando attribute=True
. Pesquisar elementos com uma classe específica é diferente de pesquisar atributos regulares. Desde a class
é uma palavra-chave reservada em Python, você terá que usar o class_
argumento de palavra-chave ao procurar elementos com uma classe específica.
import re len(soup.find_all(id=True)) # 425 len(soup.find_all(class_=True)) # 1734 len(soup.find_all(class_="mw-headline")) # 20 len(soup.find_all(href=True)) # 1410 len(soup.find_all(href=re.compile("python"))) # 102
Você pode ver que o documento tem 1.734 tags com um class
atributo e 425 tags com um id
atributo.
Limitando o número de resultados
Se você precisar apenas dos primeiros resultados, você pode passar um número para o método como o valor de limit
. Passar esse valor instruirá a Beautiful Soup a parar de procurar por mais elementos assim que atingir um determinado número. Aqui está um exemplo:
soup.find_all(class_="mw-headline", limit=4) # History # Features and philosophy # Syntax and semantics # Indentation
Pesquisa não recursiva
Quando você usa o find_all()
método, você está dizendo ao Beautiful Soup para passar por todos os descendentes de uma determinada tag para encontrar o que você está procurando. Às vezes, você deseja procurar um elemento apenas nos filhos diretos de uma tag. Isso pode ser conseguido passando recursive=False
ao find_all()
método.
len(soup.html.find_all("meta")) # 6 len(soup.html.find_all("meta", recursive=False)) # 0 len(soup.head.find_all("meta", recursive=False)) # 6
Encontrando um único resultado
Se você estiver interessado em encontrar apenas um resultado para uma consulta de pesquisa específica, poderá usar o find()
método para encontrá-lo em vez de passar limit=1
para find_all()
. A única diferença entre os resultados retornados por esses dois métodos é que find_all()
retorna uma lista com apenas um elemento e find()
apenas retorna o resultado.
soup.find_all("h2", limit=1) # [Contents
] soup.find("h2") #Contents
o find()
e find_all()
métodos pesquisam em todos os descendentes de uma determinada tag para procurar um elemento.
Pesquisas de pais e irmãos
Existem dez outros métodos muito semelhantes que você pode usar para percorrer a árvore DOM em diferentes direções.
find_parents(name, attrs, string, limit, **kwargs) find_parent(name, attrs, string, **kwargs) find_next_siblings(name, attrs, string, limit, **kwargs) find_next_sibling(name, attrs, string, **kwargs) find_previous_siblings(name, attrs, string, limit, **kwargs) find_previous_sibling(name, attrs, string, **kwargs) find_all_next(name, attrs, string, limit, **kwargs) find_next(name, attrs, string, **kwargs) find_all_previous(name, attrs, string, limit, **kwargs) find_previous(name, attrs, string, **kwargs)
o find_parent()
e find_parents()
métodos percorrem a árvore DOM para encontrar o elemento fornecido. o find_next_sibling()
e find_next_siblings()
métodos irão iterar sobre todos os irmãos do elemento que vem depois do atual. Da mesma forma, o find_previous_sibling()
e find_previous_siblings()
métodos irão iterar sobre todos os irmãos do elemento que vêm antes do atual.
o find_next()
e find_all_next()
métodos irão iterar sobre todas as tags e strings que vêm após o elemento atual. Da mesma forma, o find_previous()
e find_all_previous()
métodos irão iterar sobre todas as tags e strings que vêm antes do elemento atual.
Pesquisar usando seletores CSS
Você também pode pesquisar elementos usando seletores CSS com a ajuda do select()
método. Aqui estão alguns exemplos:
len(soup.select("p a")) # 411 len(soup.select("p > a")) # 291 soup.select("h2:nth-of-type(1)") # [Contents
] len(soup.select("p > a:nth-of-type(2)")) # 46 len(soup.select("p > a:nth-of-type(10)")) # 6 len(soup.select("[class*=section]")) # 80 len(soup.select("[class$=section]")) # 20
Modificando a árvore
Você pode não apenas pesquisar na árvore DOM para encontrar um elemento, mas também modificá-lo. É muito fácil renomear uma tag e modificar seus atributos.
heading_tag = soup.select("h2:nth-of-type(2)")[0] heading_tag.name = "h3" print(heading_tag) #Feat... heading_tag['class'] = 'headingChanged' print(heading_tag) #
Continuando com nosso último exemplo, você pode substituir o conteúdo de uma tag por uma determinada string usando o
.string
atributo. Se você não quiser substituir o conteúdo, mas adicionar algo extra no final da tag, você pode usar oappend()
método.
Adicionando vários elementos a uma tag
E se você quiser adicionar vários elementos a uma tag? Você pode fazer isso com o
extend()
método. Ele aceita uma lista de elementos como seu parâmetro. Esses elementos são adicionados à tag de chamada na ordem em que aparecem.import requests from bs4 import BeautifulSoup req = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)') soup = BeautifulSoup(req.text, "lxml") new_soup = BeautifulSoup("", "lxml") new_soup.ol.extend(['
' + heading.text + ' ' for heading in soup.find_all('h2')]) # ['Contents ', 'History[edit] ', ... , 'Navigation menu '] print(new_soup.ol.contents) # Returns an empty list print(new_soup.find_all('li'))
No exemplo acima, criamos um novo BeautifulSoup
objeto para armazenar os títulos como uma lista. A lista é gerada usando compreensões de lista em Python. Passamos essa lista dentro do extend()
método para anexar tudo ao nosso ol
marcação. Pode parecer que estamos adicionando os títulos dentro do nosso ol
tag como elementos de lista individuais, mas eles estão sendo adicionados como uma string. Isso fica evidente quando usamos find_all()
no new_soup
Nós criamos.
A melhor maneira de adicionar elementos como tags HTML apropriadas é chamar o new_tag()
método. O único argumento necessário neste caso é o nome da tag, mas você também pode adicionar outros atributos conforme mostrado abaixo.
import requests from bs4 import BeautifulSoup req = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)') soup = BeautifulSoup(req.text, "lxml") new_soup = BeautifulSoup("", "lxml") all_tags = [] counter = 0 for heading in soup.find_all('h2'): counter += 1 id_string = "list-item-" + str(counter) tag = new_soup.new_tag('li', id=id_string, attrs={"class": "outline"}) tag.string = heading.text all_tags.append(tag) new_soup.ol.extend(all_tags) # [
Você pode ver na saída desta vez que os elementos da lista não são mais simples strings, mas elementos HTML reais.
Inserir um elemento em um local específico
Se você deseja inserir algo dentro de uma tag em um local específico, você pode usar o insert()
método. O primeiro parâmetro para este método é a posição ou índice no qual você deseja inserir o conteúdo e o segundo parâmetro é o próprio conteúdo. Você pode remover todo o conteúdo dentro de uma tag usando o clear()
método. Isso apenas deixará você com a própria tag e seus atributos.
heading_tag.string = "Features and Philosophy" print(heading_tag) #Features and Philosophy
heading_tag.append(" [Appended This Part].") print(heading_tag) #Features and Philosophy [Appended This Part].
print(heading_tag.contents) # ['Features and Philosophy', ' [Appended This Part].'] heading_tag.insert(1, ' Inserted this part ') print(heading_tag) #Features and Philosophy Inserted this part [Appended This Part].
heading_tag.clear() print(heading_tag) #
No início desta seção, você selecionou um título de nível dois do documento e o alterou para um título de nível três. Usar o mesmo seletor novamente agora mostrará o próximo título de nível dois que veio após o original. Isso faz sentido porque o título original não é mais um título de nível dois.
O título original agora pode ser selecionado usando h3:nth-of-type(2)
. Se você deseja remover completamente um elemento ou tag e todo o conteúdo dentro dele da árvore, você pode usar o decompose()
método.
soup.select("h3:nth-of-type(2)")[0] # soup.select("h3:nth-of-type(3)")[0] #Indentation... soup.select("h3:nth-of-type(2)")[0].decompose() soup.select("h3:nth-of-type(2)")[0] #
Indentation...
Depois de decompor ou remover o título original, o título do terceiro ponto toma seu lugar.
Se você deseja remover uma tag e seu conteúdo da árvore, mas não deseja destruir completamente a tag, você pode usar o comando extract()
método. Este método retornará a tag que extraiu. Agora você terá duas árvores diferentes que você pode analisar. A raiz da nova árvore será a tag que você acabou de extrair.
heading_tree = soup.select("h3:nth-of-type(2)")[0].extract() len(heading_tree.contents) # 2
Você também pode substituir uma tag dentro da árvore por outra de sua escolha usando o replace_with()
método. Este método retornará a tag ou string que substituiu. Pode ser útil se você quiser colocar o conteúdo substituído em outro lugar do documento.
soup.h1 #Python (programming language)
bold_tag = soup.new_tag("b") bold_tag.string = "Python" soup.h1.replace_with(bold_tag) print(soup.h1) # None print(soup.b) # Python
No código acima, o título principal do documento foi substituído por um b
marcação. O documento não tem mais h1
marca, e é por isso que print(soup.h1)
agora imprime None
.
Embrulhar e desembrulhar tags
Mais dois métodos que serão úteis quando você estiver modificando o DOM são wrap()
e unwrap()
. O método wrap()
é útil quando você deseja envolver uma tag em torno de determinado conteúdo. Da mesma forma, o unwrap()
O método se livra da tag de chamada deixando apenas seu conteúdo para trás.
soup = BeautifulSoup("", "lxml") for list_item in soup.find_all('li'): list_item.string.wrap(soup.new_tag("b")) # [
- Overview
- Main Content
- Conclusion
Você pode usar o unwrap()
método para remover a marcação fornecida de tags específicas. No exemplo a seguir, vamos usá-lo para remover todos os e
tags de um parágrafo.
soup = BeautifulSoup("We will try to get rid of tags that make text bold or italic. The content within the tags should still be preserved.
", "lxml") for unwanted_tag in soup.find_all(["b", "i"]): unwanted_tag.unwrap() # ['We will ', 'try', ' to get rid of ', 'tags', ... , 'preserved', '.'] print(soup.p.contents) soup.p.smooth() # ['We will try to get rid of tags ... preserved.'] print(soup.p.contents)
No exemplo acima, criamos uma lista de tags indesejadas que queremos remover e passamos para find_all()
. Este método então encontra todas as instâncias dessas tags e chama unwrap()
em todos eles. Um efeito colateral de executar o código acima é que todos os bits individuais de texto são armazenados como NavigableString
objetos. NavigableStrings
são como strings regulares, exceto que carregam informações sobre a árvore de análise. Você pode combiná-los todos em uma única string chamando o método smooth()
método.
Pensamentos finais
Depois de ler os dois tutoriais desta série, agora você poderá analisar diferentes páginas da Web e extrair dados importantes do documento. Você também deve poder recuperar a página da Web original, modificá-la para atender às suas próprias necessidades e salvar a versão modificada localmente.
Se você tiver alguma dúvida sobre este tutorial, deixe-me saber nos comentários.
Originally posted 2022-05-24 04:09:38.