如何使用 Guzzle 设置代理

使用Guzzle和代理应对网页抓取的挑战。确保PHP 7.2.5+和Composer已设置,然后集成Guzzle进行代理配置。
1 min read
Setting-Proxy-in-Guzzle

在使用 Guzzle 的情况下,代理可充当连接客户端应用程序与目标 Web 服务器的中介服务器。它有助于将请求转发到所需服务器,并将服务器的响应返回至您的客户端。此外,代理能够规避基于 IP 的限制,这些限制可能会屏蔽网页抓取活动或使某些网站的访问受限,此外还会提供诸多好处,比如缓存服务器响应,以减少对目标服务器的直接请求数量。

本文概述了通过 Guzzle 有效使用代理的要点。

入门要求 – 如何集成

在继续操作前,请确保您在系统中安装了 PHP(7.2.5 或更高版本)和 Composer。对使用 PHP 进行网页抓取有基本的了解也有益于遵循本指南完成操作。首先为您的项目创建新目录,然后使用 Composer 在该目录中安装 Guzzle:

composer require guzzlehttp/guzzle

接下来,在新建目录中创建 PHP 文件并添加 Composer 的自动加载器,然后继续:

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

这样,我们就可以配置代理设置了。

通过 Guzzle 使用代理

本节演示了如何通过使用代理的 Guzzle 发出请求并对其进行身份验证。首先是源代理,确保它们处于活动状态并遵循以下格式:://:@:

核心点:Guzzle 允许通过请求选项或中间件使用代理。对于固定不变的简单代理设置,适合使用请求选项。相反,中间件可提供更强的灵活性和控制力,即使初始配置较多也能如此。

我们接下来将深入探究这两种方法,首先从请求选项开始,其中涉及导入 Guzzle 的 Client 和 RequestOptions 类来完成设置。

方法 A:使用请求选项设置 Guzzle 代理

要使用请求选项设置代理,首先导入 Guzzle 的 Client 和 requestOptions 类:

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

然后,定义目标 URL 和包含所有待使用代理的关联数组:

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

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

指定的目标 URL (lumtest) 旨在返回向其发出 GET 请求的任何客户端的 IP 地址。此设置允许 Guzzle 管理 HTTP 和 HTTPS 流量,从而通过 HTTP 和 HTTPS 代理路由流量。

接下来,我们会启动 Guzzle 客户端实例,通过将之前定义的代理分配给 Guzzle 配置中的代理选项来合并这些代理。

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

由于代理服务器经常出现 SSL 验证问题,此设置选择通过 verify 选项来禁用验证。此外,超时设置可将每个请求的持续时间限制在 30 秒以内。您可以根据以下配置执行请求并显示生成的响应。

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

现在,您的 PHP 脚本应如下方所示:

'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(); } ?>

使用 php .php 命令执行脚本后,您会收到类似于以下示例中的输出:

{"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"}}

太棒了!IP 密钥值与向 lumtest 发起请求的客户端的 IP 地址相对应。此时,它应显示出您配置的代理。

方法 B:使用中间件

使用中间件设置 Guzzle HTTP 代理遵循与第一种方法类似的模式。唯一的区别在于您需要创建代理中间件并将其合并到默认处理程序堆栈中。

首先,按如下方式调整导入设置:

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

然后,直接在 $proxies 数组后插入以下代码来创建代理中间件。该中间件会拦截每个请求并相应地配置代理。

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);
        };
    };
}

现在,我们可以将中间件集成到默认处理程序堆栈中,并通过合并堆栈来刷新 Guzzle 客户端:

$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
]);

您的 PHP 脚本应如下方所示:

<?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();
}

?>

再次执行 PHP 脚本,您会获得类似于使用其他方法产生的结果。

使用 Guzzle 实现轮换代理涉及使用经常变更 IP 地址的代理服务器。由于每个请求都来自不同 IP,这种方法有助于规避 IP 封锁,使得识别来自单一来源的机器人的过程变得复杂化。

我们首先来通过 Guzzle 实现轮换代理,这在使用 Bright Data 代理服务时很容易操作,例如:

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;
}

现在,添加目标函数并将其命名:

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;

下面给出了完整的 PHP 脚本:

<?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;

结语

在本指南中,我们介绍了在 Guzzle 中集成代理所需的步骤。您学到了以下内容:

  • 操作 Guzzle 时使用代理的基础知识。
  • 实现轮换代理系统的策略。

Bright Data 提供了可通过 API 调用访问的可靠轮换代理服务,以及旨在规避反机器人措施、提高抓取效率的复杂功能。