Fluxo de Execução do Framework
O Scrapy opera através de um mecanismo assíncrono coordenado por cinco componentes principais:
- O componente Spider gera requisições iniciais (objetos
Request) baseadas nas URLs definidas emstart_urls - O Scheduler enfileira essas requisições e as disponibiliza para o Engine conforme a capacidade de processamento
- O Dwonloader executa as requisições HTTP e encapsula as respostas em objetos
Response - O Spider recebe o
Responsee extrai dados estruturados ou novas URLs para rastreamento - O Item Pipeline processa os dados extraídos — validação, limpeza e persistência em storage
- URLs descobertas durante a análise retornam ao Scheduler, reiniciando o ciclo
Componentes da Arquitetura
| Componente | Responsabilidade Principal |
|---|---|
| Engine | Orchestração de eventos e sinais entre todos os módulos |
| Scheduler | Fila de prioridade para requisições pendentes |
| Downloader | Camada de rede: execução HTTP/HTTPS com gerenciamento de conexões |
| Spider | Lógica de extração: parsing de HTML/JSON e geração de itens |
| Item Pipeline | Pós-processamento: deduplicação, transformação e exportação |
| Middlewares | Interceptação de requisições/respostas para customização |
Configuração do Ambiente
# Instalação via pip
pip install scrapy
# Geração da estrutura de projeto
scrapy startproject coletor
Estrutura de diretórios gerada:
coletor/
├── coletor/
│ ├── __init__.py
│ ├── items.py # Definição de schemas de dados
│ ├── middlewares.py # Hooks de processamento
│ ├── pipelines.py # Fluxos de exportação
│ ├── settings.py # Configurações globais
│ └── spiders/ # Módulos de extração
│ └── __init__.py
└── scrapy.cfg # Configuração de deployment
Criação de Spider Básico
scrapy genspider noticias example.com
Implementação mínima em spiders/noticias.py:
import scrapy
class NoticiasSpider(scrapy.Spider):
identificador = "noticias" # nome único do spider
dominios_permitidos = ["example.com"]
urls_iniciais = ["https://example.com/noticias"]
def analisar(self, resposta):
# resposta.text: conteúdo HTML bruto
# resposta.css(): seletores CSS
# resposta.xpath(): expressões XPath
for artigo in resposta.css('article.noticia'):
yield {
'titulo': artigo.css('h2::text').get(),
'link': artigo.css('a::attr(href)').get()
}
Execução:
scrapy crawl noticias
Extração de Conteúdo
Métodos disponíveis no objeto Response:
resposta.css('seletor'): Retorna objetoSelectorList.get()/.extract_first(): Primeiro elemento ouNone.getall()/.extract(): Lista com todos os elementosresposta.xpath('//tag[@classe="valor"]'): Navegação via XPath
def analisar(self, resposta):
registros = resposta.xpath('//div[@class="lista"]/div')
for reg in registros:
yield {
'nome': reg.xpath('.//h3/text()').get(default=''),
'preco': reg.xpath('.//span[@class="valor"]/text()').get(),
'disponivel': bool(reg.xpath('.//button[not(@disabled)]'))
}
Modelagem de Dados com Items
Definição em items.py:
import scrapy
class ProdutoItem(scrapy.Item):
referencia = scrapy.Field() # SKU ou identificador
denominacao = scrapy.Field() # nome do produto
valor = scrapy.Field() # preço numérico
estoque = scrapy.Field() # quantidade disponível
Uso no spider:
from coletor.items import ProdutoItem
class LojaSpider(scrapy.Spider):
# ... configurações ...
def analisar(self, resposta):
for card in resposta.css('.produto-card'):
item = ProdutoItem()
item['referencia'] = card.css('::attr(data-sku)').get()
item['denominacao'] = card.css('.titulo::text').get().strip()
item['valor'] = self._extrair_preco(card.css('.preco::text').get())
item['estoque'] = card.css('.estoque::text').get()
yield item
def _extrair_preco(self, texto):
return float(texto.replace('R$', '').replace('.', '').replace(',', '.'))
Procesamento e Persistência
Pipeline para exportação JSON Lines:
import json
from itemadapter import ItemAdapter
class ExportacaoJsonPipeline:
def __init__(self):
self.arquivo = None
def open_spider(self, spider):
self.arquivo = open(f'{spider.name}_dados.jsonl', 'w', encoding='utf-8')
def close_spider(self, spider):
self.arquivo.close()
def process_item(self, item, spider):
linha = json.dumps(ItemAdapter(item).asdict(), ensure_ascii=False)
self.arquivo.write(linha + '\n')
return item # obrigatório: propagação para próximo pipeline
Ativação em settings.py:
ITEM_PIPELINES = {
'coletor.pipelines.ExportacaoJsonPipeline': 100,
'coletor.pipelines.ValidacaoPipeline': 200,
}
Valores menores indicam prioridade de execução mais alta.
Rastreamento Automático com CrawlSpider
Para sites com estrutura hierárquica previsível:
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class RastreadorSpider(CrawlSpider):
name = 'rastreador'
start_urls = ['https://example.com/categorias']
regras = (
# Segue paginação
Rule(
LinkExtractor(allow=r'/categorias\?pagina=\d+'),
follow=True
),
# Extrai detalhes de produtos
Rule(
LinkExtractor(allow=r'/produto/\d+'),
callback='extrair_detalhes',
follow=False
),
)
def extrair_detalhes(self, resposta):
yield {
'sku': resposta.css('span.sku::text').get(),
'descricao': resposta.css('meta[name="description"]::attr(content)').get()
}
Restrição importante: não sobrescrever o método parse() em CrawlSpider, pois é utilizado internamente para processamento de regras.
Middlewares para Customização
Tipos e aplicações típicas:
- Downloader Middleware: rotação de proxies, manipulação de headers, retry automático, delay adaptativo
- Spider Middleware: tratamento de exceções em parsing, modificação de itens antes do pipeline
Exemplo de rotação de User-Agent:
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random
class RotacaoUserAgentMiddleware(UserAgentMiddleware):
agentes = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.0',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)',
]
def process_request(self, request, spider):
request.headers['User-Agent'] = random.choice(self.agentes)
Registro em settings.py:
DOWNLOADER_MIDDLEWARES = {
'coletor.middlewares.RotacaoUserAgentMiddleware': 400,
}