用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头部信息的一个数组

后者是将头文件的信息以数据流的方式输出。

Curl 采集乱码 gzip 原因及解决方案 utf-8的更多相关文章

  1. PHP接收GET中文参数乱码的原因及解决方案

    方案1: $str = iconv("gb2312","utf-8",$str); 方案2: mb_convert_encoding($str, "u ...

  2. hive表数据导出到csv乱码原因及解决方案

    转载自http://blog.csdn.net/lgdlxc/article/details/42126225 Hive表中的数据使用hive - e"select * from table ...

  3. php中文乱码问题的终极解决方案汇总

    乱码是我们在开发可能经常遇见,也是最让人头疼的一个问题了,下面这篇文章主要介绍了在php开发中,可能遇见中文乱码问题的终极解决方案,文中介绍好几个情况下的解决方法,需要的朋友可以参考借鉴,下面来一起看 ...

  4. 玩转Windows服务系列——无COM接口Windows服务启动失败原因及解决方案

    将VS创建的Windows服务项目编译生成的程序,通过命令行 “服务.exe -Service”注册为Windows服务后,就可以通过服务管理器进行管理了. 问题 通过服务管理器进行启动的时候,发现服 ...

  5. php curl 采集

    curl 采集五个步骤: 1.curl_init()初始化curl 2.curl_setopt()设置传输数据和参数 3.curl_exec()执行传输并获取返回数据 4.curl_errono()返 ...

  6. PowerDesigner16.5 连64位MySQL,出错:SQLSTATE = IM014。原因及解决方案

  7. NIOS II CPU复位异常的原因及解决方案

    NIOS II CPU复位异常的原因及解决方案   近期在用nios ii做项目时,发现一个奇怪的现象,在NIOS II EDS软件中编写好的代码,烧写到芯片中,第一次能够正常运行,但是当我按下板卡上 ...

  8. mysql保存中文乱码的原因和解决办法

    当你遇到这个mysql保存中文乱码问题的时候,期待找到mysql保存中文乱码的原因和解决办法这样一篇能解决问题的文章是多么激动人心.    也许30%的程序员会选择自己百度,结果发现网友已经贴了很多类 ...

  9. oracle超出打开游标的最大数的原因和解决方案

    oracle超出打开游标的最大数的原因和解决方案 分类: Oracle相关2012-06-05 10:36 6362人阅读 评论(0) 收藏 举报 oracle数据库sqljavasessionsys ...

随机推荐

  1. 完美转换MySQL的字符集 Mysql 数据的导入导出,Mysql 4.1导入到4.0

    MySQL从4.1版本开始才提出字符集的概念,所以对于MySQL4.0及其以下的版本,他们的字符集都是Latin1的,所以有时候需要对mysql的字符集进行一下转换,MySQL版本的升级.降级,特别是 ...

  2. 升级cocoapods到1.2 beta版本的方法

    最近写Swfit3.0, 要用到一些框架, 然后就用cocoapods嘛, 结果说要cocoapods1.1.0版本才行, 而自己的是cocoapods1.0.1版本的, 所以就想着升级嘛, 结果就遇 ...

  3. nginx日志格式含义

  4. CSS学习之盒子模式

    从CSS角度来看,页面上每个元素都是一个盒子,不管是块元素还是内敛元素等.而这个盒子由四个部分组成.内容区,补白,边框,边界,下面来介绍下这四种元素. 1 内容 每个元素都是以某些内容开始的,比如文本 ...

  5. hdu 4634 Swipe Bo 搜索

    典型的bfs模拟 (广度优先搜索) ,不过有好多细节要注意,比如图中如果是  R#  走到这个R的话就无限往右走了,这样就挂了~肯定到不了出口.还有一种容易造成死循环的,比如 #E## DLLL D. ...

  6. SQL语法集锦一:显示每个类别最新更新的数据

    本文转载http://www.cnblogs.com/lxblog/archive/2012/09/28/2707504.html (1)显示每个类别最新更新的数据 在项目中经常遇到求每个类别最新显示 ...

  7. WCF客户端和服务器时间不一致,导致通道建立失败的问题)

    本文转载:http://www.cnblogs.com/bcbr/articles/2288374.html 最近,经常有客户反应,前天还用的好好的系统,今天就不能用了. 考虑到系统近来没有做过改动和 ...

  8. Uncaught TypeError: Cannot read property 'post' of undefined

  9. 【PHP分享】Windows tail工具分享

    作者:zhanhailiang 日期:2014-09-28 在Linux下能够使用tail -f工具实时查看输出的日志.近期切换到本地Windows开发环境,顿时有点不爽.百度了下,最终找到tail的 ...

  10. Ⅰ.AngularJS的点点滴滴--引导

    AngularJS已经被很多人像炒冷饭一样炒过啦,大部分都是直接复制官方文档没有说明一些注意事项,不过什么都要从头开始吧 页面引导实例化 1.自动实例化 <html> <script ...