Scraping com Proxy PHP cURL: CURLOPT_PROXY, Autenticação e Guzzle

A maioria dos snippets de proxy PHP online ainda possuem os mesmos três bugs de 2011. Aqui está uma configuração limpa de CURLOPT_PROXY — autenticação, SOCKS5, rotação com curl_multi, Guzzle e timeouts — que se mantém em produção.

O código de proxy PHP cURL tem um problema peculiar: os snippets mais bem classificados têm uma década e muitos contêm bugs reais — chamadas duplas de curl_exec() que disparam cada solicitação duas vezes, CURLOPT_SSL_VERIFYPEER desativado "para fazer o HTTPS funcionar", opções definidas para valores que já eram padrão. Este guia é a versão atual e correta: CURLOPT_PROXY com autenticação, SOCKS5, rotação paralela com curl_multi, a mesma configuração no Guzzle e timeouts que impedem um trabalhador de ficar travado a noite toda.

Um exemplo mínimo e correto de proxy PHP cURL

<?php
function fetch(string $url): string|false {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_PROXY          => 'http://gate.quantumproxies.io:8000',
        CURLOPT_PROXYUSERPWD   => 'USER:PASS',
        CURLOPT_RETURNTRANSFER => true,  // return the body, don't echo it
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 3,
        CURLOPT_CONNECTTIMEOUT => 10,    // seconds to reach the proxy
        CURLOPT_TIMEOUT        => 30,    // seconds for the whole transfer
        CURLOPT_ENCODING       => '',    // accept gzip/br, decode automatically
    ]);
    $body = curl_exec($ch);
    if ($body === false) {
        error_log('cURL error ' . curl_errno($ch) . ': ' . curl_error($ch));
    }
    curl_close($ch);
    return $body;
}

echo fetch('https://httpbin.org/ip'); // -> the proxy exit IP

As credenciais podem estar em CURLOPT_PROXYUSERPWD como mostrado, ou embutidas na URL do proxy (http://USER:PASS@host:port) — ambas funcionam. CURLOPT_RETURNTRANSFER é o que os iniciantes esquecem: sem ele, curl_exec() ecoa a resposta diretamente na saída e retorna true, o que nunca é o que um scraper deseja. No PHP 8, o handle é um objeto CurlHandle em vez de um recurso; as chamadas de opção permanecem inalteradas.

As opções que as pessoas copiam sem pensar (e um erro real de segurança)

Lista de verificação de opções de proxy PHP cURL a definir versus opções a evitar, incluindo o erro SSL_VERIFYPEER
Seis opções realizam todo o trabalho; a coluna da direita é dívida de cópia e colagem de tutoriais da era 2011.

Proxies SOCKS5 em PHP

<?php
// scheme style: socks5h = DNS resolved by the proxy (use for scraping)
curl_setopt($ch, CURLOPT_PROXY, 'socks5h://USER:PASS@gate.quantumproxies.io:1080');

// split style: same result via CURLOPT_PROXYTYPE
curl_setopt($ch, CURLOPT_PROXY, 'gate.quantumproxies.io:1080');
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'USER:PASS');

O esquema decide onde o DNS acontece: socks5:// (e CURLPROXY_SOCKS5) resolve nomes de host no seu servidor, vazando cada domínio alvo para o seu resolvedor local; socks5h:// (CURLPROXY_SOCKS5_HOSTNAME) resolve na saída do proxy. Prefira o último. QuantumProxies oferece HTTP e SOCKS5 em todos os planos, então mudar de protocolo é uma alteração de uma linha, não uma nova assinatura. A mesma regra de DNS se aplica na linha de comando — veja nossas receitas de proxy curl para os equivalentes de shell de tudo nesta página.

Rotação e solicitações paralelas com curl_multi

PHP não tem async/await, mas não precisa de um para scraping paralelo — curl_multi executa dezenas de transferências simultaneamente em um único processo. Aponte cada handle para um gateway rotativo e cada solicitação sai automaticamente de um IP residencial diferente, sem necessidade de manutenção de lista de proxies no seu código:

<?php
$urls = ['https://example.com/p/1', 'https://example.com/p/2', 'https://example.com/p/3'];

$mh = curl_multi_init();
$handles = [];

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        // one gateway, a fresh residential exit per request
        CURLOPT_PROXY          => 'http://USER:PASS@rotating.quantumproxies.io:8000',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_ENCODING       => '',
    ]);
    curl_multi_add_handle($mh, $ch);
    $handles[$url] = $ch;
}

do {
    curl_multi_exec($mh, $running);
    curl_multi_select($mh);        // wait for activity instead of spinning
} while ($running > 0);

$results = [];
foreach ($handles as $url => $ch) {
    $results[$url] = curl_multi_getcontent($ch);
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}
curl_multi_close($mh);

Mantenha os lotes modestos — 8 a 16 handles simultâneos são suficientes para a maioria dos alvos, e o ritmo importa mais do que a velocidade bruta: um usleep() de algumas centenas de milissegundos entre os lotes custa pouco e mantém as taxas de solicitação abaixo dos limites que acionam 429s. Verifique curl_getinfo($ch, CURLINFO_HTTP_CODE) por handle e encaminhe falhas para uma fila de tentativas em vez de refazer inline. Quando um fluxo abrange várias solicitações que devem compartilhar uma identidade (login, carrinho, paginação com cookies), altere esse handle para uma sessão fixa adicionando um sufixo ao nome de usuário: USER-session-a1b2c3 mantém o mesmo IP de saída durante a duração em vez de rotacionar.

