核心提示:对ACG有了解的朋友们应该都熟悉有名的P站,P站的特点就是有大量的精美二次元图片,但P站连个搜索功能都不开放给非付费用户,遂有了这个项目,有兴趣可以移步PHelper-Github这里使用了Node....
对ACG有了解的朋友们应该都熟悉有名的P站,P站的特点就是有大量的精美二次元图片,但P站连个搜索功能都不开放给非付费用户,遂有了这个项目,有兴趣可以移步
PHelper-Github
这里使用了Node.js作为爬虫的服务器,不断获取的页面评分来筛选出高质量的图片
实现
技术选型
- request.js-Github –发起HTTP请求
- cheerio –解析response
- mongodb –图片信息存储
在开始之前,我们要先熟悉一下Pixiv对爬虫技术做的限制
对于这类图片类网站来说,URL就是其最大的财富了,当然要做相应的防护
不过我还是要吐槽,这破站程序员的水平也太差了
对于反爬虫的策略只有一种,就是cookie限制
在国内常用的一些反爬虫策略,例如针对ip段的封锁,随机拒绝高频请求,蜜罐战术等等的防护一个都没有
我随便弄一段前端代码,大家可以看看
<script> new function() { var user_id = ""; var service = 'www.pixiv.net'; var api = 'https://www.pixiv.net/rpc/js_error.php'; window.onerror = function(message, url, line) { window.onerror = null; if (!url && line === 0)return; // Googleのスクリプトがエラー出しまくってるので一時的に対処 if (url.indexOf('https://apis.google.com/_/scs/apps-static/') === 0) { if (Math.random() > 0.01) return; } //.......... // Safariがエラー出しまくってるので一時的に対処 if ((message === null || message === "null") && line === 0 && window.navigator.userAgent.indexOf("Safari") >= 0) { if (Math.random() > 0.01) return; } window._send(message, url, line); };
这种针对浏览器的错误处理让我大开眼界
pixiv的页面URl形式非常固定,都是统一的前缀+连续的图片id(再次吐槽),不拿来当爬虫的靶子都浪费这设计啊
先看请求单一页面的代码,
var createOption = require("./requestHeader"); var request = require("request"); var option = createOption("45358677") console.log("begin time :",process.uptime()); request(option,function(err,response){ if(err){ console.log("error"); } if(response.statusCode == 200){ console.log("success,end time:",process.uptime()); console.log(response.body);//需要后续解析 }else{ console.log("get http response error,check your network"); } });
获取response的html页面后,就可以使用cheerio把相应的数据清洗出来了,随后存入mongo就好,这个过程不表
还有一个问题是在面对数十万个连续请求时,如何控制流程的问题,for循环肯定是不能用的,还要控制回调队列的大小,于是采用了折中的方法,
采用定时器,每隔五秒发出一个请求,一般在网络顺畅的情况下,可以完成http请求并返回,进行下一个请求,即使在五秒内没有返回,也不会造成多个事件等待返回的情况
function myFunction(){ console.log(begin); var option = createOption(begin.toString()); console.log("begin time :",process.uptime()); request(option,function(err,response){ if(err){ console.log("error"); return; } if(response.statusCode == 200){ console.log("success,end time:",process.uptime()); parseRes.processPage(response) }else{ console.log(begin," warning:get http response exception "); } }); begin++; if(begin < end){ setTimeout(myFunction, 5000); }else{ process.exit(); } }
同时,我觉得单个进程跑太费时间怎么办,多进程走起
把上面的代码fork五份,作为五个单独的进程,速度就能提升五倍