用curl獲取一個通過gzip壓縮後的網頁時返回亂碼html
緣由大致就是服務器返回的Content-Encoding的值和網頁的編碼不一樣,形成curl解碼出問題,直接將gzip或deflate編碼的文件下載了,因此看起來是亂碼了。
linux
Content-Encoding: gzip
讀取前幾個字節爲:1F 8B 08 ,其中1F 8B代表爲gzip壓縮,而08表示爲deflate壓縮。
這樣實際編碼和經過Content-Encoding獲取的編碼不同,因此curl解碼出錯,致使下載的是未解碼的頁面,也就是一堆亂碼。
知道了緣由,就有了解決方案了
能夠經過讀取下載的二進制文件的前3個字節,來判斷是不是壓縮文件
shell
如下是採集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;
}
服務器
還有文提到:app
curl_setopt($this->curl,CURLOPT_ENCODING ,'gzip')curl
或函數
zlib庫中的gzuncompress函數工具
shell用curl抓取頁面亂碼,參考一下2方面:this
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頭部信息的一個數組
後者是將頭文件的信息以數據流的方式輸出。