有时候我们需要程序化的方式访问网页, 最典型的应用当然是网络爬虫, 但也可以是其他有用的应用. 和通常的用浏览器访问不同, 爬虫会碰到一些意想不到的问题, 一般大型站点基本都有某种反爬虫策略. 不过理论上, 服务器是无法完全区别浏览器和爬虫的, 只要不是恶意的发送大量请求, 基本可以绕过这些限制, 下面是常见的问题总结.
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服务.
总结
如果抓取网页有问题, 基本上原因不会超出上面提到的几个, 先把这些基本的排除掉之后, 再想别的可能性.