Extração de dados

Seleção de Proxy como um Problema de Multi-Armed Bandit

Qual configuração de proxy a próxima requisição deve usar? Trate isso como um problema de bandit. Em nossa simulação, a seleção adaptativa ficou a 2% da perfeição em retrospectiva.

James Meadow

James Meadow

2 de outubro de 2026 · 10 min de leitura

Na maioria dos setups de scraping, a configuração de proxy é escolhida uma vez, na fase de design, e depois mantida. Residencial com segmentação por cidade para este site, proxies ISP para aquele outro, datacenter onde funciona. A escolha costuma ser feita testando algumas opções durante uma tarde, e raramente é revista até que algo quebre.

Essa é uma decisão tomada sob incerteza, repetida milhares de vezes por dia, com feedback a cada requisição. É exatamente o formato de problema que os estatísticos chamam de multi-armed bandit, e existem algoritmos bem compreendidos para isso. Este guia explica o enquadramento, apresenta uma implementação curta e testada, e mostra o que ela fez em uma simulação na qual a melhor opção mudou na metade do caminho.

Principais conclusões

  • Cada configuração de proxy que você poderia usar para um alvo é um “braço”; cada requisição é um “pull”; uma resposta boa é uma recompensa. O objetivo é o menor custo por resposta boa, não a maior taxa de sucesso a qualquer preço.
  • O Thompson sampling lida com a troca entre usar a melhor opção conhecida e testar as outras, com poucas linhas de código e sem cronograma de ajuste.
  • Os alvos mudam, então evidências antigas devem perder peso. Um fator de desconto dá ao seletor uma memória de aproximadamente os últimos mil resultados.
  • Em nossa simulação, o Thompson sampling com desconto ficou a 2% de uma estratégia que conhecia as taxas reais de antemão, enquanto uma escolha fixa “segura” custou 25% a mais por resposta boa, e manter o vencedor inicial custou 57% a mais.
  • Conte como sucesso apenas respostas genuinamente boas, e limite quanto de exploração um alvo sensível vê.

O enquadramento

Imagine uma fileira de caça-níqueis, cada um pagando com uma probabilidade desconhecida. Cada puxada ensina algo sobre uma máquina, e cada puxada gasta aprendendo sobre uma máquina ruim é uma puxada que não foi gasta em uma boa. Essa tensão entre explorar o que você já sabe e investigar o que você não sabe é o problema do multi-armed bandit.

Para a seleção de proxy, o mapeamento é direto:

Termo do banditNo scraping
BraçoUma configuração de proxy para um alvo: tipo de proxy, segmentação, configuração de sessão
PullUma requisição
RecompensaUma resposta que contém os dados desejados
Custo de um pullBanda, retentativas e tempo gasto naquela requisição
Não estacionariedadeO alvo mudando suas defesas, ou a reputação de um pool mudando

Duas características tornam a versão para scraping mais difícil do que a do livro-texto. Os braços custam quantias diferentes, então o melhor braço é aquele com o menor custo por sucesso, não a maior taxa de sucesso. E as taxas mudam: uma configuração que funciona hoje pode ser bloqueada amanhã, o que é o padrão por trás do contágio de sub-rede e o motivo para construir um índice de saúde do alvo.

O seletor

O Thompson sampling mantém uma distribuição de probabilidade sobre a taxa de sucesso de cada braço, uma distribuição Beta construída a partir de seus sucessos e falhas. Antes de cada requisição, ele sorteia uma taxa plausível para cada braço e escolhe o braço que parece mais barato por sucesso sob esses sorteios. Braços com pouca evidência têm distribuições amplas, então ocasionalmente sorteiam uma taxa alta e acabam sendo testados. Braços com muita evidência sorteiam valores próximos de sua taxa real. A exploração diminui por conta própria conforme a evidência se acumula.

A versão abaixo adiciona duas coisas: custo por tentativa, e um desconto que decai a evidência antiga em direção ao prior, para que o seletor consiga perceber mudanças.

import random


class ProxySelector:
    """Pick a proxy configuration per request with Thompson sampling, minimising cost per successful response.

    Each configuration keeps a Beta(successes, failures) belief about its success rate. A discount below 1
    lets old outcomes fade, so the selector notices when a configuration that used to work starts failing.
    """

    def __init__(self, costs, discount=0.999, prior=(1.0, 1.0), rng=None):
        self.costs = dict(costs)                       # configuration -> cost per attempt
        self.discount = discount
        self.prior = prior
        self.wins = {arm: prior[0] for arm in self.costs}
        self.losses = {arm: prior[1] for arm in self.costs}
        self.rng = rng or random.Random()

    def choose(self):
        """Sample a plausible success rate for each configuration and take the cheapest per expected success."""
        def sampled_cost_per_success(arm):
            rate = self.rng.betavariate(self.wins[arm], self.losses[arm])
            return self.costs[arm] / max(rate, 1e-9)
        return min(self.costs, key=sampled_cost_per_success)

    def update(self, arm, success):
        """Record one outcome. Every belief decays towards the prior first, so recent results count most."""
        for a in self.costs:
            self.wins[a] = self.prior[0] + self.discount * (self.wins[a] - self.prior[0])
            self.losses[a] = self.prior[1] + self.discount * (self.losses[a] - self.prior[1])
        if success:
            self.wins[arm] += 1
        else:
            self.losses[arm] += 1

No uso prático, chame choose() antes de cada requisição, envie a requisição através da configuração escolhida, e chame update() informando se a resposta foi boa. Um desconto de 0.999 dá uma memória efetiva de aproximadamente mil resultados; 0.995 dá aproximadamente duzentos.

A simulação

