Automatizar o rastreamento de rankings parece ser um cron job embrulhado em torno de uma chamada de API, e na primeira semana é isso mesmo. Os problemas surgem depois: duas execuções que não são comparáveis porque uma saiu em um horário diferente, um dia faltando na série que ninguém percebe até que um gráfico pareça errado, um alerta que dispara por causa de ruído, e um cliente perguntando por que o seu número diverge do dele.
Nenhum desses é um problema de coleta. São problemas de pipeline, e vale a pena projetar para eles antes de você ter seis meses de dados cheios de buracos. Aqui está a construção.
Defina primeiro o contrato de medição
Antes de escrever qualquer código, fixe as variáveis que tornam duas medições comparáveis, porque um ranking não tem significado sem elas: a localização, o dispositivo, o idioma e se a busca é personalizada. Essas escolhas se tornam constantes no seu job, não opções por execução.
O motivo é que qualquer variação nelas aparece nos seus dados como uma movimentação de ranking que nunca aconteceu de fato. Uma palavra-chave medida a partir de uma cidade na segunda-feira e de outra na terça parecerá ter se movido. O raciocínio completo está em measuring accurate keyword rankings, e a versão resumida é que a consistência importa mais do que a precisão absoluta.
Escreva o contrato como um schema, porque é também o que você vai entregar a um cliente quando ele perguntar o que seus números significam:
# one row per keyword per market: this is the unit of measurement
TARGET = {
"keyword": "residential proxies",
"country": "de",
"city": "berlin", # optional, only where local intent matters
"language": "de",
"device": "desktop",
}
Colete por meio de uma SERP API
Você pode construir a camada de coleta você mesmo sobre proxies, ou chamar uma API que retorna resultados já processados. Para um job de monitoramento diário, o caminho da API remove os dois encargos de manutenção que de fato consomem tempo: acompanhar mudanças no layout das páginas de resultados e manter a infraestrutura de coleta em funcionamento.
Uma SERP API recebe os parâmetros acima e devolve resultados estruturados, de modo que seu job se torna uma requisição e uma gravação, em vez de uma coleta e um parser:
import requests, datetime as dt
def fetch_serp(t):
r = requests.get("https://serp.shifter.io/v1", timeout=45, params={
"api_key": API_KEY,
"q": t["keyword"],
"gl": t["country"], # market
"hl": t["language"], # interface language
"location": t.get("city"),
"device": t["device"],
})
r.raise_for_status()
return r.json()
Verifique os nomes de parâmetros atuais na documentação da API em vez de confiar em um post de blog, já que eles evoluem. A alternativa, executar a coleta você mesmo sobre saídas residenciais, é abordada em why accurate rank tracking requires residential proxies, e a compensação é controle versus manutenção.
Armazene o resultado inteiro, não só a sua posição
O erro de projeto mais comum, e o mais caro de reverter depois, é armazenar um único número por palavra-chave por dia.
Armazene a lista completa de URLs ranqueadas, a presença e a posição de recursos do resultado, e o payload bruto. Três razões. As movimentações dos seus concorrentes são o contexto que torna a sua própria movimentação interpretável. Mudanças em recursos, como um resumo de IA aparecendo acima da dobra, mudam o valor de uma posição sem mudar o número. E você não pode coletar retroativamente uma SERP da terça-feira passada, então tudo o que você não armazenou está perdido.
CREATE TABLE serp_snapshot (
id BIGSERIAL PRIMARY KEY,
keyword TEXT NOT NULL,
country TEXT NOT NULL,
city TEXT,
device TEXT NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
results JSONB NOT NULL, -- full ranked list
features JSONB NOT NULL, -- ai overview, local pack, shopping
raw JSONB -- keep it, storage is cheaper than regret
);
CREATE INDEX ON serp_snapshot (keyword, country, device, captured_at DESC);
As posições são então derivadas dos snapshots em vez de armazenadas como o registro primário, o que significa que uma correção de parsing pode ser reaplicada ao histórico, e não apenas às execuções futuras.
Programe para garantir comparabilidade
Execute no mesmo horário todos os dias, dentro de uma janela que você mantém estável, porque os resultados mudam ao longo do dia e uma série coletada em horários variáveis tem uma variância que você não consegue atribuir a nada.
Distribua o trabalho ao longo dessa janela em vez de disparar todas as palavras-chave de uma vez. Uma rajada é ao mesmo tempo mais pesada para a fonte e mais propensa a sofrer limitação de taxa, e distribuir não custa nada quando o prazo é de horas. Adicione jitter entre as requisições, limite a concorrência, e deixe a execução levar uma hora em vez de quatro minutos.
import random, time
def run_daily(targets, window_seconds=3600):
gap = window_seconds / max(len(targets), 1)
for t in targets:
snapshot = fetch_with_retries(t)
store(t, snapshot)
time.sleep(gap * random.uniform(0.7, 1.3)) # spread, with jitter
Diferencie “nenhuma mudança” de “sem dados”
Este é o detalhe que separa uma série confiável de uma enganosa. Se uma coleta falha e você não grava nada, um leitor posterior verá uma lacuna que parece idêntica a um dia em que nada se moveu. Depois, uma comparação com “ontem” abrange silenciosamente dois dias e reporta uma movimentação que não aconteceu.
Registre cada tentativa com um resultado explícito, e faça a análise posterior recusar comparações que atravessem uma lacuna:
def fetch_with_retries(t, attempts=3):
for i in range(attempts):
try:
data = fetch_serp(t)
if is_valid(data): # sanity-check the payload
return {"status": "ok", "data": data}
record_status(t, "invalid") # parsed, but not a real SERP
except requests.RequestException:
record_status(t, "error")
time.sleep(2 ** i * random.uniform(0.5, 1.5))
return {"status": "failed", "data": None} # written as a failure, not skipped
Validar o payload importa tanto quanto capturar exceções, porque uma resposta pode chegar com sucesso e ainda assim não ser uma página de resultado utilizável, o que é o problema geral tratado em detecting blocked or fake content.
Alerte sobre movimentações que significam algo
Um alerta ingênuo para qualquer mudança de posição vai disparar constantemente, e um sistema de alertas em que ninguém confia é pior do que nenhum.
Três regras tornam os alertas úteis. Exija um limiar proporcional à posição, já que um movimento de 3 para 6 importa muito mais do que de 47 para 50. Exija persistência, ou seja, duas ou três execuções consecutivas, porque oscilações de um único dia são normais. E alerte separadamente sobre entrar ou sair da primeira página, já que essa fronteira tem consequências reais de tráfego.
Depois, adicione o alerta que as pessoas esquecem: a própria saúde da coleta. Se um mercado parou silenciosamente de retornar dados há três dias, isso é mais urgente do que qualquer mudança de ranking, e só é visível se você estiver tratando os resultados das execuções como dados de primeira classe, conforme monitoring a scraping pipeline.
Faça os dados se explicarem sozinhos
Duas adições transformam uma tabela de rankings em algo que você pode apresentar a um cliente.
Anote sua própria linha do tempo: deploys, publicações de conteúdo, migrações. Metade de todas as investigações do tipo “por que caímos” termina com um lançamento que saiu naquela semana, e um gráfico anotado encontra isso imediatamente.
Calcule a movimentação em nível de portfólio, não apenas as posições por palavra-chave, porque uma mudança ampla em muitas palavras-chave não relacionadas é um evento diferente de uma única página perdendo posições. Essa é a análise de volatilidade descrita em detecting SERP volatility and algorithm updates, e ela só é possível porque você armazenou o conjunto completo de resultados, e não apenas sua própria posição.
Colete com responsabilidade
Ative-se aos resultados de busca públicos, respeite os termos de serviço de cada mecanismo, e mantenha um ritmo respeitoso em vez de tratar os limites de taxa como um obstáculo. Um job de monitoramento diário não tem motivo para ser agressivo: o prazo é a manhã seguinte, então distribuir o trabalho não custa nada.
Conclusão
O cron job é a parte fácil. Fixe primeiro o seu contrato de medição, de modo que localização, dispositivo, idioma e personalização sejam constantes, e não variáveis acidentais. Armazene o conjunto completo de resultados e o payload bruto, não apenas a sua posição, porque o contexto dos concorrentes e os recursos do resultado são o que torna um número interpretável, e você não pode voltar no tempo para obtê-los. Execute em um horário consistente, distribuído pela janela com jitter. Registre as falhas explicitamente para que uma lacuna nunca possa ser confundida com estabilidade. Alerte sobre movimentações persistentes e ponderadas pela posição, além da saúde da coleta. Depois, anote suas próprias mudanças, para que o gráfico responda à pergunta que as pessoas realmente fazem.
A camada de coleta é uma SERP API se você quiser resultados processados sem manter a coleta, ou residential proxies com segmentação por país e cidade se preferir executá-la você mesmo, com per-GB pricing que se adequa a verificações pequenas e frequentes.