Node爬虫 爬博客园搜索

博客园右边有一个“找找看”的索引窗口,我们输入关键词,可以查到几万篇的相关的博客,这里用Node的爬虫来抓取给定关键词的查询的特定内容,实现翻页功能,抓取文章链接,作者,发布日期等信息。

Node适合高并发IO操作的程序,用来写爬虫速度最快了。这里我们把爬到的数据存储到数据库中。

前奏:

1.cheerio模块 ,一个类似jQuery的选择器模块,分析HTML利器。

2.request模块,让http请求变的更加简单

3.mysql模块,node连接mysql的模块,可参考:http://www.runoob.com/nodejs/nodejs-mysql.html。

4.数据库部分:

分析:

结构已经比较清晰

注意的是:

这里的URL地址栏明文显示的结构,中文是显示出现了,但是实际URL内容是经过编码了,复制地址栏内容,粘贴过来,我们发现http://zzk.cnblogs.com/s/blogpost?Keywords=%E6%B8%B8%E6%88%8F,游戏其实被编码成了%E6%B8%B8%E6%88%8F,我们这里:

var url = 'http://zzk.cnblogs.com/s/blogpost?Keywords=' + key + '&pageindex=' + page;如果key是中文,是会抓取不到任何数据,用JS函数url = encodeURI(url);转换一下就好。

翻页部分:

用"pageindex="出现的位置加上本身长度即得到页数

 page = nextUrl.slice(nextUrl.indexOf('pageindex=') + 10);

indexof是返回子串在母串的第一个位置,没有则-1,区分大小写,slice也是从start取到end,注意下标1就是1开始,不是0

 参考方法:http://www.w3school.com.cn/jsref/jsref_indexOf.asp

      http://www.w3school.com.cn/jsref/jsref_slice_array.asp

代码:

var request = require('request');
var cheerio = require('cheerio');

var mysql = require('mysql');
var db = mysql.createConnection({
    host:     '127.0.0.1',
    user:     'root',
    password: '123456',
    database: 'spider_data'
});
db.connect();

function fetchData(key, page) {
    var url = 'http://zzk.cnblogs.com/s/blogpost?Keywords=' + key + '&pageindex=' + page;
    //用JS的全局对象函数,作为URI编码,不然中文,空格等抓取不到
    url = encodeURI(url);
    request(url, function(err, res) {

        if (err) return console.log(err);
        var $ = cheerio.load(res.body.toString());
        var arr = [];
        //解析HTML代码
        $('.searchItem').each(function() {
            var title = $(this).find('.searchItemTitle');
            var author = $(this).find('.searchItemInfo-userName a');
            var time = $(this).find('.searchItemInfo-publishDate');
            var view = $(this).find('.searchItemInfo-views');
            var info = {
                title: $(title).find('a').text(),
                href: $(title).find('a').attr('href'),
                author: $(author).text(),
                time: $(time).text(),
                view: $(view).text().replace(/[^0-9]/ig, '')
            };
            arr.push(info);
            //打印
            console.log('~~~~~~~~~~~~~~~~~~~~~~~ 输出开始 ~~~~~~~~~~~~~~~~~~~~~~~');
            console.log(info);
            console.log('~~~~~~~~~~~~~~~~~~~~~~~ 输出结束 ~~~~~~~~~~~~~~~~~~~~~~~');
            //保存数据
            db.query('insert into blog set ?', info, function(err, result) {
                if (err) throw err;
                if (!!result) {
                    console.log('插入成功');
                    console.log(result.insertId);
                } else {
                    console.log('插入失败');
                }
            });
        });

        //下一页
        var nextA = $('.pager a').last(),
            nextUrl = '';
        if ($(nextA).text().indexOf('Next') != -1) {
            nextUrl = nextA.attr('href');
            page = nextUrl.slice(nextUrl.indexOf('pageindex=') + 10);//"pageindex="出现的位置加上本身长度得到页数
            setTimeout(function() {
                fetchData(key, page);
            }, 2000);
        } else {
            db.end();
            console.log('结束,爬取完所有数据');
        }

    });
}
fetchData('游戏开发', 1);

结果:

 

我们可以随意换关键词,清空这个数据表。

改进:

速度太多,可能会被封IP,我们在后续可以用async 模块控制,async是一个流程控制工具包,提供了直接而强大的异步功能,进而在爬取页面时控制并发数。

用eventproxy 带来一种事件式编程的变化,如果你是要抓取多个源的数据,由于你根本不知道这些异步操作到底谁先完成,那么每次当抓取成功的时候,就需要判断一下count === n。当值为真时,使用另一个函数继续完成操作。而 eventproxy 就起到了这个计数器的作用,它来帮你管理到底这些异步操作是否完成,完成之后,它会自动调用你提供的处理函数,并将抓取到的数据当参数传过来。

原文地址:https://www.cnblogs.com/zhangmingzhao/p/7692185.html