Melhores Práticas de Geo-Targeted SERP Scraping: Guia para Desenvolvedores

Guia técnico de geo-targeted SERP scraping com Python: parâmetros uule, gl, hl, proxies residenciais por cidade, parsing com selectolax, sessões sticky e estratégias de anti-bloqueio.

Geo-Targeted SERP Scraping Best Practices for Local SEO Data
Neste artigo

Melhores práticas de geo-targeted SERP scraping: por que a localização muda os resultados do Google

Se você já pesquisou "pizza" de São Paulo e de Nova York no mesmo dia, notou que os resultados são completamente diferentes. O Google personaliza os SERPs com base na localização do usuário, e isso cria um desafio para engenheiros de SEO que precisam coletar dados de ranking por mercado. As melhores práticas de geo-targeted SERP scraping exigem que você controle não apenas os parâmetros da URL, mas também a geografia do IP que faz a requisição.

O Google determina a localização do usuário através de três sinais principais: o endereço IP, os parâmetros de consulta (gl, hl, uule) e, quando disponível, dados de GPS ou localização do navegador. Quando esses sinais conflitam — por exemplo, um IP brasileiro com gl=US — o Google pode retornar resultados inconsistentes ou acionar verificações de segurança.

Para SEO local, o targeting a nível de país é insuficiente. Uma consulta por "dentista" em "Rio de Janeiro" versus "Recife" produz rankings radicalmente diferentes, mesmo dentro do mesmo país. Você precisa de precisão a nível de cidade, e é aqui que o parâmetro uule se torna essencial.

Parâmetros de localização do Google: gl, hl, google_domain, lr e uule

O Google oferece vários parâmetros de URL para controlar a localização dos resultados. Combiná-los corretamente é o primeiro passo para coletar SERPs geo-targeted consistentes:

ParâmetroFunçãoExemplo
glPaís dos resultadosgl=US, gl=BR, gl=DE
hlIdioma da interfacehl=en-US, hl=pt-BR
google_domainDomínio do Googlegoogle.com.br, google.de
lrRestringir idioma dos resultadoslr=lang_pt, lr=lang_en
uuleLocalização precisa (cidade)w+CAIQICI... (base64)
pwsPersonalização webpws=0 (desativada)

O parâmetro uule é o mais poderoso e o menos documentado. Ele aceita um valor codificado que representa uma localização precisa — cidade ou até mesmo coordenadas. O Google não documenta oficialmente o formato do uule, mas a comunidade de scraping identificou sua estrutura através de engenharia reversa:

w+CAIQICI + caractere_de_comprimento + base64(nome_da_cidade)

O caractere de comprimento é derivado do tamanho da string base64 usando chr(63 + len(b64)). A codificação base64 é um padrão bem estabelecido, e o prefixo w+CAIQICI é fixo para todas as localizações. Sempre adicione pws=0 para obter uma baseline não personalizada — sem esse parâmetro, o Google pode injetar resultados baseados no histórico de pesquisa associado ao IP.

Construindo o uule e montando a URL de busca

import base64
import urllib.parse

def build_uule(city_name):
    """Constrói o parâmetro uule do Google para uma cidade específica."""
    b64 = base64.b64encode(city_name.encode("utf-8")).decode("utf-8")
    length_char = chr(63 + len(b64))
    return f"w+CAIQICI{length_char}{b64}"

def build_serp_url(query, gl, hl, city, google_domain="google.com", num=10):
    uule = build_uule(city)
    params = {
        "q": query,
        "gl": gl,
        "hl": hl,
        "uule": uule,
        "pws": "0",
        "num": str(num),
    }
    base = f"https://www.{google_domain}/search"
    return base + "?" + urllib.parse.urlencode(params)

# Exemplo: "pizza" em São Paulo, Brasil
url = build_serp_url("pizza", gl="BR", hl="pt-BR", city="Sao Paulo")
print(url)
# https://www.google.com/search?q=pizza&gl=BR&hl=pt-BR&uule=w%2BCAIQICI...&pws=0&num=10

Note que urllib.parse.urlencode codifica automaticamente o + do prefixo uule como %2B, garantindo que o Google interprete o parâmetro corretamente.

