欢迎光临
技术人生-雨巷前端

node爬虫001

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)
        }
    })
}

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

 

作者:林城
链接:https://www.zhihu.com/question/56666548/answer/643163138
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

未经允许不得转载:技术人生 » node爬虫001

分享到:更多 ()

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    从头写一个原始的爬虫,Node.js 确实很方便,不过其它语言驱动浏览器也没复杂到哪里。写一个能工程实用的,比的其实是现有工具的成熟度,目前 Crawler 虽然已经很像 Scrapy 了,上手很快,但还是需要自己找个 cheerio 之类的来分析 dom,Scrapy 则是全集成的,当然 Crawler 也可随便换熟悉的 dom 处理框架也算一个好处啦。而搞一个24×7运行的分布式爬虫系统,Scrapy 成熟度强的就不是一星半点儿了,logging/stats/telnet/email什么的都标配了,节点任务分配、调度都现成的……相信假以时日,总会有人拿 Node.js 搞出一套同样强大的……

    yuxang1个月前 (06-30)回复

我是前端,我不迷茫