# PHP cURL プロキシスクレイピング: CURLOPT_PROXY、認証とGuzzle

オンラインのほとんどのPHPプロキシスニペットは、2011年からの同じ3つのバグを未だに抱えています。ここでは、実運用に耐えるクリーンなCURLOPT_PROXY設定—認証、SOCKS5、curl_multiローテーション、Guzzle、タイムアウト—を紹介します。

PHP cURL プロキシコードには特有の問題があります。上位のスニペットは10年前のもので、多くが実際のバグを含んでいます。例えば、`curl_exec()`が二重に呼ばれ、リクエストが二度実行される、`CURLOPT_SSL_VERIFYPEER`が無効化されて「HTTPSを動作させる」、既にデフォルトの値に設定されているオプションなど。このガイドは現在の正しいバージョンです: 認証付き`CURLOPT_PROXY`、SOCKS5、`curl_multi`を使った並列ローテーション、Guzzleでの同様の設定、そしてワーカーが一晩中ハングしないようにするタイムアウト。

## 最小限で正しいPHP cURL プロキシの例

```php
<?php
function fetch(string $url): string|false {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_PROXY          => 'http://residentialpremium.quantumproxies.io:8080',
        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
```

認証情報は`CURLOPT_PROXYUSERPWD`に示されるように設定するか、プロキシURLにインラインで埋め込むことができます（`http://USER:PASS@host:port`）。どちらも機能します。初心者が見落としがちな`CURLOPT_RETURNTRANSFER`も重要です。これがないと、`curl_exec()`はレスポンスをそのまま出力し、`true`を返しますが、これはスクレイパーが望むものではありません。PHP 8ではハンドルが`CurlHandle`オブジェクトであり、リソースではありませんが、オプションの呼び出しは変わりません。

## 人々が模倣するオプション（と1つの本当のセキュリティミス）

- `CURLOPT_HTTPPROXYTUNNEL` — HTTPSターゲットには不要です。cURLはターゲットが`https://`の場合、自動的にCONNECTトンネルを確立します。このオプションは、通常スクレイピングではほとんど必要とされないプレーンHTTPターゲットに対してのみトンネリングを強制します。
- `CURLOPT_CUSTOMREQUEST => 'GET'` — GETは既にデフォルトです。これを設定しても何も変わりませんが、エッジケースでリダイレクト処理を壊すことがあります。
- `curl_exec()`を二度呼び出す — クラシックなStack Overflowのバグ: リクエストが二度実行され、二度目の戻り値が最初のものを上書きします。ハンドルごとに一度だけ。
- `CURLOPT_SSL_VERIFYPEER => false` — 本当に危険なものです。証明書エラーを「修正」するために、すべてのリクエストで中間者攻撃を許可します。PHPマニュアルによると、実際の修正はphp.iniの`curl.cainfo`を最新の`cacert.pem`バンドルに指し示すことです。

![設定すべき6つのオプション（CURLOPT_PROXY、PROXYUSERPWD、RETURNTRANSFER、CONNECTTIMEOUT 10、TIMEOUT 30、ENCODING）と、削除すべきコピペの負債（HTTPPROXYTUNNEL、CUSTOMREQUEST GET、SSL_VERIFYPEER false、二重のcurl_exec）を示したPHPのcurl_setopt_array。カードには、証明書の問題は代わりにcurl.cainfoで直すよう記している。](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335147/blog/generated/qp-b9-008-diag1.png)
*6つのオプションが全体の仕事を担います。右の列は2011年のチュートリアルからのコピーペーストの負債です。*

## PHPでのSOCKS5プロキシ

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

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