Alinhando geografia do IP com parâmetros de consulta

Aqui está o ponto crítico que muitos desenvolvedores ignoram: o Google verifica a consistência entre a geografia do IP e os parâmetros de localização. Se você enviar gl=US e uule=New York através de um IP alemão, o Google pode ignorar seus parâmetros, retornar resultados híbridos inconsistentes ou acionar um CAPTCHA via redirecionamento 302.

A solução é rotear cada requisição através de um proxy residencial cuja geografia corresponda ao mercado alvo. Com ProxyHat, você especifica país e cidade diretamente no nome de usuário: user-country-US-city-newyork:pass@gate.proxyhat.com:8080. Isso garante que o IP de saída seja um IP residencial real localizado em Nova York, alinhado com gl=US e uule=New York.

Proxy genérico vs ProxyHat com geo-targeting

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# --- Proxy genérico (raw) ---
raw_proxy = "http://user:pass@190.0.0.1:8080"

# --- ProxyHat com geo-targeting (country + city) ---
proxyhat_proxy = (
    "http://user-country-BR-city-saopaulo:pass"
    "@gate.proxyhat.com:8080"
)

session = requests.Session()
session.proxies = {"http": proxyhat_proxy, "https": proxyhat_proxy}

retry = Retry(total=3, backoff_factor=2,
              status_forcelist=[429, 500, 502, 503])
adapter = HTTPAdapter(max_retries=retry)
session.mount("https://", adapter)

response = session.get(url, timeout=30)
print(f"Status: {response.status_code}, Length: {len(response.text)}")

A diferença é fundamental: o proxy genérico não controla a geografia do IP de saída, enquanto o ProxyHat com -country-BR-city-saopaulo garante um IP residencial em São Paulo. O timeout de 30 segundos acomoda a latência adicional de proxies residenciais (~200–500ms).

Usando curl_cffi com impersonação de Chrome

O Google detecta fingerprints TLS inconsistentes. A biblioteca curl_cffi resolve isso impersonando o handshake TLS do Chrome:

from curl_cffi import requests as cffi_requests

proxyhat_proxy = (
    "http://user-country-US-city-newyork:pass"
    "@gate.proxyhat.com:8080"
)

url = build_serp_url("coffee shops", gl="US", hl="en-US",
                     city="New York", google_domain="google.com")

response = cffi_requests.get(
    url,
    proxies={"https": proxyhat_proxy},
    impersonate="chrome",
    timeout=30,
)

if response.status_code == 200:
    html = response.text
    print(f"Recebidos {len(html)} caracteres")
elif response.status_code == 429:
    print("Rate limited - aplicar backoff exponencial")
else:
    print(f"Status inesperado: {response.status_code}")

Parsing de resultados orgânicos e local pack com selectolax

Depois de receber o HTML do SERP, você precisa extrair os resultados orgânicos e o local pack (a caixa de mapas com negócios locais). O Google muda a estrutura do HTML frequentemente, então use uma biblioteca de parsing rápida e tolerante. A selectolax usa libhtml5ever (Rust) por baixo, oferecendo velocidade próxima ao parsing nativo do navegador.

from selectolax.parser import HTMLParser

def parse_serp(html):
    tree = HTMLParser(html)
    results = {"organic": [], "local_pack": []}

    # Resultados orgânicos
    for div in tree.css("div.g"):
        title_el = div.css_first("h3")
        link_el = div.css_first("a")
        if title_el and link_el:
            results["organic"].append({
                "title": title_el.text(strip=True),
                "url": link_el.attributes.get("href", ""),
            })

    # Local Pack (mapa + negócios locais)
    for div in tree.css("div[role='article']"):
        name_el = div.css_first("div.dbg0pd")
        if name_el:
            results["local_pack"].append({
                "name": name_el.text(strip=True),
            })

    return results

parsed = parse_serp(html)
print(f"Organicos: {len(parsed['organic'])}")
print(f"Local pack: {len(parsed['local_pack'])}")
for r in parsed["organic"][:3]:
    print(f"  {r['title']}")

