Guzzleでプロキシの設定を行う方法

Guzzleとプロキシを使ってウェブスクレイピングの課題を解決しましょう。PHP 7.2.5以降とComposerが設定されていることを確認した後、Guzzleを使用してプロキシの設定を行います。
1 min read
Setting a proxy in Guzzle blog image

Guzzleを使用する際、プロキシはクライアントアプリケーションと目的のウェブサーバーの橋渡しをする中間サーバーとして機能してくれます。プロキシサーバーはリクエストを目的のサーバーに転送し、サーバーのレスポンスをクライアントに返します。また、プロキシはIPベースのボット対策プログラムを回避するのに役立ちます。ボット対策を受けると、ウェブスクレイパーの動作がブロックされたり、特定のウェブサイトへのアクセスが制限されたりしてしまいます。さらに、プロキシにはサーバーからのレスポンスをキャッシュして、サーバーに直接リクエストを送る回数を減らせるという利点もあります。

以下では、Guzzleでプロキシを効果的に利用するために必要な知識を学んでいきます。

始める前の要件 〜 プロキシの使用準備

チュートリアルを始める前に、システムにバージョン7.2.5以上のPHPとComposerがインストールされていることを確認してください。PHPでのウェブスクレイピングの基礎的な知識があると、このガイドを理解しやすくなります。はじめにプロジェクト用の新しいディレクトリを作成し、Composerを使用してそのディレクトリの中にGuzzleをインストールします:

composer require guzzlehttp/guzzle

次に、新しく作成したディレクトリ内にPHPファイルを作成し、Composerのオートローダーを有効にします:

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

これで、プロキシの設定を行う準備が整いました。

Guzzleでプロキシを使う

以下では、Guzzleを使ってプロキシ経由でリクエストを送り、認証を行う方法を紹介しています。最初にプロキシ設定を反映させます。プロキシが有効で、次のフォーマットで指定されていることを確認してください: //: @:

注意:Guzzleでプロキシを使う際は、リクエストのOPTIONSメソッドか、ミドルウェアを使用します。プロキシ設定が複雑でなく、変更する必要がない場合は、OPTIONSメソッドがおすすめです。一方で、ミドルウェアは初期設定が多いものの、柔軟性とコントロールのしやすさという点で優れています。

この記事ではOPTIONSメソッドを使用した方法から始め、両方のアプローチについて詳しく説明します。また、GuzzleのClientクラスとRequestOptionsクラスをインポートし、設定を行う方法についても学んでいきます。

アプローチ A:リクエストのOPTIONSメソッドを使用し、Guzzleでプロキシを設定する

リクエストのOPTIONSメソッドを使用してプロキシを設定するには、まずGuzzleのClientクラスと RequestOptionsクラスをインポートします。

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

次に目的のURLと、使用するすべてのプロキシの情報が含まれた配列(array)を定義します。

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

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

ここでは「lumtest」を目的のURLとして指定しています。このサイトが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認証での問題がよく発生するため、今回は認証オプションでSSL認証を無効にしています。さらに、タイムアウト設定で各リクエストの最長待機時間を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アドレスです。この場合、設定したプロキシの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経由でアクセスできる信頼性の高いローテーションプロキシサービスや、ボット対策回避のための高度な機能を提供しており、これらを利用することでスクレイピング作業の効率が高まります。