Node.js可能是最适合写简易小爬虫的语言,速度极快、代码极少

3,870 阅读4分钟

“凡是能用JS 写出来的,最终都会用JS 写”,这是一个非常著名的定律,用在爬虫这里再合适不过了。

一说到爬虫很多人都会想到python,的确,python语法简洁,还有scrapy这一类强大的工具可以使用。

但是如果只是想写一个小爬虫,爬取论坛里的几张帖子,帖子里面的几个楼层,然后合成一篇文章。这点小数据量使用scrapy就有点杀鸡用牛刀了,而且还得设置一堆东西,非常麻烦,不够灵活。

而JavaScript就非常适合这一类小爬虫,首先是自带异步架构,能同时爬取多张网页内容,效率上来说比python高多了,我用两个语言写过爬取代理ip的爬虫,当JavaScript爬完时吓了我一跳,这速度快极了。

当然,python也可以通过开启多线程、多协程来实现同时爬取多张网页,但是这就比默认就异步的JavaScript麻烦多了。

所以,如果想简单、高效地写个小爬虫,非JavaScript莫属。

有多快多简单呢?现在就来写个豆瓣top250的爬虫,爬取10张网页,250部电影的名字、评分和封面地址;

1.如何安装

要通过js写爬虫,需要用到三个模块,request、cheerio和fs,其中fs内置了,只需要安装前两个即可,安装命令:

npm install request cheerio

2.获取网页内容

request可以链接网页,爬取内容,这里我们只需要给它传递两个参数就行,一个为url(网址),另一个为回调函数; request会向回调函数传递三个参数,分别是error(错误信息),response(响应信息),body(网页内容):

var request = require('request')
var cheerio = require('cheerio')
var fs = require('fs')
var movies = []

var requstMovie = function(url){
	request('https://movie.douban.com/top250',function(error, response, body)){
		//res.statusCode 为200则表示链接成功
		if(error === null && response.statusCode === 200){
			console.log('链接成功')
			//使用cheerio来解析body(网页内容),提取我们想要的信息
			var e = cheerio.load(body)
			
			//通过分析网页结构,我们发现豆瓣每部电影都通过item属性隔开
			var movieDiv = e('.item')
	
			//通过for循环来提取每部电影里的信息
			for (let i = 0; i < movieDiv.length; i++) {
					//takeMovie函数能提取电影名称、评分和封面
	                let movieInfo = takeMovie(movieDiv[i])
	                log('正在爬取' + movieInfo.name)
	                //将提取到的电影放入数组
	                movies.push(movieInfo)
	            }
		}
	})
}

3.提取电影信息

通过创建一个类来包含我们想要的属性,在每次调用takeMovie函数提取信息时都会初始化这个类,然后赋值给相应的属性; 之后放入movies数组里;

//电影的类
var movie = function(){
    this.id = 0
    this.name = ''
    this.score = 0
    this.pic = ''
}

var takeMovie = function(div){
    var e = cheerio.load(div)
    //将类初始化
    var m = new movie()
    m.name = e('.title').text()
    m.score = e('.rating_num').text()
    var pic = e('.pic')
    //cheerio如果要提取某个属性的内容,可以通过attr()
    m.pic = pic.find('img').attr('src')
    m.id = pic.find('em').text()
    return m
}

4.爬取所有top250

现在要爬取所有的top250信息,总共有10张网页,每张包含25部电影信息,创建一个函数来生成每张网页的网址,然后通过request进行爬取:

var top250Url = function(){
    let l = ['https://movie.douban.com/top250']
    var urlContinue = 'https://movie.douban.com/top250?start='
    let cont = 25
    for (let i = 0; i < 10; i++) {
        l.push(urlContinue+cont)
        cont += 25
    }
    return l
}

//爬取所有网页
var __main = function(){
    var url = top250Url()
    for (let i = 0; i < url.length; i++) {
        requstMovie(url[i])
    }
}

__main()

5.异步架构的坑

当我们爬取完所有的网页后就会发现,movies里的电影信息并不按我们爬取的顺序,这也是异步架构一个需要注意的大坑; 在爬取第一张网页时,JavaScript不会等到处理结束才接着爬第二张,有时候各个网页返回的速度有所差异,会造成先爬取的不一定会先出结果,因此在电影排序上会出现混乱; 所以我们还需要对爬取下来的内容重新进行排序,然后保存:

//sortMovie回调函数能比较两个对象属性大小
var sortMovie = function(id){
    return function(obj ,obj1){
        var value = obj[id]
        var value1 = obj1[id]
        return value - value1
    }
}

//保存文件
var saveMovie = function(movies){
    var path = 'movie.txt'
    var data = JSON.stringify(movies, null, 2)
    fs.appendFile(path, data, function(error){
        if(error == null){
            log('保存成功!')
        } else {
            log('保存失败',error)
        }
    })
}

我们需要等到所有网页都爬取分析完才执行sortMovie和saveMovie函数,由于JavaScript是异步,即使这两个函数放在最底部也会在分析完之前执行; 一般会通过Promise来控制异步,但是为了方便,这里我们通过if来判断,在每次爬取网页后,都会判断movies里是否包含250条信息,如果有则说明全部爬取到了:

var requstMovie = function(url){
    request(url, function(error, response, body){
        if (error === null && response.statusCode === 200){
            var e = cheerio.load(body)
            var movieDiv = e('.item')
            for (let i = 0; i < movieDiv.length; i++) {
                let movieInfo = takeMovie(movieDiv[i])
                log('正在爬取' + movieInfo.name)
                movies.push(movieInfo)
            }
            //判断movies数量
            if (movies.length === 250){
            	//通过sort将数组内每两个元素放入比较函数
                var sortM = movies.sort(sortMovie('id'))
                //保存文件
                saveMovie(sortM)
            }
        } else {
            log('爬取失败', error)
        }
    })
    
}

到这里,爬虫已经写完了,来运行一下:

完整代码可以通过github查看:DoubanMovies.JS

也可以访问我的网站,获取更多文章:Nothlu.com