Curl 采集亂碼 gzip 原因及解決方案 utf-8


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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM