Nodejs,不一样的爬虫实践

linjiazhen 8年前

来自: http://www.famanoder.com/bokes/56b0ff763b9d992825f0a1bb

做前端以来,对我成长帮助最大的恐怕也就是各位大侠们的博客了, ,慢慢的也在我心里种下了颗种子:我也要写博客!哪怕我文笔差,技术菜,难以望其项背,我也要追随大神们的脚步,写写博客,处处留香。摸爬滚打,终是成长;学习分享,与君共勉!小前端初学Nodejs,搭了个 简单的博客 ,捉襟见肘,望大侠路过指导!好了,此处有广告之嫌,进入正题。

关于Nodejs的爬虫程序,百度一大把,是的,我也是百度到的,然后到github上看了看cheerio模块;乍一看,这不就是Jquery嘛,没想到Jquery都能牛到后端操作DOM了啊,恩。。。可以先这么认为和理解吧,因为cheerio确实可以像jquery操作DOM一样操作从远程爬取过来的页面;顿时邪念四起:我要把我喜欢的博客全抓到我的博客站点上去、我要不KX上网而是把谷歌抓过来访问。。。然后就有了我博客上的 “爬呀爬” 版块,都是爬取的国内顶尖的前端团队呕心沥血写的博客, ,保留了作者信息和版权,当然,如果他们看到了,骂我一顿,叫我删掉,我肯定会屁颠屁颠删掉的;

还有就是怎么通过我的站点访问谷歌了,比如:http://www.famanoder.com?url=http://google.com的请求,我res.send(googleBody);恩 ,这样是可以的,可是问题在于:相对路径的资源返回404了,因为相对路径域名取的是我的站点,而我的站点没有对应的目录和资源,自然404了;这时cheerio可就很有用了,在res.send之前可以对body进行些DOM操作啊,貌似有点复杂啊,我正则还不行。。。为了让爬取过来的谷歌可以点击,链接没有404,还需要做一步,就是通过cheerio将所有相对路径补上http://www.famanoder.com?url=http://google.com,绝对路径补上http://www.famanoder.com?url=来将爬取的目标站点所有资源都通过我的博客站点请求;

很高兴,一个小小的爬虫让我我又知道了三个很好用的模块:request、superagent、cheerio

request和superagent差不多,过年后做个横向的比较,争取进一步理解http模块;

先看看request怎么抓页面的吧,江湖中人不多废话了,看代码:

if (req.query.url) {    var iurl = decodeURIComponent(req.query.url);    var thaturl = url.parse(iurl).protocol +url.parse(iurl).host;    var body=[],size=0;    request.get({      url: iurl,      headers: {             }    })    .on('response', function (response) {      res.set(response.headers);      response.setEncoding='utf8';      response.on('data',function(chunk){       body.push(chunk);       size+=chunk.length;      });    })    .on('error', function (err) {      console.log(err);    })    .on('end',function(){  //buffer   防止丢包的情况,如果数据过多,会抓不全的。。。。。     var data = Buffer.concat(body , size);           var html = data.toString();     var $=cheerio.load(html);  //只是个栗子,具体情况还会要url.parse解析所有href、src再处理     $('a').each(function(){      $(this).attr('href','http://famanoder.com?url='+$(this).attr('href'));     });     res.write($('html,body').html());    })        //.pipe(res);   }

除了丢包,一般不会有什么问题了,可能就是unicode中文的乱码了,也有很多模块可以处理,不过用JS弄也不错,谢谢某大侠的代码,找半天了,值得收藏:

function reconvert(str){             str = str.replace(/(\\u)(\w{4})/gi,function($0){                    return (String.fromCharCode(parseInt((escape($0).replace(/(%5Cu)(\w{4})/g,"$2")),16)));             });                    str = str.replace(/(&#x)(\w{4});/gi,function($0){                    return String.fromCharCode(parseInt(escape($0).replace(/(%26%23x)(\w{4})(%3B)/g,"$2"),16));             });                      return str;  }

对了,抓取来的body是不能直接操作的,不信试一试,不用cheerio而是直接toString再replace什么的,都是无用功;

好吧,到这我一口气爬取了上百篇大神们的博客, ,还是有些繁琐的,巴拉巴拉,我想还是弄个定时任务吧,每天到点就爬一页,取最新的留下;这里也有两个模块供选择:schedule、later,还是下次做个横向的比较吧,期待。schedule这个单词在学校时就没写对过,今天终于写对了,原谅我,又掉底子了。。。看看定时任务吧:

var registTask=function(hour,minute,taskname,fn){   var rule = new schedule.RecurrenceRule();   //每天这个时刻定时执行任务    rule.dayOfWeek = [0, new schedule.Range(1, 6)];    rule.hour = hour;    rule.minute = minute;    var j = schedule.scheduleJob(rule, function(){      console.log("开始爬取:"+taskname);    fn&&fn();    });  }  registTask(23,30,'TaoBaoFED',function(){   getTaoBaoFEDBokes(1);          //呵呵,每天23:30爬一页  });

差不多这就是“爬呀爬” 这个小玩意,对于一个业余的noder来说,又走了一小步,也挺好玩的;马上就过年了,又要老一岁了啊,不过也很快就到春天了,祝大家猴年大吉,四季如春!