Python Admin

O que Todo Dev Deve Saber sobre Web Scraping em Python: requests, BeautifulSoup e Selenium Já leu

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 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: para fazer requisições HTTP, para parsear HTML e para automação de navegadores. Fundamentos: Requests e BeautifulSoup para Sites Estáticos Como Funciona o Requests O é a biblioteca

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.

Referências


Artigos relacionados