Como Construir um Pipeline de Web Scraping Incremental com Scrapeless
Senior Web Scraping Engineer
TL;DR:
- Um pedido condicional transforma uma página inalterada em um
304com um corpo de 0 bytes — a resposta completa foi de 50.388 bytes, e dez verificações condicionais foram executadas em 4,21 s contra 5,52 s para dez buscas completas. - A verificação de conteúdo detecta mudanças, mas não economiza largura de banda: a segunda busca de uma página sem validador ainda transferiu 11.064 bytes antes que o hash pudesse ser comparado.
- Impressões digitais por registro são o que reduzem a escrita. Um preço movido fez uma página de 20 registros ser reduzida para 1 linha escrita a montante.
- Fingerprint os campos que você se importa em vez de todo o registro, ou um campo não relacionado mudando marca tudo como sujo.
- Validadores HTTP descrevem o documento que o servidor enviou, então eles deixam de ser úteis no momento em que os registros chegam através da renderização do lado do cliente.
- Um
304só está disponível se a execução anterior armazenou o validador, o que faz da tabela de estado parte do pipeline em vez de uma otimização. - Coloque uma rastreamento incremental em páginas renderizadas com o plano gratuito Scrapeless.
A maioria dos scrapers programados baixa tudo novamente, reinterpreta tudo e reescreve cada linha, então relata sucesso. Isso funciona até que o catálogo cresça, momento em que o trabalho diário gasta quase todo seu tempo confirmando que os dados de ontem ainda são os dados de ontem.
A raspagem incremental é o arranjo oposto: pergunte o que mudou e faça o trabalho apenas onde a resposta for sim. A pergunta pode ser feita em três níveis, e cada um economiza algo diferente — largura de banda, análise ou escritas. Cada camada abaixo foi medida contra a mesma página de catálogo ao vivo, então as trocas vêm com números em vez de adjetivos.
Pipeline à Vista
| Camada | Pergunta | Mecanismo | Resultado medido |
|---|---|---|---|
| HTTP | O documento mudou? | If-None-Match / If-Modified-Since |
304, 0 bytes vs 50.388 |
| Documento | Os bytes mudaram? | SHA-256 do corpo | detectado, mas 11.064 bytes ainda foram transferidos |
| Registro | Quais linhas mudaram? | impressão digital de campo por registro | 1 das 20 linhas escritas |
O fluxo é verificar → buscar apenas se mudado → extrair → comparar impressões digitais → escrever apenas as diferenças. As camadas se empilham: a verificação HTTP é a mais barata e menos frequentemente disponível, a verificação de registros está sempre disponível e custa uma busca completa.
Verificar com menos frequência é a outra metade do mesmo problema. O Protocolo de Exclusão de Robôs é onde um site declara o que deseja que seja rastreado, e um design incremental é o que permite que um scraper respeite uma cadência mais lenta sem ficar para trás — cada verificação custa uma viagem de ida e volta em vez de uma transferência completa.
Camada 1: Deixe o Servidor Responder
Um validador HTTP é a própria opinião do servidor sobre se sua representação mudou. Existem dois: ETag, um token de versão opaco, e Last-Modified, um carimbo de data/hora. A primeira resposta os transporta.
python
import requests
URL = "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html"
first = requests.get(URL, timeout=30)
first.raise_for_status()
etag = first.headers["ETag"]
print(f"first GET {first.status_code} {len(first.content)} bytes ETag={etag}")
second = requests.get(URL, headers={"If-None-Match": etag}, timeout=30)
print(f"second GET {second.status_code} {len(second.content)} bytes")
print(f"bytes avoided: {len(first.content) - len(second.content)}")
text
first GET 200 50388 bytes ETag=W/"63e40de8-c4d4"
second GET 304 0 bytes
bytes avoided: 50388
O 304 não carrega corpo algum. O mecanismo de pedido condicional é definido de forma que o cliente mantenha a cópia que já possui, o que significa que o scraper precisa ter mantido uma.
Last-Modified funciona da mesma forma através de um cabeçalho diferente:
python
third = requests.get(URL, headers={"If-Modified-Since": first.headers["Last-Modified"]}, timeout=30)
print(first.headers["Last-Modified"], "->", third.status_code, len(third.content), "bytes")
text
Wed, 08 Feb 2023 21:02:32 GMT -> 304 0 bytes
Prefira o ETag quando ambos estiverem presentes. A especificação de semântica HTTP faz do Last-Modified um carimbo de data/hora de resolução de um segundo, então duas mudanças dentro do mesmo segundo são indistinguíveis, enquanto um tag de entidade é livre para mudar a qualquer edição.
A economia é real, mas não é toda a execução:
text
10 conditional GETs 4.21s
10 full GETs 5.52s
A verificação condicional foi 1,31 vezes mais rápida e deixou 492 KB não transferidos. A solicitação ainda custa uma viagem de ida e volta — o que desaparece é o corpo, não a conexão.
Camada 2: Faça o Hash do Documento Quando o Servidor Não Diz Nada
Muitos alvos não enviam nenhum validador. Então, a única maneira de saber se o documento mudou é buscá-lo e olhar. Um digest criptográfico é a ferramenta padrão para essa comparação — o padrão SHA-256 fornece um valor de largura fixa onde qualquer diferença de um único byte produz um digest não relacionado, então a igualdade é um sinal confiável de "nada se moveu".
python
import hashlib
import requests
q1 = requests.get("https://quotes.toscrape.com/", timeout=30)
q1.raise_for_status()
print("ETag:", q1.headers.get("ETag"), " Last-Modified:", q1.headers.get("Last-Modified"))
q2 = requests.get("https://quotes.toscrape.com/", timeout=30)
h1 = hashlib.sha256(q1.content).hexdigest()
h2 = hashlib.sha256(q2.content).hexdigest()
print(f"sha256 run 1: {h1[:16]}...")
print(f"sha256 run 2: {h2[:16]}...")
print(f"unchanged: {h1 == h2} bytes still transferred: {len(q2.content)}")
text
ETag: None Last-Modified: None
sha256 run 1: efdc2605a2062dce...
sha256 run 2: efdc2605a2062dce...
unchanged: True bytes still transferred: 11064
Observe o que esta camada faz e não compra. O hash reporta corretamente sem mudança, e 11.064 bytes cruzaram a rede de qualquer forma. O hash de documentos economiza análise, escritas em banco de dados, alertas a montante e qualquer re-embutimento — nunca largura de banda.
Também possui um problema de falso positivo, em que os números aqui não aparecem. Páginas que contêm um token de sessão, um anúncio rotativo ou um timestamp renderizado produzem um hash diferente a cada busca, enquanto os dados permanecem idênticos. Hashing os registros extraídos em vez do corpo bruto é o que torna o sinal estável, que é a próxima camada.
Camada 3: Impressão Digital dos Registros
As camadas acima respondem perguntas sobre um documento. O que um pipeline geralmente precisa saber é quais linhas escrever.
python
import json, hashlib
def fingerprint(record):
payload = json.dumps({k: record[k] for k in ("price", "rating")}, sort_keys=True)
return hashlib.sha256(payload.encode()).hexdigest()[:16]
Imprimir digitalmente um subconjunto escolhido de campos em vez do registro inteiro é a decisão que faz isso funcionar. Inclua um campo que muda por conta própria — uma contagem de visualizações, um timestamp de "última visualização", uma posição em uma lista classificada — e cada registro parecerá sujo em cada execução.
Armazene uma impressão digital por registro, depois compare a próxima raspagem contra ela:
python
known = {title: (fp, price) for title, fp, price in conn.execute("SELECT title, fp, price FROM snap")}
new, changed, same = [], [], 0
for record in second_pass:
previous = known.get(record["title"])
if previous is None:
new.append(record)
elif previous[0] != fingerprint(record):
changed.append((record["title"], previous[1], record["price"]))
else:
same += 1
Contra a página ativa de 20 registros com um preço alterado para representar um movimento real:
text
parsed 20 records from the live page
stored baseline: 20 fingerprints
example fingerprint: 'Sharp Objects' -> e974692e9d935048
unchanged 19 | changed 1 | new 0
CHANGED A Murder in Time £16.64 -> £99.99
rows written downstream: 1 of 20
Uma linha escrita em vez de vinte. Em um catálogo onde a realidade diária é que quase nada se movimenta, essa proporção é todo o argumento para a impressão digital: o volume de gravação rastreia a taxa de mudança em vez do tamanho do catálogo.
Manter o valor anterior ao lado da impressão digital é o que transforma a detecção em um evento utilizável. £16.64 -> £99.99 é um registro de alteração de preço; uma flag suja é apenas uma dica de que algo aconteceu.
Executando uma raspagem incremental contra páginas que renderizam do lado do cliente? O plano gratuito do Scrapeless cobre solicitações suficientes para construir a linha de base e as primeiras diferenças.
Onde a Camada HTTP Para de Aplicar
Um validador descreve o documento enviado pelo servidor. Quando os registros chegam por meio da renderização do lado do cliente, esse documento é a shell da aplicação, e sua ETag rastreia o deployment da shell em vez do catálogo.
A consequência é específica: uma página pode retornar 304 enquanto os preços por trás dela se moveram, porque a shell realmente não mudou. Qualquer alvo cujo conteúdo é montado no navegador deve ser verificado no nível do registro, usando a API Universal Scraping para renderizar antes de comparar.
O mesmo se aplica a um endpoint JSON interno que a página chama. Esses frequentemente enviam validadores, e quando o fazem, a camada superior funciona novamente contra a carga útil que realmente contém os registros.
Armazenando o Estado
Nada disso funciona sem um local para manter o que a última execução aprendeu. O estado que um pipeline precisa é pequeno:
| Coluna | Propósito |
|---|---|
url |
o que foi verificado |
etag / last_modified |
reproduzido como o cabeçalho condicional |
body_sha256 |
comparação em nível de documento quando nenhum validador existe |
checked_at |
quando a resposta foi confirmada pela última vez |
Por registro, a tabela mantém a chave, a impressão digital e quaisquer valores anteriores que você deseja relatar. Ambos se encaixam ao lado dos dados raspados no mesmo banco de dados, e a forma do pipeline ETL permanece inalterada — um passo de verificação é adicionado antes da extração.
Uma nota operacional que é fácil de perder: um ETag é válido apenas para a URL que o emitiu. Repetir uma tag armazenada contra uma string de consulta diferente ou uma variante paginada produzirá um 200 e um corpo completo, que é um comportamento correto em vez de um erro.
Conclusão
A raspagem incremental é três perguntas, e saber qual delas você está fazendo decide o que você salva. O validador HTTP salva o corpo — 50.388 bytes tornaram-se um 304 de 0 bytes. O hash do documento salva a análise e gravações, mas nunca largura de banda, já que os 11.064 bytes chegam antes da comparação. A impressão digital do registro salva a gravação e levou uma página de 20 registros a uma única linha.
Comece com o validador quando o servidor oferecer um, volte a hash os registros extraídos em vez do corpo bruto, e imprima digitalmente apenas os campos cujo movimento você realmente se importa. Preços lista quanto custam as raspagens restantes uma vez que as inalteradas parem de ocorrer.
Pronto para parar de raspar páginas que não se moveram? Comece com o plano gratuito do Scrapeless e construa a linha de base contra a qual sua próxima execução compara.
FAQ
Q: O que é raspagem de dados incremental?
Raspar apenas o que mudou desde a última execução, em vez de recolher todo o alvo novamente. Isso é implementado como uma verificação que é executada antes da recuperação ou antes da gravação: uma solicitação HTTP condicional, uma comparação de hash de documento ou uma comparação de impressão digital por registro. O efeito medido aqui foi de 50.388 bytes evitados na camada HTTP e 19 de 20 linhas puladas na camada de registro.
Q: Como uso ETag em um raspador?
Armazene o cabeçalho ETag da resposta e, em seguida, envie-o de volta como If-None-Match na próxima solicitação para o mesmo URL. Um servidor que concorda que nada mudou responde 304 com um corpo vazio, que é o sinal para pular o restante do pipeline. A tag está vinculada ao URL exato, então uma tag armazenada reproduzida contra uma string de consulta diferente retorna um 200 normal.
Q: E se um site não enviar ETag ou Last-Modified?
Busque e compare hashes em vez disso. Faça o hash dos registros extraídos em vez do HTML bruto — um hash de corpo bruto muda quando um token de sessão, anúncio ou timestamp renderizado muda, o que marca a página como suja enquanto os dados são idênticos. Isso custa largura de banda de qualquer forma; a economia está na análise, gravação e qualquer coisa a jusante.
Q: Devo fazer hash do registro inteiro ou de campos específicos?
Campos específicos. Um hash de registro inteiro inclui qualquer coisa que a página carregue, então uma posição de classificação ou um contador de "última visualização" faz com que cada registro pareça mudado em cada execução. Fazer a impressão digital apenas dos campos cujo movimento importa é o que produziu o resultado estável de 19 inalterados acima.
Q: Uma página pode retornar 304 enquanto seus dados realmente mudaram?
Sim, em páginas renderizadas pelo cliente. O validador descreve o documento HTML que o servidor enviou, que para uma aplicação de página única é a estrutura em vez dos registros, então a tag rastreia as implantações da estrutura. Esses alvos precisam de comparação na camada de registro após a renderização, ou contra o endpoint JSON interno que carrega os dados.
Q: Quanto a raspagem incremental realmente economiza?
Depende de qual camada responde. Na medição acima, solicitações condicionais removeram completamente o corpo da resposta e rodaram 1,31 vezes mais rápido em dez verificações, e a camada de registro cortou gravações em 95% em uma página onde um de vinte itens se moveu. A viagem de ida e volta permanece em todos os casos, então a economia escala com o tamanho da página e a taxa de mudança, em vez de com a contagem de solicitações.
Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.



