核心提示:对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五份,作为五个单独的进程,速度就能提升五倍



 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                