Projeto: web scraper
LidoNesta aula, você construirá um web scraper completo em Python, cobrindo desde o uso da biblioteca requests para baixar páginas, até o parsing com BeautifulSoup, armazenamento dos dados extraídos e considerações éticas importantes. O projeto integra conceitos de HTTP, HTML e boas práticas de programação.
Bem-vindo à aula 92 do nosso curso de Python! Hoje vamos colocar a mão na massa e construir um web scraper do zero. Web scraping é a técnica de extrair dados de sites de forma automatizada. É uma habilidade extremamente útil no mercado, usada em áreas como análise de dados, pesquisa acadêmica, monitoramento de preços e muito mais. Nesta aula, você vai aprender a usar a biblioteca requests para baixar páginas web, a analisar o HTML com o BeautifulSoup, a armazenar os dados extraídos e, principalmente, a fazer isso de forma ética e responsável.
Vamos desenvolver um projeto prático: um scraper que coleta informações de livros de um site de exemplo. Ao final, você terá um script funcional que pode ser adaptado para outros sites. Além disso, vamos discutir os limites legais e éticos do scraping, para que você use esse poder com sabedoria.
requests
A biblioteca requests é a ferramenta padrão em Python para fazer requisições HTTP. Ela simplifica enormemente o processo de enviar solicitações para servidores web e receber as respostas. Com ela, podemos baixar páginas HTML, fazer download de arquivos, interagir com APIs e muito mais. O primeiro passo em qualquer scraper é obter o conteúdo da página que queremos analisar.
Para instalar o requests, use o pip: pip install requests. Em seguida, podemos fazer uma requisição GET simples:
import requests
url = "https://example.com"
resposta = requests.get(url)
print(resposta.status_code) # 200 indica sucesso
print(resposta.text[:200]) # primeiros 200 caracteres do HTML
O objeto Response contém várias informações úteis, como o status code, os cabeçalhos e o conteúdo. É importante sempre verificar se a requisição foi bem-sucedida (status 200) antes de tentar analisar o conteúdo. Podemos usar resposta.raise_for_status() para lançar uma exceção se houver erro. Além disso, podemos passar parâmetros na URL, cabeçalhos personalizados (como User-Agent) e até mesmo dados de formulário em requisições POST. No scraping, muitas vezes precisamos imitar um navegador para evitar bloqueios, então definir um User-Agent realista é uma boa prática.
No nosso projeto, vamos usar o site books.toscrape.com, que é um site fictício criado especificamente para praticar scraping. Ele contém uma lista de livros com títulos, preços, avaliações e links. Vamos baixar a primeira página e ver o que obtemos:
import requests
url = "https://books.toscrape.com/"
resposta = requests.get(url)
if resposta.status_code == 200:
print(resposta.text[:1000])
else:
print(f"Erro: {resposta.status_code}")
Essa é a base do scraping com requests. Agora, vamos aprender a interpretar o HTML que recebemos.
Parsing (visão geral)
Baixar a página é apenas o primeiro passo. O HTML retornado é um texto cheio de tags, e precisamos extrair as informações relevantes. É aqui que entra o parsing. Existem várias bibliotecas em Python para isso, sendo a mais popular o BeautifulSoup. Ela nos permite navegar pela árvore HTML e selecionar elementos de forma intuitiva.
Para instalar o BeautifulSoup: pip install beautifulsoup4. Vamos criar um objeto BeautifulSoup a partir do texto HTML:
from bs4 import BeautifulSoup
soup = BeautifulSoup(resposta.text, 'html.parser')
Com o objeto soup, podemos usar métodos como find(), find_all() e seletores CSS com select(). Por exemplo, para encontrar todos os títulos de livros no site books.toscrape.com, precisamos inspecionar a estrutura HTML da página. Abra a página no navegador, clique com o botão direito e selecione "Inspecionar". Veremos que cada livro está dentro de um <article class="product_pod">. Dentro dele, o título está em uma tag <h3><a>. Podemos extrair todos os títulos assim:
artigos = soup.find_all('article', class_='product_pod')
for artigo in artigos:
titulo = artigo.h3.a['title']
print(titulo)
O BeautifulSoup é muito flexível. Podemos acessar atributos, navegar entre tags pai e filho, e até mesmo modificar o HTML. Para scraping, geralmente usamos seletores CSS para ser mais preciso. Por exemplo, soup.select('article.product_pod h3 a') retorna a mesma lista de links. É importante inspecionar a página de destino para entender a estrutura e criar seletores corretos.
No nosso projeto, vamos extrair título, preço, avaliação e link de cada livro. Vamos ver como acessar o preço: ele está em um <p class="price_color">. A avaliação está em uma classe como star-rating One, Two, etc. Vamos fazer isso de forma robusta.
import requests
from bs4 import BeautifulSoup
url = "https://books.toscrape.com/"
resposta = requests.get(url)
soup = BeautifulSoup(resposta.text, 'html.parser')
livros = []
for artigo in soup.select('article.product_pod'):
titulo = artigo.h3.a['title']
preco = artigo.select_one('.price_color').text
avaliacao = artigo.select_one('.star-rating')['class'][1] # ex: 'One'
link = artigo.h3.a['href']
livros.append({'titulo': titulo, 'preco': preco, 'avaliacao': avaliacao, 'link': link})
for livro in livros[:3]:
print(livro)
Esse código já extrai as informações principais. Mas note que o preço vem como texto, como "£51.77". Podemos limpá-lo depois. Agora, vamos pensar no armazenamento.
Armazenamento
Depois de extrair os dados, precisamos salvá-los de alguma forma para uso posterior. As opções mais comuns são: salvar em um arquivo CSV, JSON, ou em um banco de dados como SQLite. Para projetos simples, CSV e JSON são suficientes e fáceis de manipular. Para projetos maiores, um banco de dados é mais adequado.
Vamos começar com JSON, que é muito popular para troca de dados. O módulo json da biblioteca padrão permite serializar listas e dicionários facilmente:
import json
with open('livros.json', 'w', encoding='utf-8') as f:
json.dump(livros, f, ensure_ascii=False, indent=2)
Isso cria um arquivo livros.json com os dados. Para CSV, usamos o módulo csv:
import csv
with open('livros.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['titulo', 'preco', 'avaliacao', 'link'])
writer.writeheader()
writer.writerows(livros)
CSV é ótimo para abrir em Excel ou planilhas. JSON é melhor para integração com APIs e outros sistemas. A escolha depende do seu objetivo. No nosso projeto, vamos salvar em JSON e depois, se quisermos, podemos carregar e analisar.
Se a quantidade de dados for grande, considere usar SQLite, que é um banco de dados leve e embutido. O módulo sqlite3 permite criar tabelas e inserir registros. Exemplo:
import sqlite3
conn = sqlite3.connect('livros.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS livros
(titulo TEXT, preco TEXT, avaliacao TEXT, link TEXT)''')
for livro in livros:
c.execute("INSERT INTO livros VALUES (?, ?, ?, ?)",
(livro['titulo'], livro['preco'], livro['avaliacao'], livro['link']))
conn.commit()
conn.close()
Armazenar em banco de dados facilita consultas complexas. No entanto, para este projeto, vamos nos ater a JSON, que é simples e suficiente.
Ética e limites
Web scraping pode ser uma ferramenta poderosa, mas também pode violar os termos de uso de um site ou até mesmo leis, se feito de forma inadequada. É fundamental agir com ética e respeitar os limites. Aqui estão algumas diretrizes:
- Respeite o robots.txt: O arquivo
robots.txtindica quais partes do site podem ser acessadas por bots. Você pode consultá-lo emhttps://site.com/robots.txt. Se o site proibir o acesso a determinadas rotas, não as escrape. - Seja gentil com o servidor: Faça requisições com moderação, adicione um atraso entre elas (por exemplo,
time.sleep(1)) para não sobrecarregar o servidor. - Identifique-se: Use um User-Agent que identifique seu bot e inclua informações de contato, como um e-mail.
- Verifique os termos de serviço: Muitos sites proíbem explicitamente o scraping. Leia os termos e respeite.
- Use APIs quando disponíveis: Se o site oferecer uma API oficial, prefira usá-la em vez de raspar.
- Não colete dados pessoais: Evite raspar informações pessoais, a menos que tenha consentimento.
No nosso exemplo, books.toscrape.com foi criado especificamente para praticar scraping, então é permitido. Mas lembre-se: sempre verifique antes de raspar qualquer site.
Além disso, é importante lidar com erros e exceções. Sites podem mudar de estrutura, a rede pode falhar, etc. Use try/except para tratar falhas de rede e erros de parsing. Por exemplo:
try:
resposta = requests.get(url, timeout=10)
resposta.raise_for_status()
except requests.RequestException as e:
print(f"Erro na requisição: {e}")
Essas práticas tornam seu scraper mais robusto e responsável.
Boas práticas e observações finais
Ao desenvolver scrapers, mantenha o código modular e legível. Separe as funções de requisição, parsing e armazenamento. Use nomes claros. Considere usar classes para encapsular o comportamento. Documente seu código. E, sempre que possível, reutilize código com funções.
Vamos montar o projeto completo com uma função principal:
import requests
from bs4 import BeautifulSoup
import json
import time
def obter_pagina(url):
try:
resposta = requests.get(url, headers={'User-Agent': 'MeuScraper/1.0'}, timeout=10)
resposta.raise_for_status()
return resposta.text
except requests.RequestException as e:
print(f"Erro ao acessar {url}: {e}")
return None
def extrair_livros(html):
soup = BeautifulSoup(html, 'html.parser')
livros = []
for artigo in soup.select('article.product_pod'):
titulo = artigo.h3.a['title']
preco = artigo.select_one('.price_color').text
avaliacao = artigo.select_one('.star-rating')['class'][1]
link = artigo.h3.a['href']
livros.append({'titulo': titulo, 'preco': preco, 'avaliacao': avaliacao, 'link': link})
return livros
def salvar_json(dados, arquivo):
with open(arquivo, 'w', encoding='utf-8') as f:
json.dump(dados, f, ensure_ascii=False, indent=2)
def main():
url = "https://books.toscrape.com/"
html = obter_pagina(url)
if html:
livros = extrair_livros(html)
salvar_json(livros, 'livros.json')
print(f"Extraídos {len(livros)} livros.")
if __name__ == "__main__":
main()
Esse código é um exemplo de scraper bem estruturado. Você pode expandi-lo para percorrer várias páginas, seguir links de detalhes, etc. Lembre-se de sempre respeitar os limites éticos.
Referências
- Documentação oficial do Python: urllib.request
- Documentação oficial da biblioteca requests
- Documentação oficial do BeautifulSoup
- MDN: Aprender HTML
- Sobre o robots.txt
- Documentação oficial do módulo json
- Documentação oficial do módulo csv
Exercícios
- Escreva um código usando
requestspara baixar a páginahttps://books.toscrape.com/e imprimir o título da página (a tag<title>). - Usando BeautifulSoup, extraia todos os preços dos livros da primeira página de
books.toscrape.come imprima a lista. - Modifique o scraper para percorrer as primeiras 3 páginas do site (alterando o parâmetro
?page=2, etc.) e imprima o número total de livros coletados. - Salve os dados extraídos (título, preço, avaliação) de todos os livros da primeira página em um arquivo CSV chamado
livros.csv. - Pesquise sobre o arquivo
robots.txtdo sitebooks.toscrape.com(acessehttps://books.toscrape.com/robots.txt) e explique se é permitido raspar todas as páginas. Escreva um pequeno comentário no código indicando sua conclusão.
import requests
from bs4 import BeautifulSoup
url = "https://books.toscrape.com/"
resposta = requests.get(url)
soup = BeautifulSoup(resposta.text, 'html.parser')
print(soup.title.text)
import requests
from bs4 import BeautifulSoup
url = "https://books.toscrape.com/"
resposta = requests.get(url)
soup = BeautifulSoup(resposta.text, 'html.parser')
precos = [p.text for p in soup.select('.price_color')]
print(precos)
import requests
from bs4 import BeautifulSoup
import time
total_livros = 0
for pagina in range(1, 4):
url = f"https://books.toscrape.com/catalogue/page-{pagina}.html"
resposta = requests.get(url)
soup = BeautifulSoup(resposta.text, 'html.parser')
livros = soup.select('article.product_pod')
total_livros += len(livros)
time.sleep(1) # pausa para não sobrecarregar
print(f"Total de livros: {total_livros}")
import requests
from bs4 import BeautifulSoup
import csv
url = "https://books.toscrape.com/"
resposta = requests.get(url)
soup = BeautifulSoup(resposta.text, 'html.parser')
livros = []
for artigo in soup.select('article.product_pod'):
titulo = artigo.h3.a['title']
preco = artigo.select_one('.price_color').text
avaliacao = artigo.select_one('.star-rating')['class'][1]
livros.append({'titulo': titulo, 'preco': preco, 'avaliacao': avaliacao})
with open('livros.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['titulo', 'preco', 'avaliacao'])
writer.writeheader()
writer.writerows(livros)
robots.txt de books.toscrape.com permite todas as rotas (não há disallow). Portanto, é permitido raspar o site. Comentário no código:# O robots.txt permite todas as rotas, então podemos raspar.