Análise básica de regex
A maioria das linguagens de programação de uso geral suporta regex. Pode utilizar regex numa ampla variedade de linguagens de programação, incluindo Python, C, C++, Java, Rust, OCaml e JavaScript.
Eis como se apresenta uma regra de expressão regular para extrair o valor da tag <title>:
<title>(.*?)</title>
Assustador, não é? Tenha em mente que isto é apenas o começo. Em breve, vamos mergulhar mais fundo no assunto.
Para este artigo, estou a usar o Python 3.11.1. Vamos pegar nesta regra e transformá-la em código. Crie um ficheiro chamado main.py e cole este trecho:
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 através do comando `python main.py`. O resultado que verá como saída é a palavra “Scraping”.
Neste exemplo, estamos a usar o módulo `re` para trabalhar com expressões regulares. A função `re.search()` procura um padrão específico dentro de uma string. O primeiro argumento é o padrão de expressão regular e o segundo argumento é a string na qual estamos a pesquisar.
O padrão de expressão regular neste exemplo é "<title>(.*?)</title>". É composto por várias partes:
- <title>: Esta é uma cadeia de caracteres literal, que corresponderá exatamente aos caracteres “<title>”.
- (.*?): 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 precedente. Além disso, ? torna o * não-greedy, o que significa que irá parar assim que encontrar a tag de fecho.
- </title>: Esta é também uma cadeia literal, que corresponderá exatamente aos caracteres "</title>".
A função re.search() retorna um objeto de correspondência se for encontrada uma correspondência, e o método group(1) é usado para extrair o texto correspondido pelo primeiro grupo de captura, que é o texto entre as tags de abertura e fechamento do título.
Este texto será atribuído à variável title, e a saída será "Scraping".
Análise avançada de expressões regulares
Extrair os dados de uma única tag HTML não é muito útil. Dá-lhe uma ideia do que pode fazer com expressões regulares, mas não pode usá-la numa situação real.
Vamos dar uma vista de olhos no site do PyPI, o Índice de Pacotes Python. Na página inicial, são apresentadas quatro estatísticas: o número de projetos, o número de lançamentos, o número de ficheiros e o número de utilizadores.
Queremos extrair o número de projetos. Para o fazer, podemos usar esta expressão regular:
([0-9,]+) projects
A expressão regular irá corresponder a qualquer cadeia de caracteres que comece com um ou mais dígitos, opcionalmente separados por vírgulas, e termine com a palavra «projects». É assim que funciona:
- ([0-9,]+): Este é um grupo de captura, indicado pelos parênteses; os colchetes [0-9,] correspondem a qualquer dígito de 0 a 9 e ao caractere `,`; o quantificador + significa corresponder a 1 ou mais do caractere precedente.
- projects: Esta é uma cadeia literal, que corresponderá exatamente a "projects".
É hora de testar a regra. 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. Aplicamos a regra regex ao conteúdo HTML e imprimimos o primeiro grupo correspondente.
Execute o código com o comando `python main.py` e verifique o resultado: ele exibirá o número de projetos do site.
Extrair links
Agora que temos um scraper simples capaz de obter o documento HTML de um site, vamos brincar um pouco com o código.
Podemos extrair todos os links com esta regra:
href=[\'"]?([^\'" >]+)
Esta expressão regular é composta por várias partes:
- href=: esta é uma cadeia de caracteres literal, que corresponderá exatamente aos caracteres "href=".
- [\'"]?: os parênteses retos [] correspondem a qualquer caractere único dentro deles, neste caso, os caracteres ' ou "; o quantificador ? significa corresponder a zero ou um dos caracteres precedentes, o que significa que o valor href pode estar entre " ou ' ou nenhum.
- ([^\'" >]+): este é um grupo de captura, denotado pelos parênteses; o ^ dentro dos colchetes significa negação, irá corresponder a qualquer caractere que não seja ', ", > ou um espaço; o quantificador + significa corresponder a 1 ou mais do caractere precedente, o que significa que o grupo irá capturar um ou mais caracteres que correspondam ao padrão.
Extrair imagens
Mais uma coisa e estamos quase a terminar de escrever as regras de regex: precisamos de extrair as imagens. Vamos usar esta regra:
<img.*?src="(.*?)"
Esta expressão regular consiste em várias partes:
- <img: Esta é uma cadeia literal, que corresponderá exatamente aos caracteres "<img".
- .*?: o .* corresponde a qualquer caractere (exceto uma nova linha) 0 ou mais vezes, e o quantificador ? significa corresponder ao menor número possível do caractere anterior; isto é usado para corresponder a qualquer caractere que apareça antes do atributo src na tag <img>, e permite que o padrão corresponda a qualquer tag <img>, independentemente do número de atributos que tenha.
- src=": esta é uma cadeia literal, que corresponderá exatamente aos caracteres "src=".
- (.*?): isto é um grupo de captura, denotado pelos parênteses; o .*? corresponde a qualquer caractere (exceto uma nova linha) 0 ou mais vezes, e o quantificador ? significa corresponder ao menor número possível do caractere precedente; este grupo captura o valor src da tag <img>.
- ": esta é uma string literal, irá corresponder exatamente ao caractere ".
Vamos testar isto. Substitua o trecho de código 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")
A saída deste código irá apresentar uma lista com todos os links de imagens da página do Pypi.
Limitações
O web scraping com expressões regulares pode ser uma ferramenta poderosa para extrair dados de sites, no entanto, também tem as suas limitações. Um dos principais problemas com a utilização de regex para web scraping é que pode falhar quando a estrutura do HTML muda.
Por exemplo, considere o seguinte exemplo de código, onde estamos a tentar extrair o texto do h2 usando 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 a primeira tag <h2> com a segunda. Poderá notar que a segunda tag <h2> não está devidamente fechada e que o código tem </p> em vez de </h2>. Vamos atualizar o trecho com isto:
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:
First Subtitle
Falta o texto da segunda tag de título. Isto acontece porque a regra de expressão regular não corresponde à tag de título não fechada.
Uma solução para este problema é utilizar uma biblioteca como a BeautifulSoup, que permite navegar e pesquisar na estrutura da árvore HTML, em vez de depender de expressões regulares. Com a BeautifulSoup, pode extrair o título de uma página web da seguinte 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 tags malformadas e o resultado fica assim:
First Subtitle
Second Subtitle
Esta abordagem é mais robusta face a alterações na estrutura HTML, uma vez que não depende de padrões específicos no código HTML. Se estiver interessado em saber mais sobre o BeautifulSoup, este artigo é a leitura perfeita.
Outra solução é utilizar uma API de web scraping, como a WebScrapingAPI, que simplifica as complexidades do web scraping e permite extrair facilmente os dados de que necessita sem se preocupar com a estrutura HTML subjacente.
Com a WebScrapingAPI, pode extrair dados de qualquer site com uma simples chamada de API, e esta lida automaticamente com 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 sites.
Neste artigo, discutimos os conceitos básicos das expressões regulares, como utilizá-las para analisar HTML e alguns dos desafios que poderá encontrar ao utilizá-las. Também vimos como bibliotecas como a BeautifulSoup podem ser utilizadas como 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 demorada, mas com as ferramentas certas, pode ser fácil e eficiente. Se procura uma solução de web scraping que lhe poupe tempo e esforço, experimente a WebScrapingAPI.
Oferecemos um período de teste gratuito de 14 dias, durante o qual poderá testar o nosso serviço e ver as vantagens de utilizar uma API de web scraping.