Para ver como o seletor se comporta quando a resposta muda, simulamos um alvo com quatro configurações. As taxas de sucesso e os custos abaixo são premissas escolhidas para tornar as trocas visíveis, não medições de qualquer provedor ou rede:

ConfiguraçãoTaxa de sucesso assumidaCusto por tentativa assumidoCusto por sucesso
Datacenter25%0.52.00
ISP80%, caindo para 30% após a requisição 5.0000.91.13, depois 3.00
Residencial, segmentação por país92%1.31.41
Residencial, segmentação por cidade95%1.61.68

Os custos são unidades relativas que cobrem banda e a sobrecarga de uma tentativa. Nas primeiras 5.000 requisições, o ISP é a forma mais barata de obter uma resposta boa; depois o alvo começa a bloqueá-lo, e o residencial com segmentação por país se torna a melhor escolha. Cada estratégia fez 20.000 requisições, e repetimos cada estratégia 100 vezes com sementes aleatórias diferentes.

EstratégiaCusto por resposta boaContra a visão retrospectiva perfeitaRespostas boasRequisições para se adaptar após a mudança
Visão retrospectiva perfeita (conhece as taxas reais)1.348linha de base17.8060
Thompson sampling, desconto 0.9991.375+2.0%16.694cerca de 800
Thompson sampling, desconto 0.9951.389+3.0%15.704cerca de 1.075
Thompson sampling, sem desconto1.428+5.9%15.933cerca de 3.500
Epsilon-greedy, 10% de exploração1.441+6.9%15.848cerca de 2.800
Thompson sampling, desconto 0.981.443+7.0%13.834nunca se estabilizou
Sempre residencial, segmentação por cidade1.684+24.9%19.000não aplicável
Round robin entre as quatro1.689+25.3%12.731não aplicável
Sempre ISP, o vencedor inicial2.118+57.1%8.501não aplicável

Os números são médias de 100 execuções; para cada estratégia, os 90% centrais das execuções ficaram dentro de menos de 2.5% de sua média. “Requisições para se adaptar” é o número mediano de requisições após a mudança até que 90% de uma janela de 200 requisições fosse direcionado para a nova melhor configuração.

O que os resultados dizem

Escolhas fixas são caras nos dois sentidos. Usar sempre a configuração mais confiável entregou o maior número de respostas boas, mas custou 25% a mais por cada uma. Usar sempre a configuração que venceu no início foi a pior estratégia de todas depois que o alvo mudou, a 57% acima da linha de base.

Aprender não basta sem esquecer. O Thompson sampling sem desconto levou cerca de 3.500 requisições após a mudança para parar de confiar na evidência que havia acumulado sobre o antigo vencedor. Um desconto de 0.999 reduziu isso para cerca de 800.

Esquecer rápido demais é um problema por si só. Com um desconto de 0.98, o seletor lembrava apenas cerca de 50 resultados, continuava retestando as opções ruins e nunca se estabilizou. A faixa útil depende do tráfego: a memória deve cobrir requisições suficientes para distinguir as configurações, e nada além disso.

O objetivo determina a resposta. O bandit minimizou o custo por resposta boa. Se o que importa é a maior quantidade de dados independentemente do custo, ou um prazo, a recompensa precisa dizer isso; caso contrário, o seletor vai otimizar a coisa errada de forma muito eficiente.

Usando isso em tráfego real

  • Defina sucesso de forma rigorosa. Um HTTP 200 com uma página de bloqueio ou resultados vazios é uma falha. Alimente o seletor com as mesmas verificações que você usa para a taxa de falha silenciosa, ou ele vai aprender a preferir configurações que falham silenciosamente.
  • Execute um seletor por alvo. As taxas de sucesso variam por site, então um seletor compartilhado faz uma média que apaga exatamente as diferenças que você quer que ele aprenda.
  • Limite a exploração em alvos sensíveis. Cada requisição exploratória através de uma configuração ruim é uma requisição que um site pode contar contra você. Remova configurações claramente inadequadas em vez de deixar o seletor redescobrir isso sozinho.
  • Mantenha as sessões coerentes. Escolha a configuração por sessão, não por requisição, para tudo que depende de estado, como logins ou carrinhos de compra, conforme explica sessões fixas versus rotativas.
  • Use custos reais. Em proxies cobrados por banda, o custo por tentativa depende do peso da página e das retentativas; os números por trás do custo por registro limpo são os insumos corretos.
  • Mantenha um piso de balanceamento de carga. Um bandit decide qual configuração usar, não como distribuir as requisições dentro dela; isso ainda é tarefa da arquitetura de rotação, concorrência e retentativa.

Conclusão

Escolher uma configuração de proxy uma vez e mantê-la é uma aposta de que nem seus custos nem o alvo vão mudar. Tratar cada requisição como um pequeno experimento, com Thompson sampling e uma memória sensata, transforma essa aposta em uma medição que continua se atualizando.

Em nossa simulação, isso ficou a 2% de conhecer a resposta de antemão, se adaptou em cerca de 800 requisições quando a melhor opção mudou, e evitou o sobrecusto de 25% a 57% das estratégias fixas. As taxas e os custos eram premissas; o código é curto o suficiente para ser executado com os seus próprios dados.

Fontes e referências

  • Daniel Russo e colegas, A Tutorial on Thompson Sampling, 2017.
  • Simulação executada pela Shifter em 2 de outubro de 2026 usando o código acima; as taxas de sucesso e os custos são premissas, não medições de qualquer rede.

Pronto para começar?

Experimente os proxies residenciais da Shifter, mais de 205M IPs, mais de 195 países, a partir de $ 0,75/GB.

Começar