<h2>O que é Web Scraping e Por Que Aprender</h2>
<p>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.</p>
<p>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 <code>robots.txt</code> 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: <code>requests</code> para fazer requisições HTTP, <code>BeautifulSoup</code> para parsear HTML e <code>Selenium</code> para automação de navegadores.</p>
<h2>Fundamentos: Requests e BeautifulSoup para Sites Estáticos</h2>
<h3>Como Funciona o Requests</h3>
<p>O <code>requests</code> é 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 <code>requests</code> faz exatamente isso.</p>
<pre><code class="language-python">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}")</code></pre>
<p>O <code>status_code</code> 200 significa sucesso. Códigos 4xx indicam erros no cliente (URL inválida, acesso negado), e 5xx indicam problemas no servidor. O <code>response.text</code> contém o HTML completo da página em forma de string.</p>
<h3>Parseando HTML com BeautifulSoup</h3>
<p>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.</p>
<pre><code class="language-python">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")</code></pre>
<p>Neste exemplo, usamos <code>find_all()</code> para obter todos os elementos que correspondem ao seletor CSS, depois <code>find()</code> para localizar elementos específicos dentro deles. O método <code>get_text()</code> extrai apenas o texto, removendo tags HTML. Este é o padrão: navegar a árvore DOM, localizar elementos e extrair dados.</p>
<h3>Seletores CSS vs. Métodos Find</h3>
<p>Você tem duas abordagens com BeautifulSoup. A primeira é usar <code>find()</code> e <code>find_all()</code> com parâmetros específicos (tag, class, id). A segunda é usar seletores CSS com <code>select()</code>, que é mais poderosa quando você tem seletores complexos.</p>
<pre><code class="language-python">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())</code></pre>
<p>Use <code>select()</code> quando seus seletores são complexos ou quando você já está familiarizado com CSS. Use <code>find()</code> e <code>find_all()</code> para casos simples e quando a legibilidade é prioritária.</p>
<h2>Automação com Selenium para Sites Dinâmicos</h2>
<h3>Por Que Selenium é Necessário</h3>
<p>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 <code>requests</code>, 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.</p>
<p>A desvantagem é que Selenium é muito mais lento que requests + BeautifulSoup. Use-o apenas quando necessário, quando o conteúdo é carregado dinamicamente via JavaScript.</p>
<pre><code class="language-python">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</code></pre>
<p>A combinação Selenium + BeautifulSoup é poderosa: o Selenium espera e executa tudo, depois você usa BeautifulSoup para fazer o parsing. O <code>WebDriverWait</code> é crucial — ele espera o elemento aparecer antes de prosseguir, evitando erros de "elemento não encontrado".</p>
<h3>Interações Avançadas com Selenium</h3>
<p>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.</p>
<pre><code class="language-python">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()</code></pre>
<p>Note que usamos <code>time.sleep()</code> para aguardar elementos carregarem. Embora funcione, <code>WebDriverWait</code> é mais robusto porque espera até que a condição seja atendida, com timeout automático. Sempre use <code>WebDriverWait</code> em produção; <code>sleep()</code> é frágil e lento.</p>
<h2>Boas Práticas e Tratamento de Erros</h2>
<h3>Gerenciando Requisições Responsavelmente</h3>
<p>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.</p>
<pre><code class="language-python">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}")</code></pre>
<p>Este código implementa tratamento de erros robusto: timeout, erros HTTP, e exceções genéricas. O <code>headers</code> com User-Agent é simples mas eficaz. O delay <code>time.sleep(1)</code> respeita o servidor. Em produção, você pode aumentar o delay ou usar <code>requests.Session()</code> para reutilizar conexões e ser mais eficiente.</p>
<h3>Salvando Dados Estruturados</h3>
<p>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.</p>
<pre><code class="language-python">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")</code></pre>
<p>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.</p>
<h2>Conclusão</h2>
<p>Você aprendeu as três ferramentas fundamentais para web scraping em Python: <strong>requests</strong> para fazer requisições HTTP de forma simples e eficiente, <strong>BeautifulSoup</strong> para parsear e navegar HTML/XML, e <strong>Selenium</strong> 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 <code>robots.txt</code>, 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://requests.readthedocs.io/" target="_blank" rel="noopener noreferrer">Documentação Oficial - Requests</a></li>
<li><a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/" target="_blank" rel="noopener noreferrer">Documentação Oficial - BeautifulSoup</a></li>
<li><a href="https://selenium-python.readthedocs.io/" target="_blank" rel="noopener noreferrer">Documentação Oficial - Selenium Python</a></li>
<li><a href="https://quotes.toscrape.com/" target="_blank" rel="noopener noreferrer">Quotes to Scrape - Site de Prática</a></li>
<li><a href="https://realpython.com/web-scraping-python/" target="_blank" rel="noopener noreferrer">Real Python - Web Scraping Best Practices</a></li>
</ul>
<p><!-- FIM --></p>