抓取网页常见问题总结

有时候我们需要程序化的方式访问网页, 最典型的应用当然是网络爬虫, 但也可以是其他有用的应用. 和通常的用浏览器访问不同, 爬虫会碰到一些意想不到的问题, 一般大型站点基本都有某种反爬虫策略. 不过理论上, 服务器是无法完全区别浏览器和爬虫的, 只要不是恶意的发送大量请求, 基本可以绕过这些限制, 下面是常见的问题总结.

Referer

某些网页要求Referer来自同一个domain, 解决办法是总是在request中加上Referer.

 
 
    public URL verifyUrl( String url ){
        if ( !url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
            return null;
 
        URL verifiedUrl = null;
        try {
            verifiedUrl = new URL(url);
        } catch ( Exception e ) {
            e.printStackTrace();
            return null;
        }
        return verifiedUrl;
    }
 
    public String getReferer(String url) {
        URL src = verifyUrl(url);
        return "http://" + src.getHost() + "/";
    }
 
            conn.setRequestProperty("Referer", getReferer(url));
 

Cookie

这个会比较棘手, 有些网站会用cookie来确认你的请求是来自真实的浏览器请求, 例如Yandex 搜索引擎. 解决办法是找到这些cookie, 然后在请求中加上.

 
    function set_yandexcookies(){
        $cookies = array();
        $cookies["z"] = "s:l:0x6a0d34a:1447676662221";
        $cookies["spravka"] = "dD0xNDE2MTM0MDM0O2k9MjIyLjQyLjE2MC4xOTt1PTE0MTYxMzQwMzQ3NjgzODg4MTg7aD00Yzk0NzBiZDNhZTBlMTBjMWIxZjEzMjAwOWI2ZGIwMw==";
        $cookies["yandexuid"] = "2621378601447676656";
        $cookies["yp"] = "1450268656.ygu.1";
        $cookies["yandex_gid"] = "10590";
        $cookies["z"] = "m-white_com.webp.css-https%3Awww_w2qSYLIT7nU8xRcDu67CDaXKcQ4%3Al";
        $cookies["_ym_uid"] = "14476766551047775157";
        $cookies["fuid01"] = "5439fdcd2d126029.YHGNLEZRJ7KSx7xXh4GiXkRqqlLUfzW7Oq6Gony7kFmX-OW1YwAe9BfONuz3geaZE421ReHEG-d-otLgtab_ej4noYQVMb8NLQ3nOcnIjDuMhUejtxSyiMO-C1zTn9rE";
        $cookies["ys"] = "wprid.1447676682041160-1437031890021480968510718-ws39-235";
        $cookies["_ym_visorc_11803342"] = "w";
        $this->set_extracookies($cookies);
    }
 

User Agent

一般来说随便选一个User Agent就可以了, 之前一直用的是

 
            con.setRequestProperty("User-Agent",
                                   "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)");
 

Google是少数的几个站点之一, 会针对User Agent的类型做不同的输出, 例如下面的url

 
https://www.google.as/search?q=emacs&sa=X&hl=en&tbm=isch&gbv=2&gws_rd=ssl
 

参数gbv等于2表示支持javascript, 等于1表示不支持javascript, Google的图片搜索有两种格式, 一种是没有javascrit的, 图片只有缩略图, 禁用浏览器的javascript, 就会看到这种格式, 另一种就是常见的瀑布流布局.

如果我们用上面那个User Agent访问, 无论gbv参数是什么都是输出没有javascript的版本. 所以我得到很奇怪的结果, 用java或者php脚本工具访问的时候, 总是得到错误的输出, 只要用浏览器就没事.

 
(def bar (prototype.Util/DownloadPage "https://www.google.as/search?q=emacs&sa=X&hl=en&tbm=isch&gbv=2"))
(print bar)
 

完全没有想到是User Agent的问题, 确认了不是https, cookie, 还有Referer的问题之后, 最终发现是User Agent的问题.

 
 
function testgoogleimage() {
    $url = "https://www.google.as/search?q=emacs&sa=X&hl=en&tbm=isch&gbv=2&gws_rd=ssl";
    $ua = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2054.3 Safari/537.36";
    $crawler_ua = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)";
    $dynamicua = $_SERVER['HTTP_USER_AGENT'];
 
    $ch = curl_init();
 
    @curl_setopt($ch, CURLOPT_URL, $url);
    @curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    if(strtolower(substr($url,0,6)) == 'https:') {
        @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    }
    @curl_setopt($ch, CURLOPT_REFERER,$url);
    @curl_setopt($ch,CURLOPT_AUTOREFERER,true);
    echo "current use user agent: " . $ua . "<br/>\n";
    curl_setopt($ch, CURLOPT_USERAGENT, $ua);
 
    $raw = curl_exec($ch);
 
    $num_imgres = preg_match("/imgres/", $raw);
    $num_images_table = preg_match("/images_table/", $raw);
 
    echo "num_imgres : " . $num_imgres . " num_images_table: ". $num_images_table . "<br/>";
 
}
 

Google图片搜索根据不同的情况会尽量展示合适的输出, 如果用的是版本较新的浏览器但是没有开启javascript, 就会用noscript来跳转, 这个是用gbv参数来控制的, 另一个就是根据User Agent, 如果检测到旧版本的浏览器, 或者爬虫则忽略gbv参数直接输出不支持javascript的版本.

Javascript challenge

这是难度最高的一种了, CloudFlare用的是这种办法. 也是使用cookie来验证, 但是cookie是用javascript计算出来的, 而爬虫一般不可能去运行页面上的javascript, 所以这个也没什么很好的解决办法, 好在只是一少部分站点使用了CloudFlare的CDN服务.

总结

如果抓取网页有问题, 基本上原因不会超出上面提到的几个, 先把这些基本的排除掉之后, 再想别的可能性.