$ python modulo_2.py

Web scraping

Extrair dados de páginas HTML de forma ética, robusta e reutilizável. Requests + BeautifulSoup são a base de 80% das automações de coleta.

O que você vai aprender

1. Instalação

pip install requests beautifulsoup4 lxml

2. Requisição básica e boa prática

import requests
from bs4 import BeautifulSoup
import time

HEADERS = {
    "User-Agent": "Mozilla/5.0 (compatible; IRNBot/1.0; +https://irndevs.com)",
    "Accept-Language": "pt-BR,pt;q=0.9",
}

def get_html(url: str, tentativas: int = 3) -> str | None:
    for i in range(tentativas):
        try:
            r = requests.get(url, headers=HEADERS, timeout=15)
            r.raise_for_status()
            r.encoding = r.apparent_encoding
            return r.text
        except requests.RequestException as e:
            print(f"Tentativa {i+1} falhou: {e}")
            time.sleep(2 ** i)
    return None
DICA Sempre envie um User-Agent identificável. Em produção, respect robots.txt e adicione delays entre requisições.

3. Parsing com BeautifulSoup

html = get_html("https://exemplo.com/lista")
soup = BeautifulSoup(html, "lxml")

# CSS selector (preferido)
titulos = [el.get_text(strip=True) for el in soup.select("h2.titulo")]

# find / find_all
links = soup.find_all("a", class_="item")
for a in links:
    href = a.get("href")
    texto = a.get_text(strip=True)
    print(texto, href)

4. Exemplo completo: coletar lista de posts

scraper_posts.py
#!/usr/bin/env python3
import json
from pathlib import Path
from bs4 import BeautifulSoup
import requests
import time

HEADERS = {"User-Agent": "IRNBot/1.0 (+https://irndevs.com)"}
BASE = "https://quotes.toscrape.com"

def scrape_pagina(num: int) -> list[dict]:
    url = f"{BASE}/page/{num}/"
    r = requests.get(url, headers=HEADERS, timeout=15)
    r.raise_for_status()
    soup = BeautifulSoup(r.text, "lxml")
    itens = []
    for q in soup.select("div.quote"):
        itens.append({
            "texto": q.select_one("span.text").get_text(strip=True),
            "autor": q.select_one("small.author").get_text(strip=True),
            "tags": [t.get_text(strip=True) for t in q.select("a.tag")],
        })
    return itens

def main():
    todos = []
    for pagina in range(1, 4):  # primeiras 3 páginas
        print(f"Página {pagina}...")
        todos.extend(scrape_pagina(pagina))
        time.sleep(1.5)  # educado

    Path("quotes.json").write_text(
        json.dumps(todos, ensure_ascii=False, indent=2),
        encoding="utf-8"
    )
    print(f"Salvos {len(todos)} itens em quotes.json")

if __name__ == "__main__":
    main()

5. Paginação e parada inteligente

Muitos sites não têm número fixo de páginas. Detecte o botão “próxima” ou o fim da lista.

proxima = soup.select_one("li.next a")
if proxima:
    url_proxima = BASE + proxima["href"]
else:
    print("Última página alcançada")
ÉTICA E LEGAL Scraping agressivo pode violar termos de uso. Prefira APIs oficiais quando existirem. Nunca sobrecarregue o servidor (use delays e rate limits).

6. Salvar em CSV

import csv

with open("quotes.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=["texto", "autor", "tags"])
    w.writeheader()
    for item in todos:
        w.writerow({
            "texto": item["texto"],
            "autor": item["autor"],
            "tags": ",".join(item["tags"]),
        })
EXERCÍCIO 1. Adapte o scraper para um site de sua escolha (preços, notícias ou jobs).
2. Implemente retry com backoff exponencial.
3. Salve tanto JSON quanto CSV.
4. Adicione um campo coletado_em com datetime ISO.

Sumário do curso

Precisa de algo assim, sob medida pro seu negócio?

agendar diagnóstico gratuito →