Diagrama de um pipeline de scraping PHP enviando lotes curl_multi através de um gateway de proxy residencial rotativo
O trabalhador permanece simples, o gateway permanece inteligente: rotação, fixação e direcionamento geográfico vivem todos na URL do proxy.

Cabeçalhos e cookies: pareça o navegador que você afirma ser

Um IP de saída limpo com cabeçalhos cURL padrão ainda é lido como um bot — PHP se anuncia a menos que você vista a solicitação. Três opções fecham a maioria das lacunas: CURLOPT_USERAGENT com uma string de navegador atual, CURLOPT_HTTPHEADER para Accept-Language e similares, e o par de cookies CURLOPT_COOKIEJAR / CURLOPT_COOKIEFILE apontados para o mesmo arquivo, que persiste cookies de sessão entre handles da mesma forma que um navegador faz. A persistência de cookies importa mais do que as pessoas esperam: muitos sites definem um token no primeiro acesso e desafiam silenciosamente clientes que nunca o retornam. Mantenha a identidade reivindicada coerente — um User-Agent do Chrome sem Accept-Language e um conjunto de cabeçalhos simples é em si uma impressão digital, e a verificação de coerência é exatamente como fornecedores anti-bot de nível médio pegam scrapers PHP em sua segunda solicitação.

Guzzle: o mesmo proxy, ergonomia melhor

<?php
use GuzzleHttp\Client;

$client = new Client([
    'proxy'           => 'http://USER:PASS@gate.quantumproxies.io:8000',
    'timeout'         => 30,
    'connect_timeout' => 10,
]);

$res = $client->get('https://httpbin.org/ip');
echo $res->getBody();

// per-request override, e.g. a sticky session for a login flow
$res = $client->get('https://example.com/account', [
    'proxy' => 'http://USER-session-a1b2c3:PASS@gate.quantumproxies.io:8000',
]);

Guzzle dirige o cURL por baixo, então tudo acima ainda se aplica — só fica mais legível. Um aviso: o timeout padrão do Guzzle é 0, significando esperar para sempre. Uma saída de proxy que trava no meio da transferência fixará um trabalhador PHP-FPM indefinidamente, então defina ambos os timeouts em cada cliente que você construir. A opção proxy também aceita um array com as chaves http, https e no quando você precisa de roteamento por esquema ou exclusões de host interno.

Quando o PHP cURL deixa de ser suficiente

cURL busca HTML; ele não executa JavaScript. Se a resposta vier como uma página esqueleto com divs vazios, os dados são renderizados no cliente — nosso guia sobre respostas de página vazia mostra como detectar isso. E em alvos protegidos, o próprio handshake TLS do cURL te denuncia, não importa quão limpo seja o IP (o mistério do browser-funciona-curl-403). PHP não pode razoavelmente executar um navegador headless por solicitação, então a saída pragmática é a Scraper API: uma chamada cURL para um único endpoint, e a renderização, impressões digitais do navegador, rotação e análise retornam como markdown, JSON ou HTML limpos.

Experimente a Scraper API a partir do PHP

Perguntas frequentes

Como uso um proxy com PHP cURL?

Defina CURLOPT_PROXY para a URL completa do proxy, adicione CURLOPT_PROXYUSERPWD se precisar de credenciais, e sempre ative CURLOPT_RETURNTRANSFER além de timeouts explícitos. Esse é o conjunto necessário — alvos HTTPS tunelam automaticamente, e todo o resto é ajuste opcional.

Como passo um nome de usuário e senha de proxy no PHP?

Ou curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:pass') ou embuta-os na URL: http://user:pass@host:port. Se você receber HTTP 407, o proxy rejeitou essas credenciais — verifique se há erros de digitação e codifique caracteres especiais na URL. Provedores que usam autenticação por lista branca de IP não precisam de credenciais assim que o IP do seu servidor está autorizado.

O PHP cURL pode usar um proxy SOCKS5?

Sim. Passe uma URL socks5h:// para CURLOPT_PROXY, ou defina CURLOPT_PROXYTYPE para CURLPROXY_SOCKS5_HOSTNAME. Prefira as variantes de hostname sobre o socks5 simples para que o DNS seja resolvido na saída do proxy em vez do seu servidor — caso contrário, cada domínio alvo vaza através do seu resolvedor local.

O que é CURLOPT_HTTPPROXYTUNNEL e eu preciso dele?

Ele força o cURL a tunelar através do proxy com CONNECT em vez de pedir ao proxy para buscar a URL. Para alvos https:// o cURL tunela automaticamente, então a opção é redundante em quase todo código de scraping. Defina-a apenas quando precisar de semântica de túnel para um alvo HTTP simples.

Como defino um timeout para PHP cURL através de um proxy?

Defina ambos: CURLOPT_CONNECTTIMEOUT cobre alcançar o proxy e abrir o túnel, CURLOPT_TIMEOUT limita toda a transferência. Variantes em milissegundos (_MS) existem para orçamentos mais apertados. Sem eles, uma saída travada mantém a solicitação — e sob PHP-FPM, um trabalhador inteiro — até que o próprio limite do servidor web a mate.

Copie o primeiro snippet, mantenha a lista de verificação em mente, e o PHP é uma linguagem de scraping perfeitamente sólida: curl_multi para paralelismo, um gateway rotativo para identidade, Guzzle para ergonomia, e a Scraper API para as páginas que resistem.

Execute seu scraper PHP em IPs residenciais da QuantumProxies