Os seletores CSS (div.g, div.dbg0pd) mudam periodicamente. Monitore a taxa de resultados extraídos por página — se cair abaixo de 8 resultados em uma página de 10, provavelmente o seletor quebrou e precisa de atualização.

Sessões sticky para SERPs de múltiplas páginas

Ao coletar múltiplas páginas de resultados (páginas 1–3), é importante manter o mesmo IP entre requisições. Se o IP mudar entre páginas, o Google pode interpretar como comportamento anômalo e retornar resultados diferentes ou bloquear. O ProxyHat suporta sessões sticky através do parâmetro session no nome de usuário.

import uuid
import requests

def scrape_multipage(query, gl, hl, city, pages=3):
    """Raspa múltiplas páginas mantendo o mesmo IP (sticky session)."""
    session_id = f"serp-{uuid.uuid4().hex[:8]}"
    proxy = (
        f"http://user-country-{gl}-city-{city.replace(' ', '').lower()}"
        f"-session-{session_id}:pass@gate.proxyhat.com:8080"
    )

    all_results = []
    s = requests.Session()
    s.proxies = {"http": proxy, "https": proxy}

    for page in range(pages):
        start = page * 10
        url = build_serp_url(query, gl, hl, city, num=10)
        url = url + "&start=" + str(start)

        resp = s.get(url, timeout=30)
        if resp.status_code != 200:
            print(f"Pagina {page+1}: status {resp.status_code}, parando")
            break

        parsed = parse_serp(resp.text)
        all_results.extend(parsed["organic"])
        print(f"Pagina {page+1}: {len(parsed['organic'])} resultados")

    return all_results

results = scrape_multipage("dentista", "BR", "pt-BR",
                           "Rio de Janeiro", pages=3)
print(f"Total coletado: {len(results)} resultados")

Backoff, retries e pools de proxy por país

Mesmo com proxies residenciais, você encontrará rate limits (HTTP 429) e CAPTCHAs ocasionais. O RFC 7231 define o status 429 e o header Retry-After, que o Google pode enviar para indicar quando tentar novamente. Sempre respeite esse header quando presente.

Estratégia recomendada:

  • Backoff exponencial: dobre o tempo de espera a cada tentativa, com jitter aleatório
  • Rotação de sessão: mude o identificador de sessão a cada retry para obter um novo IP
  • Pools por país: mantenha configurações de proxy separadas por mercado
  • Timeout adequado: 30 segundos é um bom padrão para requisições via proxy
import time
import random
import uuid
import logging
import requests

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("serp_scraper")

PROXY_POOLS = {
    "US": "http://user-country-US-session-{sid}:pass@gate.proxyhat.com:8080",
    "BR": "http://user-country-BR-session-{sid}:pass@gate.proxyhat.com:8080",
    "DE": "http://user-country-DE-session-{sid}:pass@gate.proxyhat.com:8080",
}

def fetch_with_backoff(url, country, max_retries=5):
    for attempt in range(max_retries):
        sid = f"retry-{attempt}-{uuid.uuid4().hex[:6]}"
        proxy = PROXY_POOLS[country].format(sid=sid)

        try:
            resp = requests.get(
                url,
                proxies={"https": proxy, "http": proxy},
                timeout=30,
            )
            if resp.status_code == 200:
                return resp.text
            elif resp.status_code == 429:
                wait = (2 ** attempt) + random.uniform(0, 1)
                logger.warning(f"429 recebido, esperando {wait:.1f}s")
                time.sleep(wait)
            elif resp.status_code == 302:
                logger.warning("Redirect (possivel CAPTCHA)")
                time.sleep(5)
            else:
                logger.error(f"Status {resp.status_code}")
                time.sleep(2)
        except requests.RequestException as e:
            logger.error(f"Erro de rede: {e}")
            time.sleep(2)

    return None

# Uso:
url = build_serp_url("restaurante", gl="BR", hl="pt-BR", city="Sao Paulo")
html = fetch_with_backoff(url, "BR")
if html:
    parsed = parse_serp(html)
    print(f"{len(parsed['organic'])} resultados orgânicos")

Comparação de tipos de proxy para SERP scraping

