プロキシ経由でダウンロードしたページが不完全に見えるときは、まず cURL の結果から始めます。短い応答、失敗した転送、完全なブロックページでは、それぞれ異なる説明が必要です。

既存の設定済みハンドルで CURLOPT_RETURNTRANSFER を有効にした状態で:

$body = curl_exec($ch);
if ($body === false) {
    throw new RuntimeException(curl_error($ch), curl_errno($ch));
}

$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$downloaded = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD_T);
$declared = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T);

ハンドルを解放する前にこれらの値を収集します。厳密な比較により、転送の失敗と、空文字列や “0” のような正常な本文とを区別できます。HTTP のエラーステータスは自動的に curl_exec の失敗ではないため、ステータスも解釈します。

ダウンロードのカウンタにはヘッダーが含まれません。宣言された値は Content-Length から得られ、サイズが不明なときは -1 になります。応答が Content-Length を正当に省略することはあり、不明な値は切り詰められたファイルの診断ではありません。

自動コンテンツデコードはもう一つの区別を加えます。Content-Length は通常、圧縮された表現を表すのに対し、PHP の文字列には展開後のバイトが入ることがあります。そのヘッダーと strlen($body) を比べることは、異なる表現を比べていることになります。

転送が想定サイズに反するとき、cURL は CURLE_PARTIAL_FILE を報告することがあります。空でない出力をすべて成功とみなすのではなく、そのエラーを保持します。逆に、小さな 200 応答での成功した転送は、配信元やプロキシが返すことを選んだページ全体である場合もあります。

最後に必要な内容を確認してください。cURL が取得した HTML は、ページを埋めるために JavaScript を実行しません。ブラウザーが表示するセクションは、送信されたすべてのバイトが届いていても元の応答に存在しないことがあります。

関連記事