用curl獲取一個經過gzip壓縮后的網頁時返回亂碼
原因大體就是服務器返回的Content-Encoding的值和網頁的編碼不同,造成curl解碼出問題,直接將gzip或deflate編碼的文件下載了,所以看起來是亂碼了。
Content-Encoding: gzip
讀取前幾個字節為:1F 8B 08 ,其中1F 8B表明為gzip壓縮,而08表示為deflate壓縮。
這樣實際編碼和通過Content-Encoding獲取的編碼不一樣,所以curl解碼出錯,導致下載的是未解碼的頁面,也就是一堆亂碼。
知道了原因,就有了解決方案了
可以通過讀取下載的二進制文件的前3個字節,來判斷是否是壓縮文件
以下是采集163和sohu(gzip過) 的首頁的不同方法
$curl=curl_init('http://www.163.com');
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
curl_setopt($curl,CURLOPT_USERAGENT,'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)');
$html=curl_exec($curl);
var_dump($html);
$curl=curl_init('http://www.sohu.com');
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
curl_setopt($curl,CURLOPT_USERAGENT,'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)');
$html=curl_exec($curl);
//$html=strstr($html,'<');
$html=gzdecode($html);
var_dump($html);
function gzdecode($data) {
$len = strlen($data);
if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
return null; // Not GZIP format (See RFC 1952)
}
$method = ord(substr($data,2,1)); // Compression method
$flags = ord(substr($data,3,1)); // Flags
if ($flags & 31 != $flags) {
// Reserved bits are set -- NOT ALLOWED by RFC 1952
return null;
}
// NOTE: $mtime may be negative (PHP integer limitations)
$mtime = unpack("V", substr($data,4,4));
$mtime = $mtime[1];
$xfl = substr($data,8,1);
$os = substr($data,8,1);
$headerlen = 10;
$extralen = 0;
$extra = "";
if ($flags & 4) {
// 2-byte length prefixed EXTRA data in header
if ($len - $headerlen - 2 < 8) {
return false; // Invalid format
}
$extralen = unpack("v",substr($data,8,2));
$extralen = $extralen[1];
if ($len - $headerlen - 2 - $extralen < 8) {
return false; // Invalid format
}
$extra = substr($data,10,$extralen);
$headerlen += 2 + $extralen;
}
$filenamelen = 0;
$filename = "";
if ($flags & 8) {
// C-style string file NAME data in header
if ($len - $headerlen - 1 < 8) {
return false; // Invalid format
}
$filenamelen = strpos(substr($data,8+$extralen),chr(0));
if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
return false; // Invalid format
}
$filename = substr($data,$headerlen,$filenamelen);
$headerlen += $filenamelen + 1;
}
$commentlen = 0;
$comment = "";
if ($flags & 16) {
// C-style string COMMENT data in header
if ($len - $headerlen - 1 < 8) {
return false; // Invalid format
}
$commentlen = strpos(substr($data,8+$extralen+$filenamelen),chr(0));
if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
return false; // Invalid header format
}
$comment = substr($data,$headerlen,$commentlen);
$headerlen += $commentlen + 1;
}
$headercrc = "";
if ($flags & 1) {
// 2-bytes (lowest order) of CRC32 on header present
if ($len - $headerlen - 2 < 8) {
return false; // Invalid format
}
$calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
$headercrc = unpack("v", substr($data,$headerlen,2));
$headercrc = $headercrc[1];
if ($headercrc != $calccrc) {
return false; // Bad header CRC
}
$headerlen += 2;
}
// GZIP FOOTER - These be negative due to PHP's limitations
$datacrc = unpack("V",substr($data,-8,4));
$datacrc = $datacrc[1];
$isize = unpack("V",substr($data,-4));
$isize = $isize[1];
// Perform the decompression:
$bodylen = $len-$headerlen-8;
if ($bodylen < 1) {
// This should never happen - IMPLEMENTATION BUG!
return null;
}
$body = substr($data,$headerlen,$bodylen);
$data = "";
if ($bodylen > 0) {
switch ($method) {
case 8:
// Currently the only supported compression method:
$data = gzinflate($body);
break;
default:
// Unknown compression method
return false;
}
} else {
// I'm not sure if zero-byte body content is allowed.
// Allow it for now... Do nothing...
}
// Verifiy decompressed size and CRC32:
// NOTE: This may fail with large data sizes depending on how
// PHP's integer limitations affect strlen() since $isize
// may be negative for large sizes.
if ($isize != strlen($data) || crc32($data) != $datacrc) {
// Bad format! Length or CRC doesn't match!
return false;
}
return $data;
}
還有文提到:
curl_setopt($this->curl,CURLOPT_ENCODING ,'gzip')
或
zlib庫中的gzuncompress函數
shell用curl抓取頁面亂碼,參考一下2方面:
1.是用curl抓取的數據是用類似gzip壓縮后的數據導致的亂碼。
亂碼:curl www.1ting.com | more
亂碼:curl -H "Accept-Encoding: gzip" www.1ting.com | more
不亂碼:curl -H "Accept-Encoding: gzip" www.1ting.com | gunzip | more
不亂碼:curl www.1616.net | more
亂碼:curl -H "Accept-Encoding: gzip" www.1616.net | more
不亂碼:curl -H "Accept-Encoding: gzip" www.1616.net | gunzip | more
下面的a,b解釋的是www.1ting.com,c,d解釋是的www.1616.net
a.某個url,如果用不加任何選項的curl命令抓取后亂碼,在curl后面加上Accept-Encoding: gzip,后面不加gunzip,則抓取的數據會亂碼。
b.某個url,如果用不加任何選項的curl命令抓取后亂碼,在curl后面加上Accept-Encoding: gzip,后面加上gunzip,則抓取的數據不會亂碼。
c.某個url,如果用不加任何選項的curl命令抓取后不亂碼,在curl后面加上Accept-Encoding: gzip,后面不加gunzip,則抓取的數據會亂碼。
d.某個url,如果用不加任何選項的curl命令抓取后不亂碼,在curl后面加上Accept-Encoding: gzip,后面加上gunzip,則抓取的數據不會亂碼。
小總:
也就是說在curl后面加上Accept-Encoding: gzip,再用gunzip解壓縮,則基本上可以保存數據不亂碼。
2.GBK或者UTF8漢字之類的亂碼
iconv 命令是運行於linux平台的文件編碼裝換工具。當我們在linux系統shell下通過curl命令或者wget命令獲取一個網頁的源代碼,當網頁的編 碼與當前操作系統壞境的設置的編碼不同時,就會發現網頁中有很多亂碼。如在網頁"meta"標簽"charset"屬性值設置為"gb2312"的http://www.baidu.com百 度首頁,在系統壞境變量"$LANG"值為"en_US.UTF-8"的linux系統即會產生中文亂碼現象。這時我們可以嘗試使用iconv命令進行編 碼裝換,讓中文不在是亂碼。如下命令是處理百度在系統壞境變量"$LANG"值為"en_US.UTF-8"的linux系統亂碼的問題的解決方案之一:
curl http://www.baidu.com|iconv -f gb2312 -t utf-8
當然,你也通過改變系統壞境變量與百度首頁的"charset"值一致,也可以解決此亂碼問題,如下命令:
set LANG="gb2312"
export LANG
curl http://www.baidu.com
iconv命令的詳細語法:
iconv [選項..] [文件..]
選項:
-f 輸入編碼
-t 輸出編碼
-l 列出所有已知的編碼
-o 輸出文件
對比采用PHP CURL庫的POST GET HEADER三種方法之間的差異
比較POST GET HEADER這三種方法的區別:
| 參數 |
POST |
GET |
HEADER |
| CURLOPT_URL |
有 |
有 |
有 |
| CURLOPT_POST |
開啟 |
關閉 |
關閉 |
| CURLOPT_HTTPHEADER |
如果有$header,則開啟 |
如果有$header,則開啟 |
如果有$header,則開啟 |
| CURLOPT_HEADER |
False |
False |
True |
| CURLOPT_NOBODY |
false |
False |
true |
| CURLOPT_POSTFILEDS |
True |
false |
false |
從上表中可以看出:
POST方法:開啟POST連接,然后發送POST報文體。關閉HEADER和NOBODY
GET方法:關閉POST相關的選項,關閉NOBODY HEADER,僅僅只是開啟curlopt_httpheader
HEADER方法:開啟HEADER和NOBODY,關閉POST相關的選項。
應該說上述三種方法,一個明顯的區別是,箱采用什么方法的時候,就開啟對應的CURL選項。
CURL_HTTPHEADER與CUROPT_HEADER的區別:
前者是設置HTTP頭部信息的一個數組
后者是將頭文件的信息以數據流的方式輸出。
