Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 611|回复: 0

Curl 采集乱码 gzip 原因及解决方案 utf-8

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-19 14:53:00 | 显示全部楼层 |阅读模式

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

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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-16 18:03 , Processed in 0.069892 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表