使用 file_get_contents 抓取開啟了 Gzip 壓縮的網頁時,直接輸出會顯示亂碼。判斷依據:響應頭包含 Content-Encoding: gzip。本文提供三種成熟解決方案。
前提:服務器已安裝並啟用 zlib 擴展。
$data = file_get_contents("compress.zlib://" . $url);
PHP 會自動解壓並返回原始內容,無需額外代碼。
function curl_get($url, $gzip = false) {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
if ($gzip) curl_setopt($curl, CURLOPT_ENCODING, "gzip"); // 關鍵行
$content = curl_exec($curl);
curl_close($curl);
return $content;
}
// 使用
$html = curl_get('https://example.com', true);
優勢:cURL 更穩健,支援 HTTPS、重定向、超時控制、自定義 Header 等複雜場景。
若環境禁用 zlib 且無 cURL,可用純 PHP 實現 RFC 1952 標準解壓:
function gzdecode($data) {
$len = strlen($data);
if ($len < 18 || substr($data, 0, 2) !== "\x1f\x8b") return null; // 非 GZIP
$method = ord($data[2]);
$flags = ord($data[3]);
if ($flags & 31 != $flags) return null; // 保留位檢查
$mtime = unpack("V", substr($data, 4, 4)); $mtime = $mtime[1];
$xfl = $data[8];
$os = $data[8];
$headerlen = 10;
$extralen = 0;
$extra = "";
// 處理 FEXTRA
if ($flags & 4) {
if ($len - $headerlen - 2 < 8) return false;
$extralen = unpack("v", substr($data, 8, 2)); $extralen = $extralen[1];
if ($len - $headerlen - 2 - $extralen < 8) return false;
$extra = substr($data, 10, $extralen);
$headerlen += 2 + $extralen;
}
// 處理 FNAME
$filenamelen = 0; $filename = "";
if ($flags & 8) {
if ($len - $headerlen - 1 < 8) return false;
$filenamelen = strpos(substr($data, 8 + $extralen), chr(0));
if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) return false;
$filename = substr($data, $headerlen, $filenamelen);
$headerlen += $filenamelen + 1;
}
// 處理 FCOMMENT
$commentlen = 0; $comment = "";
if ($flags & 16) {
if ($len - $headerlen - 1 < 8) return false;
$commentlen = strpos(substr($data, 8 + $extralen + $filenamelen), chr(0));
if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) return false;
$comment = substr($data, $headerlen, $commentlen);
$headerlen += $commentlen + 1;
}
// 處理 FHCRC
$headercrc = "";
if ($flags & 1) {
if ($len - $headerlen - 2 < 8) return false;
$calccrc = crc32(substr($data, 0, $headerlen)) & 0xffff;
$headercrc = unpack("v", substr($data, $headerlen, 2)); $headercrc = $headercrc[1];
if ($headercrc != $calccrc) return false;
$headerlen += 2;
}
// 解壓主體
$datacrc = unpack("V", substr($data, -8, 4)); $datacrc = $datacrc[1];
$isize = unpack("V", substr($data, -4)); $isize = $isize[1];
$bodylen = $len - $headerlen - 8;
if ($bodylen < 1) return null;
$body = substr($data, $headerlen, $bodylen);
$data = "";
if ($bodylen > 0) {
switch ($method) {
case 8: $data = gzinflate($body); break; // DEFLATE
default: return false;
}
}
// 校驗 CRC32 與長度
if ($isize != strlen($data) || crc32($data) != $datacrc) return false;
return $data;
}
// 使用
$html = file_get_contents('https://www.example.com/');
$html = gzdecode($html);
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| zlib:// 流 | 環境有 zlib,代碼極簡 | 一行代碼,無依賴 | 無法控制超時/重定向 |
| cURL + ENCODING | 生產環境首選 | 功能全、穩健、自動處理多種編碼 | 需 cURL 擴展 |
| 手寫 gzdecode | 極受限環境 | 純 PHP,零擴展依賴 | 代碼長、維護難、性能較差 |
實際項目中強烈建議方案二,兼容性最好且代碼可維護性最高。