O que é Web Scraping e Por Que Aprender
Web scraping é a técnica de extrair dados estruturados de páginas da web de forma automatizada. Em essência, você está programando um "robô" que acessa URLs, interpreta o HTML/CSS e extrai as informações que precisam. É uma habilidade crítica quando você precisa coletar dados em massa para análise, monitoramento de preços, pesquisa de mercado ou agregação de conteúdo.
A diferença entre web scraping e usar uma API é simples: nem todo site oferece uma API pública. Quando você enfrenta essa situação e precisa dos dados, scraping é a solução. Porém, sempre respeite o arquivo robots.txt do site e os termos de serviço — há uma linha ética que não deve ser ultrapassada. Neste artigo, você dominará as três ferramentas mais poderosas do Python para isso: requests para fazer requisições HTTP, BeautifulSoup para parsear HTML e Selenium para automação de navegadores.
Fundamentos: Requests e BeautifulSoup para Sites Estáticos
Como Funciona o Requests
O requests é a biblioteca padrão para fazer requisições HTTP em Python. Ele abstrai a complexidade do protocolo HTTP e oferece uma interface simples e intuitiva. Quando você acessa um site no navegador, basicamente está enviando uma requisição GET para um servidor. O requests faz exatamente isso.
import requests
from bs4 import BeautifulSoup
# Fazer uma requisição GET simples
url = "https://quotes.toscrape.com/"
response = requests.get(url)
# Verificar se a requisição foi bem-sucedida
if response.status_code == 200:
print("Requisição bem-sucedida!")
print(f"Tamanho do conteúdo: {len(response.text)} caracteres")
else:
print(f"Erro: {response.status_code}")
O status_code 200 significa sucesso. Códigos 4xx indicam erros no cliente (URL inválida, acesso negado), e 5xx indicam problemas no servidor. O response.text contém o HTML completo da página em forma de string.
Parseando HTML com BeautifulSoup
BeautifulSoup transforma HTML em bruto em uma árvore de objetos Python que você pode navegar e consultar facilmente. Não é suficiente apenas baixar o HTML — você precisa extrair os dados relevantes de forma estruturada.
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# Encontrar todos os elementos com classe 'quote'
quotes = soup.find_all('div', class_='quote')
for quote in quotes:
# Extrair o texto da citação
text = quote.find('span', class_='text').get_text()
# Extrair o autor
author = quote.find('small', class_='author').get_text()
print(f"{text}\n— {author}\n")
Neste exemplo, usamos find_all() para obter todos os elementos que correspondem ao seletor CSS, depois find() para localizar elementos específicos dentro deles. O método get_text() extrai apenas o texto, removendo tags HTML. Este é o padrão: navegar a árvore DOM, localizar elementos e extrair dados.
Seletores CSS vs. Métodos Find
Você tem duas abordagens com BeautifulSoup. A primeira é usar find() e find_all() com parâmetros específicos (tag, class, id). A segunda é usar seletores CSS com select(), que é mais poderosa quando você tem seletores complexos.
from bs4 import BeautifulSoup
html = """
<div class="container">
<article class="post featured">
<h2>Título 1</h2>
</article>
<article class="post">
<h2>Título 2</h2>
</article>
</div>
"""
soup = BeautifulSoup(html, 'html.parser')
# Abordagem 1: find_all com parâmetros
posts = soup.find_all('article', class_='post')
# Abordagem 2: select com CSS (mais poderosa)
featured = soup.select('article.post.featured h2')
print(featured[0].get_text()) # "Título 1"
# Seletores CSS complexos
titles = soup.select('div.container article.post > h2')
for title in titles:
print(title.get_text())
Use select() quando seus seletores são complexos ou quando você já está familiarizado com CSS. Use find() e find_all() para casos simples e quando a legibilidade é prioritária.
Automação com Selenium para Sites Dinâmicos
Por Que Selenium é Necessário
Nem todo conteúdo de um site é servido no HTML inicial. Muitos sites modernos carregam dados via JavaScript após o carregamento da página. Quando você usa requests, recebe apenas o HTML bruto — sem executar JavaScript. O Selenium resolve isso controlando um navegador real (Chrome, Firefox) que executa JavaScript e renderiza a página completamente.
A desvantagem é que Selenium é muito mais lento que requests + BeautifulSoup. Use-o apenas quando necessário, quando o conteúdo é carregado dinamicamente via JavaScript.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
# Inicializar o driver (certifique-se de ter o chromedriver instalado)
driver = webdriver.Chrome()
try:
# Navegar para a URL
driver.get("https://quotes.toscrape.com/js/")
# Esperar até 10 segundos por um elemento aparecer
element = WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.CLASS_NAME, "quote"))
)
# Agora o JavaScript foi executado, parse com BeautifulSoup
soup = BeautifulSoup(driver.page_source, 'html.parser')
quotes = soup.find_all('div', class_='quote')
for quote in quotes:
text = quote.find('span', class_='text').get_text()
print(text)
finally:
driver.quit() # Sempre fechar o driver
A combinação Selenium + BeautifulSoup é poderosa: o Selenium espera e executa tudo, depois você usa BeautifulSoup para fazer o parsing. O WebDriverWait é crucial — ele espera o elemento aparecer antes de prosseguir, evitando erros de "elemento não encontrado".
Interações Avançadas com Selenium
Selenium não apenas carrega páginas — você pode clicar em botões, preencher formulários, scrollar, enviar tecladas. Isso é essencial quando o site requer autenticação, paginação via cliques, ou quando o conteúdo é carregado sob demanda.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
driver = webdriver.Chrome()
try:
driver.get("https://quotes.toscrape.com/")
# Scrollar para baixo
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
# Clicar em um elemento
next_button = driver.find_element(By.CSS_SELECTOR, "li.next a")
next_button.click()
time.sleep(2)
# Preencher um campo de formulário (exemplo hipotético)
# search_box = driver.find_element(By.NAME, "search")
# search_box.send_keys("Python")
# search_box.send_keys(Keys.RETURN)
# Esperar elemento e extrair texto
wait = WebDriverWait(driver, 10)
quote = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "quote")))
print(quote.text)
finally:
driver.quit()
Note que usamos time.sleep() para aguardar elementos carregarem. Embora funcione, WebDriverWait é mais robusto porque espera até que a condição seja atendida, com timeout automático. Sempre use WebDriverWait em produção; sleep() é frágil e lento.
Boas Práticas e Tratamento de Erros
Gerenciando Requisições Responsavelmente
Ao fazer scraping em larga escala, você pode ser bloqueado pelo servidor se fazer muitas requisições muito rápido. Headers customizados (especialmente User-Agent) fazem seu scraper parecer um navegador real, e adicionar delays entre requisições é educado e eficaz.
import requests
import time
from bs4 import BeautifulSoup
# Headers para parecer um navegador real
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
urls = [
"https://quotes.toscrape.com/page/1/",
"https://quotes.toscrape.com/page/2/",
"https://quotes.toscrape.com/page/3/"
]
for url in urls:
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # Lança exceção se status_code >= 400
soup = BeautifulSoup(response.text, 'html.parser')
quotes = soup.find_all('div', class_='quote')
print(f"Encontradas {len(quotes)} citações em {url}")
# Delay entre requisições (1 segundo)
time.sleep(1)
except requests.exceptions.Timeout:
print(f"Timeout ao acessar {url}")
except requests.exceptions.HTTPError as e:
print(f"Erro HTTP {response.status_code}: {url}")
except Exception as e:
print(f"Erro inesperado: {e}")
Este código implementa tratamento de erros robusto: timeout, erros HTTP, e exceções genéricas. O headers com User-Agent é simples mas eficaz. O delay time.sleep(1) respeita o servidor. Em produção, você pode aumentar o delay ou usar requests.Session() para reutilizar conexões e ser mais eficiente.
Salvando Dados Estruturados
Não há ponto em raspar dados se você não os salvar estruturadamente. CSV e JSON são formatos comuns e facilmente importáveis em ferramentas de análise.
import requests
from bs4 import BeautifulSoup
import json
import csv
url = "https://quotes.toscrape.com/"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
quotes = soup.find_all('div', class_='quote')
# Estruturar dados como lista de dicionários
data = []
for quote in quotes:
text = quote.find('span', class_='text').get_text()
author = quote.find('small', class_='author').get_text()
tags = [tag.get_text() for tag in quote.find_all('a', class_='tag')]
data.append({
'text': text,
'author': author,
'tags': tags
})
# Salvar como JSON
with open('quotes.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# Salvar como CSV
with open('quotes.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['text', 'author', 'tags'])
writer.writeheader()
for item in data:
item['tags'] = ', '.join(item['tags']) # CSV não suporta listas nativamente
writer.writerow(item)
print(f"Salvos {len(data)} registros")
Estruturar dados como dicionários Python torna trivial exportar para qualquer formato. JSON é ótimo para dados hierárquicos; CSV é melhor para importação em Excel/Pandas. Escolha baseado em seus casos de uso.
Conclusão
Você aprendeu as três ferramentas fundamentais para web scraping em Python: requests para fazer requisições HTTP de forma simples e eficiente, BeautifulSoup para parsear e navegar HTML/XML, e Selenium para automação quando JavaScript está envolvido. A chave é saber qual ferramenta usar para cada situação — requests é rápido para conteúdo estático, Selenium é necessário para conteúdo dinâmico. Igualmente importante é usar esses poderes responsavelmente: respeitar robots.txt, adicionar delays entre requisições, e sempre verificar os termos de serviço antes de raspar um site. Com essas três habilidades, você está equipado para extrair dados de praticamente qualquer site.