スキームがDNSの発生場所を決定します: `socks5://`（および`CURLPROXY_SOCKS5`）はホスト名をサーバー上で解決し、すべてのターゲットドメインをローカルリゾルバに漏らします。`socks5h://`（`CURLPROXY_SOCKS5_HOSTNAME`）はプロキシ出口で解決します。後者を推奨します。QuantumProxiesは[すべてのプランでHTTPとSOCKS5を提供](https://quantumproxies.io/socks5-proxies)しているため、プロトコルの切り替えは一行の変更で済み、新しいサブスクリプションは不要です。同じDNSルールがコマンドラインにも適用されます—このページのすべてのシェル相当のものについては[curlプロキシレシピ](https://quantumproxies.io/blog/curl-with-proxy-examples)をご覧ください。

## curl_multiを使ったローテーションと並列リクエスト

PHPにはasync/awaitはありませんが、並列スクレイピングには必要ありません—`curl_multi`は1つのプロセスで数十の転送を同時に実行します。すべてのハンドルを回転ゲートウェイに向け、各リクエストは自動的に異なる[住宅IP](https://quantumproxies.io/residential-proxies)から退出します。コード内でのプロキシリストの管理は不要です。

```php
<?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@residentialpremium.quantumproxies.io:8080',
        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);
```

バッチは控えめに保ちましょう—8から16の並行ハンドルでほとんどのターゲットには十分です。ペースが生の速度よりも重要です: バッチ間に数百ミリ秒の`usleep()`を入れることで、ほとんどコストをかけずにリクエストレートを429を引き起こす閾値以下に保つことができます。各ハンドルごとに`curl_getinfo($ch, CURLINFO_HTTP_CODE)`を確認し、失敗を再取得するのではなくリトライキューにルートしましょう。フローが複数のリクエストにまたがり、1つのアイデンティティを共有する必要がある場合（ログイン、カート、クッキーを使ったページネーション）、そのハンドルをスティッキーセッションに切り替え、ユーザー名にサフィックスを付けてください: `USER-sid-a1b2c3-ttl-30m`はローテーションせずにその期間中同じ出口IPを保持します。

![PHPのワーカーが8〜16ハンドルのcurl_multiバッチを1つのプロキシURLに送り、ゲートウェイが出口を決める：ローテーションする新しい住宅IP、同じ出口を維持するスティッキーセッション、または地域指定の出口。カードの要点：ローテーション、USER-session-a1b2c3というスティッキー用ユーザー名、地域指定はすべてプロキシURLの中で指定する。](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335147/blog/generated/qp-b9-008-diag2.png)
*ワーカーは単純に、ゲートウェイは賢く: ローテーション、スティッキー性、ジオターゲティングはすべてプロキシURLに含まれています。*

## ヘッダーとクッキー: 主張するブラウザのように見せる

デフォルトのcURLヘッダーを持つクリーンな出口IPでも、PHPが自らを発表しない限り、ボットと見なされます。3つのオプションでギャップの大部分を埋めます: 現在のブラウザ文字列を持つ`CURLOPT_USERAGENT`、`Accept-Language`などのための`CURLOPT_HTTPHEADER`、そして同じファイルを指すクッキーペア`CURLOPT_COOKIEJAR` / `CURLOPT_COOKIEFILE`です。これにより、ブラウザが行うようにセッションクッキーがハンドル間で持続します。クッキーの持続性は人々が予想する以上に重要です: 多くのサイトは最初のヒットでトークンを設定し、それを返さないクライアントを静かに挑戦します。主張するアイデンティティを一貫させましょう—ChromeのUser-Agentに`Accept-Language`がなく、基本的なヘッダーセットだけでは、それ自体がフィンガープリントであり、一貫性チェックは中級のアンチボットベンダーがPHPスクレイパーを2回目のリクエストで捕まえる方法です。

## Guzzle: 同じプロキシ、より良い使い勝手

```php
<?php
use GuzzleHttp\Client;

$client = new Client([
    'proxy'           => 'http://USER:PASS@residentialpremium.quantumproxies.io:8080',
    '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-sid-a1b2c3-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080',
]);
```

Guzzleは内部でcURLを駆動するため、上記のすべてが適用されます—ただし、読みやすくなります。1つの警告: Guzzleのデフォルトの`timeout`は0で、永遠に待機します。転送中に停止するプロキシ出口はPHP-FPMワーカーを無期限に固定するため、構築するすべてのクライアントにタイムアウトを設定してください。`proxy`オプションは、スキームごとのルーティングや内部ホストの除外が必要な場合に`http`、`https`、`no`キーを持つ配列も受け入れます。

## PHP cURLが十分でなくなるとき

cURLはHTMLを取得しますが、JavaScriptを実行しません。レスポンスが空のdivを持つスケルトンページとして戻ってきた場合、データはクライアント側でレンダリングされます—[空ページレスポンス](https://quantumproxies.io/blog/scraper-returns-empty-page-javascript)の検出方法を示すガイドをご覧ください。また、強化されたターゲットでは、cURLのTLSハンドシェイク自体がIPがどれだけクリーンであってもあなたを露呈します（[ブラウザが動作するがcURLは403の謎](https://quantumproxies.io/blog/curl-403-but-browser-works)）。PHPはリクエストごとにヘッドレスブラウザを実行することは現実的ではないため、実用的な逃げ道は[Scraper API](https://quantumproxies.io/scraper-api)です: 単一のエンドポイントへの1回のcURL呼び出しで、レンダリング、ブラウザフィンガープリント、ローテーション、解析がクリーンなMarkdown、JSON、またはHTMLとして戻ってきます。

[PHPからScraper APIを試す](https://quantumproxies.io/scraper-api)

## よくある質問

### PHP cURLでプロキシを使うにはどうすればいいですか？

`CURLOPT_PROXY`にプロキシURL全体を設定し、認証情報が必要な場合は`CURLOPT_PROXYUSERPWD`を追加し、常に`CURLOPT_RETURNTRANSFER`と明示的なタイムアウトを有効にしてください。それが必要なセット全体です—HTTPSターゲットは自動的にトンネルを通過し、その他はすべてオプションの調整です。

### PHPでプロキシのユーザー名とパスワードを渡すにはどうすればいいですか？

`curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:pass')`を使うか、URLに埋め込む: `http://user:pass@host:port`。HTTP 407を受け取った場合、プロキシがその認証情報を拒否したということです—タイプミスを確認し、特殊文字をURLエンコードしてください。IPホワイトリスト認証を使用するプロバイダーは、サーバーのIPが認証されると認証情報は不要です。

### PHP cURLはSOCKS5プロキシを使用できますか？

はい。`socks5h://` URLを`CURLOPT_PROXY`に渡すか、`CURLOPT_PROXYTYPE`を`CURLPROXY_SOCKS5_HOSTNAME`に設定します。ホスト名のバリアントをプレーンな`socks5`よりも優先してください。そうしないと、すべてのターゲットドメインがローカルリゾルバを通じて漏れます。

### CURLOPT_HTTPPROXYTUNNELとは何ですか？それは必要ですか？

これはcURLがプロキシを通じてCONNECTでトンネルを通過するように強制します。`https://`ターゲットの場合、cURLは自動的にトンネルを通過するため、このオプションはほとんどのスクレイピングコードで冗長です。プレーンHTTPターゲットにトンネルセマンティクスが必要な場合にのみ設定してください。

### PHP cURLをプロキシ経由でタイムアウトを設定するにはどうすればいいですか？

両方を設定してください: `CURLOPT_CONNECTTIMEOUT`はプロキシへの接続とトンネルのオープンをカバーし、`CURLOPT_TIMEOUT`は転送全体を制限します。ミリ秒単位のバリアント（`_MS`）は、より厳しい予算に対応します。それらがないと、停止した出口がリクエストを保持し、PHP-FPMの下では、Webサーバーの制限がそれを終了するまでワーカー全体を保持します。

最初のスニペットをコピーし、チェックリストを心に留めておけば、PHPは完全に堅実なスクレイピング言語です: 並列処理には`curl_multi`、アイデンティティには回転ゲートウェイ、使いやすさにはGuzzle、そして対抗するページにはScraper API。

[QuantumProxiesの住宅IPでPHPスクレイパーを実行する](https://quantumproxies.io/residential-proxies)
