Analisar HTML como um profissional: Dominando o Web Scraping com Python e Regex
Suciu Dan em 13 de abril de 2023

A quantidade de dados disponíveis na Internet tem vindo a aumentar nas últimas décadas. Os seres humanos consomem estes dados para uma grande variedade de objectivos, desde interesses pessoais a investigação empresarial.
No entanto, se estes dados não forem devolvidos de uma forma formatada, como XML ou JSON, pode ser difícil ou impossível lê-los através de aplicações de software. É aqui que entra a técnica de raspagem da Web.
A raspagem da Web é o processo de recolha e processamento de dados brutos da Internet. Estes dados são analisados e utilizados para uma variedade de fins, tais como inteligência de preços, estudos de mercado, modelos de IA de treino, análise de sentimentos, auditorias de marcas e auditorias de SEO.
Um dos principais aspectos da raspagem da Web é a análise de HTML. Isso pode ser feito usando uma variedade de ferramentas, como BeautifulSoup para Python, Cheerio para NodeJS e Nokogiri para Ruby.
As expressões regulares (regex) são uma sequência de caracteres que definem um padrão de pesquisa.
Neste artigo, vamos explorar como analisar um documento HTML usando regex e Python. Também discutiremos alguns dos desafios e soluções alternativas que acompanham a raspagem da Web.
No final do artigo, terá uma compreensão abrangente do tema e das diferentes ferramentas e técnicas disponíveis.
Análise básica de Regex
A maioria das linguagens de programação de uso geral suporta regex. É possível utilizar regex numa grande variedade de linguagens de programação, incluindo Python, C, C++, Java, Rust, OCaml e JavaScript.
Here’s what a regex rule for extracting the value from the <title> tag looks like:
<title>(.*?)</title>
Assustador, não é? Lembrem-se que isto é o início. Em breve, vamos entrar na toca do coelho.
Para este artigo, estou a usar Python 3.11.1. Vamos pegar nesta regra e pô-la em código. Cria um ficheiro chamado main.py e cola este excerto:
import re
html = "<html><head><title>Scraping</title></head></html>"
title_search = re.search("<title>(.*?)</title>", html)
title = title_search.group(1)
print(title)
Pode executar este código com o comando `python main.py`. O resultado que verá como saída é a palavra "Scraping".
Neste exemplo, estamos utilizando o módulo `re` para trabalhar com regex. A função `re.search()` procura por um padrão específico dentro de uma string. O primeiro argumento é o padrão regex, e o segundo argumento é a string na qual estamos pesquisando.
The regex pattern in this example is "<title>(.*?)</title>". It consists of several parts:
- <title>: This is a literal string, it will match the characters "<title>" exactly.
- (.*?): Este é um grupo de captura, denotado por parênteses. O caractere . corresponde a qualquer caractere único (exceto uma nova linha), e o quantificador * significa corresponder a 0 ou mais do caractere anterior. Além disso, ? faz com que o * não seja ganancioso, o que significa que ele irá parar assim que encontrar a tag de fechamento.
- </title>: This is also a literal string, it will match the characters "</title>" exactly.
A função re.search() devolve um objeto de correspondência se for encontrada uma correspondência, e o método group(1) é utilizado para extrair o texto correspondente ao primeiro grupo de captura, que é o texto entre as etiquetas de título de abertura e de fecho.
Este texto será atribuído à variável title, e o resultado será "Scraping".
Análise avançada de Regex
Extrair os dados de uma única etiqueta HTML não é muito útil. Dá-lhe uma ideia do que pode fazer com expressões regulares, mas não pode utilizá-la numa situação real.
Vamos dar uma olhada no site do PyPI, o Índice de Pacotes Python. Na página inicial, são apresentadas quatro estatísticas: o número de projectos, o número de lançamentos, o número de ficheiros e o número de utilizadores.
Queremos extrair o número de projectos. Para o conseguir, podemos utilizar este regex:
([0-9,]+) projectos
A expressão regular corresponderá a qualquer cadeia de caracteres que comece com um ou mais dígitos, opcionalmente separados por vírgulas, e termine com a palavra "projectos". É assim que funciona:
- ([0-9,]+): Este é um grupo de captura, denotado pelos parênteses; os parênteses rectos [0-9,] correspondem a qualquer dígito de 0 a 9 e ao carácter `,`; o quantificador + significa corresponder a 1 ou mais do carácter anterior.
- projectos: Trata-se de uma cadeia literal, que corresponderá exatamente a "projects".
É hora de colocar a regra em teste. Atualize o código `main.py` com este trecho:
import urllib.request
import re
response = urllib.request.urlopen("https://pypi.org/")
html = response.read().decode("utf-8")
matches = re.search("([0-9,]+) projects", html)
projects = matches.group(1)
print(projects)
Estamos a utilizar o método urlopen da biblioteca urllib para fazer um pedido GET ao site pypi.org. Lemos a resposta na variável html. Executamos a regra regex no conteúdo HTML e imprimimos o primeiro grupo correspondente.
Execute o código com o comando `python main.py` e verifique a saída: ele exibirá o número de projetos do site.
Extração de ligações
Agora que temos um raspador simples que pode obter o documento HTML de um site, vamos brincar um pouco com o código.
Podemos extrair todas as ligações com esta regra:
href=[\'"]?([^\'" >]+)
Esta expressão regular é composta por várias partes:
- href=: trata-se de uma cadeia literal, que corresponderá exatamente aos caracteres "href=".
- [\'"]?: os parêntesis rectos [] correspondem a qualquer carácter individual no seu interior, neste caso, os caracteres ' ou "; o quantificador ? significa corresponder a zero ou a um dos caracteres anteriores, o que significa que o valor do href pode ser delimitado por " ou ' ou nenhum.
- ([^\'" >]+): trata-se de um grupo de captura, denotado pelos parênteses; o ^ dentro dos parênteses rectos significa negação, corresponderá a qualquer carácter que não seja um ',",>, ou um espaço; o quantificador + significa corresponder a 1 ou mais do carácter precedente, significa que o grupo irá capturar um ou mais caracteres que correspondam ao padrão.
Extração de imagens
Mais uma coisa e estamos quase a terminar de escrever regras regex: precisamos de extrair as imagens. Vamos utilizar esta regra:
<img.*?src="(.*?)"
Esta expressão regular é composta por várias partes:
- <img: This is a literal string, it will match the characters "<img" exactly.
- .*?: the .* match any character (except a newline) 0 or more times, and the ? quantifier means to match as few as possible of the preceding character; this is used to match any character that appears before the src attribute in the <img> tag, and it allows the pattern to match any <img> tag regardless of the number of attributes it has.
- src=": trata-se de uma cadeia literal, que corresponderá exatamente aos caracteres "src=".
- (.*?): this is a capturing group, denoted by the parentheses; the .*? match any character (except a newline) 0 or more times, and the ? quantifier means to match as few as possible of the preceding character; this group captures the src value of the <img> tag.
- ": trata-se de uma cadeia literal, que corresponderá exatamente ao carácter ".
Vamos pô-lo à prova. Substitua o código do snippet anterior por este:
import urllib.request
import re
response = urllib.request.urlopen("https://pypi.org/")
html = response.read().decode("utf-8")
images = re.findall('<img.*?src="(.*?)"', html)
print(*images, sep = "\n")
O resultado deste código apresentará uma lista com todas as ligações de imagens da página Pypi.
Limitações
O Web scraping com expressões regulares pode ser uma ferramenta poderosa para extrair dados de sítios Web, no entanto, também tem as suas limitações. Um dos principais problemas com a utilização de regex para a recolha de dados da Web é o facto de poder falhar quando a estrutura do HTML muda.
Por exemplo, considere o seguinte exemplo de código em que estamos a tentar extrair o texto do h2 utilizando regex:
<html>
<head>
<title>Example Title</title>
</head>
<body>
<h1>Page Title</h1>
<p>This is a paragraph under the title</p>
<h2>First Subtitle</h2>
<p>First paragraph under the subtitle</p>
<h2>Second Subtitle</p>
</body>
</html>
Compare the first <h2> tag with the second one. You may notice the second <h2> is not properly closed, and the code has </p> instead of </h2>. Let’s update the snippet with this:
import re
html = "<html><head><title>Example Title</title></head><body><h1>Page Title</h1><p>This is a paragraph under the title</p><h2>First Subtitle</h2><p>First paragraph under the subtitle</p><h2>Second Subtitle</p></body></html>"
headingTags = re.findall("<h2>(.*?)</h2>", html)
print(*headingTags, sep = "\n")
Vamos executar o código e verificar o resultado:
Primeira legenda
O texto da segunda etiqueta de cabeçalho está em falta. Isto acontece porque a regra regex não está a corresponder à etiqueta de título não fechada.
Uma solução para este problema é utilizar uma biblioteca como a BeautifulSoup, que lhe permite navegar e pesquisar a estrutura em árvore do HTML, em vez de se basear em expressões regulares. Com a BeautifulSoup, pode extrair o título de uma página Web desta forma:
from bs4 import BeautifulSoup
html = "<html><head><title>Example Title</title></head><body><h1>Page Title</h1><p>This is a paragraph under the title</p><h2>First Subtitle</h2><p>First paragraph under the subtitle</p><h2>Second Subtitle</p></body></html>"
soup = BeautifulSoup(html, 'html.parser')
for headingTag in soup.findAll('h2'):
print(headingTag.text)
O BeautifulSoup consegue extrair as etiquetas malformadas e o resultado é o seguinte:
Primeiro Subtítulo
Segundo Subtítulo
Essa abordagem é mais robusta a mudanças na estrutura HTML, pois não depende de padrões específicos no código HTML. Se estiver interessado em saber mais sobre a BeautifulSoup, este artigo é uma leitura perfeita.
Outra solução é utilizar uma API de raspagem da Web, como a WebScrapingAPI, que abstrai as complexidades da raspagem da Web e permite-lhe extrair facilmente os dados de que necessita sem se preocupar com a estrutura HTML subjacente.
Com o WebScrapingAPI, pode extrair dados de qualquer sítio Web com uma simples chamada à API, que trata automaticamente as alterações na estrutura HTML.
Considerações finais
A análise de dados com expressões regulares pode ser uma ferramenta poderosa para extrair dados de sítios Web.
Neste artigo, discutimos os conceitos básicos das expressões regulares, como usá-las para analisar HTML e alguns dos desafios que você pode encontrar ao usá-las. Também vimos como bibliotecas como BeautifulSoup podem ser usadas como uma solução alternativa.
Aprendeu a extrair dados de páginas Web utilizando expressões regulares e a melhorar a fiabilidade do seu código utilizando uma biblioteca mais robusta, como a BeautifulSoup.
O Web scraping pode ser uma tarefa morosa, mas com as ferramentas certas, pode ser fácil e eficiente. Se estiver à procura de uma solução de Web scraping que lhe poupe tempo e esforço, experimente o WebScrapingAPI.
Oferecemos uma avaliação gratuita durante 14 dias, onde pode testar o nosso serviço e ver as vantagens de utilizar uma API de raspagem da Web.
Notícias e actualizações
Mantenha-se atualizado com os mais recentes guias e notícias sobre raspagem da Web, subscrevendo a nossa newsletter.
We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Artigos relacionados

Mergulhe no papel transformador dos dados financeiros na tomada de decisões empresariais. Compreender os dados financeiros tradicionais e a importância emergente dos dados alternativos.


Descubra como extrair e organizar eficientemente dados para raspagem da Web e análise de dados através de análise de dados, bibliotecas de análise de HTML e metadados schema.org.


Os selectores XPath são melhores do que os selectores CSS para a recolha de dados da Web? Conheça os pontos fortes e as limitações de cada método e faça a escolha certa para o seu projeto!
