Como configurar um proxy com o Guzzle

Navegue pelos desafios da extração de dados da web usando o Guzzle e proxies. Certifique-se de que o PHP 7.2.5+ e o Composer estejam prontos e, em seguida, integre o Guzzle para a configuração do proxy.
2 min read
Setting a proxy in Guzzle blog image

No contexto da utilização do Guzzle, um proxy atua como um servidor intermediário que conecta seu aplicativo cliente ao servidor web pretendido. Ele facilita o encaminhamento das suas solicitações para o servidor desejado e retorna a resposta do servidor ao seu cliente. Além disso, os proxies são fundamentais para contornar as restrições baseadas em IPs que podem bloquear atividades de extração de dados da web ou limitar o acesso a determinados sites, além de oferecerem benefícios como armazenar em cache as respostas do servidor para reduzir o número de solicitações diretas ao servidor de destino.

Esta introdução descreve os fundamentos para a utilização eficaz de um proxy com o Guzzle.

Requisitos para os primeiros passos — como integrar

Antes de continuar, certifique-se de ter a versão 7.2.5 ou superior do PHP e o Composer instalado em seu sistema. Também será benéfico compreender as noções básicas de extração de dados da web com PHP para acompanhar este guia. Comece criando um novo diretório para o seu projeto e use o Composer para instalar o Guzzle dentro dele:

composer require guzzlehttp/guzzle

Em seguida, crie um arquivo PHP dentro do diretório recém-estabelecido e inclua o carregador automático do Composer para continuar:

<?php
// Load Composer's autoloader
require 'vendor/autoload.php';

Feito isso, estaremos prontos para configurar o proxy.

Como utilizar um proxy com o Guzzle

Este segmento demonstra como emitir uma solicitação por meio do Guzzle usando um proxy e como autenticá-la. Inicialmente, forneça os proxies, garantindo que estejam ativos e sigam o formato: ://:@:.

Informação importante: o Guzzle permite o uso de proxies por meio de opções de solicitação ou middlewares. Para configurações simples e inalteradas de proxies, as opções de solicitação são adequadas. Por outro lado, middlewares oferecem maior flexibilidade e controle, embora com maior necessidade de configuração inicial.

Vamos nos aprofundar nas duas abordagens, começando com as opções de solicitação, envolvendo a importação das classes Client e RequestOptions do Guzzle para configuração.

Método A: configurar um proxy com o Guzzle com opções de solicitação

Para configurar um proxy com opções de solicitação, comece importando as classes Client e RequestOptions do Guzzle:

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

Em seguida, defina seu URL de destino e uma matriz associativa de todos os proxies que você usará:

# make request to
$targetUrl = 'https://lumtest.com/myip.json';

# proxies
$proxies = [
    'http'  => 'http://USERNAME:[email protected]:22225',
    'https' => 'http://USERNAME:[email protected]:22225',
];

O URL de destino especificado, lumtest, foi projetado para retornar o endereço IP de qualquer cliente que emita uma solicitação GET para ele. Essa configuração permite que o Guzzle gerencie tráfego HTTP e HTTPS, encaminhando-o por meio dos proxies HTTP e HTTPS designados, conforme cada caso.

Em seguida, iniciaremos uma instância do cliente Guzzle, incorporando os proxies definidos anteriormente, atribuindo-os à opção de proxy na configuração do Guzzle.

$client = new Client([
RequestOptions::PROXY => $proxies,
RequestOptions::VERIFY => false, # disable SSL certificate validation
RequestOptions::TIMEOUT => 30, # timeout of 30 seconds
]);

Devido ao fato de os servidores de proxies frequentemente encontrarem problemas com a verificação SSL, essa configuração opta por desativá-la por meio da opção de verificação. Além disso, a configuração de tempo limite restringe a duração de cada solicitação para no máximo trinta segundos. Após essa configuração, executaremos a solicitação e exibiremos a resposta resultante.

try {
$body = $client->get($targetUrl)->getBody();
echo $body->getContents();
} catch (\Exception $e) {
echo $e->getMessage();
}

