PHP採集到的資料是chunked傳輸編碼,gzip壓縮格式的
chunk編碼的思路貌似是: 將資料分塊傳輸,每一塊分為頭部和主體欄位,頭部包含主體資訊的長度且以16進位表示,頭部和主體以斷行符號分行符號分隔,最後一塊以單行的0表示分塊結束。。
回應標頭資訊:
Array( [0] => HTTP/1.1 200 OK [1] => Server: Dict/34002 [2] => Date: Wed, 17 Dec 2014 06:49:22 GMT [3] => Content-Type: text/html; charset=utf-8 [4] => Transfer-Encoding: chunked [5] => Connection: keep-alive [6] => Keep-Alive: timeout=60 [7] => Cache-Control: private [8] => Last-Modified: Wed, 17 Dec 2014 04:57:49 GMT [9] => Expires: Wed, 17 Dec 2014 06:49:22 GMT [10] => Set-Cookie: uvid=VJEncoTSVYJC; expires=Thu, 31-Dec-37 23:55:55 GMT; domain=.dict.cn; path=/ [11] => Content-Encoding: gzip)
if($this->response_num==200) {if($this->is_chunked){//讀取chunk頭部資訊,擷取chunk主體資訊的長度$chunk_size = (int)hexdec(fgets($this->conn));//while(!feof($this->conn) && $chunk_size > 0) { //讀取chunk頭部指定長度的資訊$this->response_body .= fread( $this->conn, $chunk_size ); fseek($this->conn, 2, SEEK_CUR);$chunk_size = (int)hexdec(fgets( $this->conn,4096)); } }else{$len=0;//讀取請求返回的主體資訊while($items = fread($this->conn, $this->response_body_length)){$len = $len+strlen($items);$this->response_body = $items;//當讀取完請求的主體資訊後跳出迴圈,不這樣做,貌似會被阻塞!!!if($len >= $this->response_body_length){break;}}} if($this->is_gzip) { $this->response_body = gzinflate(substr($this->response_body,10)); }$this->getTrans($this->response_body); }
基本上每次都會出現這個提示:
Warning: gzinflate(): data error in E:\CodeEdit\php\http\dict.php on line 384
偶爾能正常解析,應該是chunked解碼有問題,查看過一些資料,也變換過集中解碼方式,但還是功虧一簣
回複討論(解決方案)
你可用 gzdecode 解碼
你可用 gzdecode 解碼
奇怪的是有時可以擷取到結果,比如:
int.(打招呼)喂;你好
有時提示錯誤,比如:
Warning: gzinflate(): data error in E:\CodeEdit\php\http\dict.php on line 380
估計錯誤還是出現在chunked解碼這塊,這裡的問題是返回的資料是先經過gzip壓縮,然後通過chunked分塊傳輸的,所以解碼的過程就是反過來的
你可用 gzdecode 解碼
if($this->is_chunked){/* //讀取chunk頭部資訊,擷取chunk主體資訊的長度$chunk_size = (int)hexdec(trim(fgets($this->conn)));while(!feof($this->conn) && $chunk_size > 0) { //讀取chunk頭部指定長度的資訊$this->response_body .= fread( $this->conn, $chunk_size ); fseek($this->conn, 2, SEEK_CUR);$next_line = trim(fgets($this->conn));if($next_line === '0'){echo $next_line;exit();}else{$chunk_size = (int)hexdec($next_line);} } */while(!feof($this->conn)){$this->response_body .= fread($this->conn, 1024);}if(preg_match_all("#\r\n#i", $this->response_body, $match)){$result=preg_split("#\r\n#i", $this->response_body, -1, PREG_SPLIT_NO_EMPTY );// echo "";// print_r($result); /* foreach($result as $v){echo $v."
";}echo ""; *//* echo hexdec($result[0])."
";echo mb_strlen($result[1])+mb_strlen($result[2])."
"; */$len = count($result);$this->response_body='';for($i=1; $i<$len-1; $i++){$this->response_body .= $result[$i];}//echo strlen($this->response_body); exit();}else{die("匹配結束符失敗");}}
基本思路,首先把頭部改成connection:close,這樣可以通過while(!feof($this->conn))一次性讀取所有的資料
然後因為chunk分塊傳輸的頭部和主體之間是用斷行符號換行分隔的,所以直接用正則分割,得到一個數組包含資料長度和資料的數組,第一項表示所有資料的總長度(而不是每一個chunk分塊的長度,這個貌似和chunk編碼有點出入,也難怪按照chunk編碼會失敗,),最後一個數組項為0表示結束.。。。反覆測試,OK了