Tipo de ProxyPrecisão GeográficaTaxa de SucessoLatência TípicaCusto
DatacenterBaixa — IPs de datacenter conhecidos30–50%~50msBaixo
ResidencialAlta — IPs reais de ISP por cidade85–95%~200–500msMédio
MobileMuito alta — IPs de operadora móvel90–98%~300–800msAlto

Para geo-targeted SERP scraping, proxies residenciais oferecem o melhor equilíbrio entre custo e confiabilidade. Datacenter proxies são facilmente detectados pelo Google, resultando em taxas de sucesso de apenas 30–50%. Mais de 50 requisições simultâneas por IP aumenta significativamente a taxa de bloqueio.

Erros comuns e casos extremos

  • Parâmetros inconsistentes: gl=US com hl=pt-BR e IP brasileiro — o Google pode priorizar o IP sobre os parâmetros
  • uule com nome errado: "New York City" vs "New York" — use o nome canônico sem qualificadores extras
  • Esquecer pws=0: sem esse parâmetro, resultados podem variar entre coletas mesmo com o mesmo IP
  • Não tratar redirecionamentos 302: o Google usa 302 para redirecionar para CAPTCHAs; trate como sinal de bloqueio
  • Concorrência excessiva: mais de 50 requisições simultâneas por IP dispara rate limiting rápido
  • Não respeitar robots.txt: sempre verifique o robots.txt do domínio alvo antes de coletar

Configuração no ProxyHat

O ProxyHat simplifica o geo-targeting através de flags no nome de usuário. Para SERP scraping localizado, a configuração é direta:

  • País: user-country-US:pass@gate.proxyhat.com:8080
  • País + cidade: user-country-US-city-newyork:pass@gate.proxyhat.com:8080
  • Sessão sticky: user-country-US-city-newyork-session-abc123:pass@gate.proxyhat.com:8080
  • SOCKS5: socks5://user-country-US-city-newyork:pass@gate.proxyhat.com:1080

Todas as requisições vão através de gate.proxyhat.com:8080 (HTTP) ou gate.proxyhat.com:1080 (SOCKS5). Consulte a documentação do ProxyHat para detalhes completos de autenticação e parâmetros avançados.

Para explorar localizações disponíveis, visite nossa página de localizações. Para comparar planos, veja preços do ProxyHat. Se você está construindo um pipeline de scraping mais amplo, confira nosso guia de web scraping e SERP tracking.

Exemplo rápido com curl

curl -x "http://user-country-US-city-newyork:pass@gate.proxyhat.com:8080" \
  "https://www.google.com/search?q=coffee&gl=US&hl=en-US&pws=0&num=10" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  --compressed

Considerações sobre dados públicos e ToS

Raspagem de SERPs do Google existe em uma área cinzenta legal. Os Termos de Serviço do Google proíbem scraping automatizado. Na União Europeia, o GDPR se aplica a dados pessoais que possam aparecer nos resultados. Recomendações práticas:

  • Colete apenas dados públicos não pessoais (rankings, títulos, URLs)
  • Não armazene dados pessoais de indivíduos identificados nos resultados
  • Respeite robots.txt de cada domínio
  • Use rate limiting razoável (não exceda 1 requisição por segundo por IP)
  • Considere a API oficial do Google quando possível, embora ela não ofereça o mesmo nível de geo-targeting por cidade

Principais aprendizados

Key Takeaways:

  • O parâmetro uule é essencial para precisão a nível de cidade — gl sozinho é insuficiente para SEO local
  • A geografia do IP deve corresponder aos parâmetros de localização — use proxies residenciais por cidade
  • Sempre adicione pws=0 para baselines não personalizados e consistentes
  • Use sessões sticky ao coletar múltiplas páginas do mesmo SERP
  • Implemente backoff exponencial com rotação de sessão para tratar HTTP 429 e CAPTCHAs
  • ProxyHat: user-country-US-city-newyork-session-abc123:pass@gate.proxyhat.com:8080

Perguntas frequentes

O que é geo-targeted SERP scraping e quais são as melhores práticas?