Agora, seu script PHP deverá estar assim:

'http://USERNAME:[email protected]:22225', 'https' => 'http://USERNAME:[email protected]:22225', ]; $client = new Client([ RequestOptions::PROXY => $proxies, RequestOptions::VERIFY => false, # disable SSL certificate validation RequestOptions::TIMEOUT => 30, # timeout of 30 seconds ]); try { $body = $client->get($targetUrl)->getBody(); echo $body->getContents(); } catch (\Exception $e) { echo $e->getMessage(); } ?>

Execute seu script com o comando php .php e você receberá um resultado semelhante ao exemplo abaixo:

{"ip":"212.80.220.187","country":"IE","asn":{"asnum":9009,"org_name":"M247 Europe SRL"},"geo":{"city":"Dublin","region":"L","region_name":"Leinster","postal_code":"D12","latitude":53.323,"longitude":-6.3159,"tz":"Europe/Dublin","lum_city":"dublin","lum_region":"l"}}

Excelente! O valor da chave ip corresponde ao endereço IP do cliente que estiver iniciando a solicitação para a lumtest. Nesse caso, ele deve refletir os proxies que você configurou.

Abordagem B: utilizando um middleware

O emprego de um middleware para configurar um proxy HTTP no Guzzle segue um padrão semelhante ao do primeiro método. A única distinção está em criar e incorporar o middleware com proxy na pilha de handlers predefinidos.

Para começar, ajuste sua importação da seguinte forma:

# ...
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\HandlerStack;
# ...

Em seguida, estabeleça um middleware com proxy inserindo o código a seguir imediatamente após a sua matriz $proxies. Esse middleware interceptará cada solicitação e configurará os proxies conforme cada caso.

function proxy_middleware(array $proxies) 
{
    return function (callable $handler) use ($proxies) {
        return function (RequestInterface $request, array $options) use ($handler, $proxies) {
            # add proxy to request option
            $options[RequestOptions::PROXY] = $proxies; 
            return $handler($request, $options);
        };
    };
}

Agora, podemos integrar o middleware na pilha de handlers predefinidos e atualizar nosso cliente Guzzle incorporando a pilha:

$stack = HandlerStack::create();
$stack->push(proxy_middleware($proxies));

$client = new Client([
    'handler' => $stack,
    RequestOptions::VERIFY => false, # disable SSL certificate validation
    RequestOptions::TIMEOUT => 30, # timeout of 30 seconds
]);

Seu script PHP deverá estar assim:

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\HandlerStack;
# make request to
$targetUrl = 'https://lumtest.com/myip.json';

# proxies
$proxies = [
    'http'  => 'http://USERNAME:[email protected]:22225',
    'https' => 'http://USERNAME:[email protected]:22225',
];
function proxy_middleware(array $proxies) 
{
    return function (callable $handler) use ($proxies) {
        return function (RequestInterface $request, array $options) use ($handler, $proxies) {
            # add proxy to request option
            $options[RequestOptions::PROXY] = $proxies; 
            return $handler($request, $options);
        };
    };
}

$stack = HandlerStack::create();
$stack->push(proxy_middleware($proxies));

$client = new Client([
    'handler' => $stack,
    RequestOptions::VERIFY => false, # disable SSL certificate validation
    RequestOptions::TIMEOUT => 30, # timeout of 30 seconds
]);

try {
    $body = $client->get($targetUrl)->getBody();
    echo $body->getContents();
} catch (\Exception $e) {
    echo $e->getMessage();
}

?>

Execute o script PHP mais uma vez e você obterá resultados semelhantes aos do outro método.

A implementação de um proxy rotativo com o Guzzle envolve a utilização de um servidor proxy que muda frequentemente os endereços IP. Essa abordagem ajuda a contornar o bloqueio de IPs, pois cada solicitação se origina de um IP distinto, complicando a identificação de bots originados de uma única fonte.

Começaremos implementando um proxy rotativo com o Guzzle. Isso é bastante fácil ao utilizar os serviços de proxies da Bright Data, por exemplo:

function get_random_proxies(): array {
    // Base proxy URL before the session identifier
    $baseProxyUrl = 'http://USERNAME-session-';

   
    $sessionSuffix = rand(1000, 9999); // Random integer between 1000 and 9999
    $proxyCredentials = ':[email protected]:22225';

    $httpProxy = $baseProxyUrl . $sessionSuffix . $proxyCredentials;
    $httpsProxy = $baseProxyUrl . $sessionSuffix . $proxyCredentials;


    $proxies = [
        'http'  => $httpProxy,
        'https' => $httpsProxy,
    ];

    return $proxies;
}

Agora, adicione a função pretendida e chame-a:

function rotating_proxy_request(string $http_method, string $targetUrl, int $max_attempts = 3): string
{
    $response = null;
    $attempts = 1;

    while ($attempts <= $max_attempts) {
        $proxies = get_random_proxies();
        echo "Using proxy: ".json_encode($proxies).PHP_EOL;
        $client = new Client([
            RequestOptions::PROXY => $proxies,
            RequestOptions::VERIFY => false, # disable SSL certificate validation
            RequestOptions::TIMEOUT => 30, # timeout of 30 seconds
        ]);
        try {
            $body = $client->request(strtoupper($http_method), $targetUrl)->getBody();
            $response = $body->getContents();
            break;
        } catch (\Exception $e) {
            echo $e->getMessage().PHP_EOL;
            echo "Attempt ".$attempts." failed!".PHP_EOL;
            if ($attempts < $max_attempts) {
                echo "Retrying with a new proxy".PHP_EOL;
            }
            $attempts += 1;
        }
    }
    return $response;
}

$response = rotating_proxy_request('get', 'https://lumtest.com/myip.json');

echo $response;

Aqui está o script PHP completo:

<?php
# composer's autoloader
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

function get_random_proxies(): array {
    // Base proxy URL before the session identifier
    $baseProxyUrl = 'http://USERNAME-session-';

    // Session ID suffix and proxy credentials
    $sessionSuffix = rand(1000, 9999); // Random integer between 1000 and 9999
    $proxyCredentials = ':[email protected]:22225';

    // Assemble the full proxy URLs with the randomized session ID
    $httpProxy = $baseProxyUrl . $sessionSuffix . $proxyCredentials;
    $httpsProxy = $baseProxyUrl . $sessionSuffix . $proxyCredentials;

    // Package and return the proxies
    $proxies = [
        'http'  => $httpProxy,
        'https' => $httpsProxy,
    ];

    return $proxies;
}


function rotating_proxy_request(string $http_method, string $targetUrl, int $max_attempts = 3): string
{
    $response = null;
    $attempts = 1;

    while ($attempts <= $max_attempts) {
        $proxies = get_random_proxies();
        echo "Using proxy: ".json_encode($proxies).PHP_EOL;
        $client = new Client([
            RequestOptions::PROXY => $proxies,
            RequestOptions::VERIFY => false, # disable SSL certificate validation
            RequestOptions::TIMEOUT => 30, # timeout of 30 seconds
        ]);
        try {
            $body = $client->request(strtoupper($http_method), $targetUrl)->getBody();
            $response = $body->getContents();
            break;
        } catch (\Exception $e) {
            echo $e->getMessage().PHP_EOL;
            echo "Attempt ".$attempts." failed!".PHP_EOL;
            if ($attempts < $max_attempts) {
                echo "Retrying with a new proxy".PHP_EOL;
            }
            $attempts += 1;
        }
    }
    return $response;
}

$response = rotating_proxy_request('get', 'https://lumtest.com/myip.json');

echo $response;

Conclusão

Neste guia, falamos sobre as etapas necessárias para integrar proxies com o Guzzle. Você aprendeu:

  • Os fundamentos do emprego de um proxy ao trabalhar com o Guzzle.
  • Estratégias para implementar um sistema de proxies rotativos.

A Bright Data oferece um serviço confiável de proxies rotativos acessível por meio de chamadas de API, junto com recursos sofisticados projetados para contornar medidas antibots, aumentando a eficiência de seus esforços de extração de dados da web.