Curl 採集亂碼 gzip 緣由及解決方案 utf-8

用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頭部信息的一個數組

後者是將頭文件的信息以數據流的方式輸出。

相關文章
相關標籤/搜索