Geo-targeted SERP scraping é a coleta de páginas de resultados do Google configuradas para uma localização específica, usando parâmetros como gl (país), hl (idioma) e uule (cidade). As melhores práticas incluem alinhar a geografia do IP proxy com os parâmetros de URL, usar pws=0 para desativar personalização, construir o uule corretamente com base64 do nome da cidade, e implementar sessões sticky para coleta de múltiplas páginas. Proxies residenciais com targeting de cidade são essenciais para evitar detecção.

Por que o geo-targeted SERP scraping importa para usuários de proxy?

O Google retorna resultados diferentes conforme a localização do IP do solicitante. Sem proxies geo-targeted, seus dados de ranking refletirão a localização do seu servidor e não do mercado alvo. Proxies residenciais com targeting de cidade garantem que cada requisição venha de um IP real na localização correta, produzindo SERPs autênticos. Isso é crítico para rank tracking local, análise de concorrência por mercado e auditoria de SEO multi-regional.

Qual tipo de proxy funciona melhor para geo-targeted SERP scraping?

Proxies residenciais são a melhor escolha, oferecendo taxas de sucesso de 85–95% com latência de 200–500ms. Datacenter proxies são facilmente detectados pelo Google (30–50% de sucesso). Mobile proxies têm a taxa mais alta (90–98%) mas custam mais. Para a maioria dos casos, proxies residenciais com targeting de cidade oferecem o melhor equilíbrio entre custo, confiabilidade e precisão geográfica.

Como evitar bloqueios ao implementar geo-targeted SERP scraping?

Para evitar bloqueios: use proxies residenciais com geo-targeting por cidade, implemente backoff exponencial ao receber HTTP 429, rotacione sessões a cada retry, mantenha concorrência baixa (máximo 50 requisições simultâneas por IP), use curl_cffi com impersonação de Chrome para fingerprint TLS consistente, e sempre adicione pws=0. Trate redirecionamentos 302 como possíveis CAPTCHAs e faça pausas adequadas entre coletas.

Perguntas frequentes

O que é geo-targeted SERP scraping e quais são as melhores práticas?

Geo-targeted SERP scraping é a coleta de páginas de resultados do Google configuradas para uma localização específica, usando parâmetros como gl (país), hl (idioma) e uule (cidade). As melhores práticas incluem alinhar a geografia do IP proxy com os parâmetros de URL, usar pws=0 para desativar personalização, construir o uule corretamente com base64 do nome da cidade, e implementar sessões sticky para coleta de múltiplas páginas. Proxies residenciais com targeting de cidade são essenciais para evitar detecção.

Por que o geo-targeted SERP scraping importa para usuários de proxy?

O Google retorna resultados diferentes conforme a localização do IP do solicitante. Sem proxies geo-targeted, seus dados de ranking refletirão a localização do seu servidor e não do mercado alvo. Proxies residenciais com targeting de cidade garantem que cada requisição venha de um IP real na localização correta, produzindo SERPs autênticos. Isso é crítico para rank tracking local, análise de concorrência por mercado e auditoria de SEO multi-regional.

Qual tipo de proxy funciona melhor para geo-targeted SERP scraping?

Proxies residenciais são a melhor escolha, oferecendo taxas de sucesso de 85–95% com latência de 200–500ms. Datacenter proxies são facilmente detectados pelo Google (30–50% de sucesso). Mobile proxies têm a taxa mais alta (90–98%) mas custam mais. Para a maioria dos casos, proxies residenciais com targeting de cidade oferecem o melhor equilíbrio entre custo, confiabilidade e precisão geográfica.

Como evitar bloqueios ao implementar geo-targeted SERP scraping?

Para evitar bloqueios: use proxies residenciais com geo-targeting por cidade, implemente backoff exponencial ao receber HTTP 429, rotacione sessões a cada retry, mantenha concorrência baixa (máximo 50 requisições simultâneas por IP), use curl_cffi com impersonação de Chrome para fingerprint TLS consistente, e sempre adicione pws=0. Trate redirecionamentos 302 como possíveis CAPTCHAs e faça pausas adequadas entre coletas.

Pronto para começar?

Proxies residenciais, ISP e móveis em mais de 148 países. Crie uma conta grátis.

Criar conta grátis
← Voltar ao Blog