这篇文章主要介绍了关于node爬取拉勾网数据并导出为excel文件,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
前言
之前断断续续学习了node.js,今天就拿拉勾网练练手,顺便通过数据了解了解最近的招聘行情哈!node方面算是萌新一个吧,希望可以和大家共同学习和进步。
一、概要
我们首先需要明确具体的需求:
可以通过node index 城市 职位
来爬取相关信息
也可以输入node index start直接爬取我们预定义好的城市和职位数组,循环爬取不同城市的不同职位信息
将最终爬取的结果存储在本地的./data
目录下
生成对应的excel文件,并存储到本地
二、爬虫用到的相关模块
三、爬虫主要步骤:
初始化项目
新建项目目录
在合适的磁盘目录下创建项目目录 node-crwl-lagou
初始化项目
进入node-crwl-lagou文件夹下
执行npm init,初始化package.json文件
安装依赖包
npm install async
npm install superagent
npm install node-xlsx
命令行输入的处理
对于在命令行输入的内容,可以用process.argv
来获取,他会返回个数组,数组的每一项就是用户输入的内容。
区分node index 地域 职位
和node index start
两种输入,最简单的就是判断process.argv的长度,长度为四的话,就直接调用爬虫主程序爬取数据,长度为三的话,我们就需要通过预定义的城市和职位数组来拼凑url了,然后利用async.mapSeries循环调用主程序。关于命令分析的主页代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | if (process.argv.length === 4) {
let args = process.argv
console.log( '准备开始请求' + args[2] + '的' + args[3] + '职位数据' );
requsetCrwl.controlRequest(args[2], args[3])
} else if (process.argv.length === 3 && process.argv[2] === 'start' ) {
let arr = []
for (let i = 0; i < defaultArgv.city.length; i++) {
for (let j = 0; j < defaultArgv.position.length; j++) {
let obj = {}
obj.city = defaultArgv.city[i]
obj.position = defaultArgv.position[j]
arr.push(obj)
}
}
async.mapSeries(arr, function (item, callback) {
console.log('准备开始请求' + item.city + '的' + item.position + '职位数据');
requsetCrwl.controlRequest(item.city, item.position, callback)
}, function (err) {
if (err) throw err
})
} else {
console.log('请正确输入要爬取的城市和职位,正确格式为: "node index 城市 关键词" 或 "node index start" 例如: "node index 北京 php" 或 "node index start" ')
}
|
Salin selepas log masuk
预定义好的城市和职位数组如下:
1 2 3 4 | {
"city" : [ "北京" , "上海" , "广州" , "深圳" , "杭州" , "南京" , "成都" , "西安" , "武汉" , "重庆" ],
"position" : [ "前端" , "java" , "php" , "ios" , "android" , "c++" , "python" , ".NET" ]
}
|
Salin selepas log masuk
接下来就是爬虫主程序部分的分析了。
分析页面,找到请求地址
首先我们打开拉勾网首页,输入查询信息(比如node),然后查看控制台,找到相关的请求,如图:
![1530957306181687.png 2217504720-5b3c89dd656e7_articlex[1].png](/static/imghw/default1.png)
这个post请求https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false
就是我们所需要的,通过三个请求参数来获取不同的数据,简单的分析就可得知:参数first
是标注当前是否是第一页,true为是,false为否;参数pn
是当前的页码;参数kd
是查询输入的内容。
通过superagent请求数据
首先需要明确得是,整个程序是异步的,我们需要用async.series来依次调用。
查看分析返回的response:
![1530957314268879.png 43112870-5b3c90261a4c3_articlex[1].png](/static/imghw/default1.png)
可以看到content.positionResult.totalCount就是我们所需要的总页数
我们用superagent直接调用post请求,控制台会提示如下信息:
1 | {'success': False, 'msg': '您操作太频繁,请稍后再访问', 'clientIp': '122.xxx.xxx.xxx'}
|
Salin selepas log masuk
这其实是反爬虫策略之一,我们只需要给其添加一个请求头即可,请求头的获取方式很简单,如下:
![1530957321762012.png 3208087083-5b3c97bf640bd_articlex[1].png](/static/imghw/default1.png)
然后在用superagent调用post请求,主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | (cb) => {
superagent
.post(`https:
.send({
'pn' : 1,
'kd' : position,
'first' : true
})
.set(options.options)
. end ((err, res) => {
if (err) throw err
let resObj = JSON.parse(res.text)
if (resObj.success === true) {
totalPage = resObj.content.positionResult.totalCount;
cb(null, totalPage);
} else {
console.log(`获取数据失败:${res.text}}`)
}
})
},
|
Salin selepas log masuk
拿到总页数后,我们就可以通过总页数/15
获取到pn参数,循环生成所有url并存入urls中:
1 2 3 4 5 6 7 | (cb) => {
for (let i=0;Math. ceil (i<totalPage/15);i++) {
urls.push(`https:
}
console.log(`${city}的${position}职位共${totalPage}条数据,${urls.length}页`);
cb(null, urls);
},
|
Salin selepas log masuk
有了所有的url,在想爬到所有的数据就不是难事了,继续用superagent的post方法循环请求所有的url,每一次获取到数据后,在data目录下创建json文件,将返回的数据写入。这里看似简单,但是有两点需要注意:
为了防止并发请求太多而导致被封IP:循环url时候需要使用async.mapLimit方法控制并发为3, 每次请求完都要过两秒在发送下一次的请求
在async.mapLimit的第四个参数中,需要通过判断调用主函数的第三个参数是否存在来区分一下是那种命令输入,因为对于node index start
这个命令,我们使用得是async.mapSeries,每次调用主函数都传递了(city, position, callback)
,所以如果是node index start
的话,需要在每次获取数据完后将null传递回去,否则无法进行下一次循环
主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | (cb) => {
async.mapLimit(urls, 3, (url, callback) => {
num++;
let page = url.split( '&' )[3].split( '=' )[1];
superagent
.post(url)
.send({
'pn' : totalPage,
'kd' : position,
'first' : false
})
.set(options.options)
. end ((err, res) => {
if (err) throw err
let resObj = JSON.parse(res.text)
if (resObj.success === true) {
console.log(`正在抓取第${page}页,当前并发数量:${num}`);
if (!fs.existsSync( './data' )) {
fs.mkdirSync( './data' );
}
fs.writeFile(`./data/${city}_${position}_${page}.json`, res.text, (err) => {
if (err) throw err;
setTimeout(() => {
num--;
console.log(`第${page}页写入成功`);
callback(null, 'success' );
}, 2000);
});
}
})
}, (err, result) => {
if (err) throw err;
if (arguments[2]) {
ok = 1;
}
cb(null, ok)
})
},
() => {
if (ok) {
setTimeout( function () {
console.log(`${city}的${position}数据请求完成`);
indexCallback(null);
}, 5000);
} else {
console.log(`${city}的${position}数据请求完成`);
}
}
|
Salin selepas log masuk
导出的json文件如下:
![1530957237348447.png 2625390489-5b3c990dd1e87_articlex[1].png](/static/imghw/default1.png)
json文件导出为excel
将json文件导出为excel有多种方式,我使用的是node-xlsx
这个node包,这个包需要将数据按照固定的格式传入,然后导出即可,所以我们首先做的就是先拼出其所需的数据格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | function exportExcel() {
let list = fs.readdirSync( './data' )
let dataArr = []
list.forEach((item, index) => {
let path = `./data/${item}`
let obj = fs.readFileSync(path, 'utf-8' )
let content = JSON.parse(obj).content.positionResult.result
let arr = [[ 'companyFullName' , 'createTime' , 'workYear' , 'education' , 'city' , 'positionName' , 'positionAdvantage' , 'companyLabelList' , 'salary' ]]
content.forEach((contentItem) => {
arr.push([contentItem.companyFullName, contentItem.phone, contentItem.workYear, contentItem.education, contentItem.city, contentItem.positionName, contentItem.positionAdvantage, contentItem.companyLabelList.join( ',' ), contentItem.salary])
})
dataArr[index] = {
data: arr,
name: path.split( './data/' )[1]
}
})
var buffer = xlsx.build(dataArr)
fs.writeFile( './result.xlsx' , buffer, function (err)
{
if (err)
throw err;
console.log( 'Write to xls has finished' );
}
);
}
|
Salin selepas log masuk
导出的excel文件如下,每一页的数据都是一个sheet,比较清晰明了:
![1530957248956808.png 3676712650-5b3c995aad59d_articlex[1].png](/static/imghw/default1.png)
我们可以很清楚的从中看出目前西安.net的招聘情况,之后也可以考虑用更形象的图表方式展示爬到的数据,应该会更加直观!
总结
其实整个爬虫过程并不复杂,注意就是注意的小点很多,比如async的各个方法的使用以及导出设置header等,总之,也是收获满满哒!
源码
gitbug地址: https://github.com/fighting12...
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
react 官网动画库(react-transition-group)的新写法
原生JS基于window.scrollTo()封装垂直滚动动画工具函数
NodeList 和 HTMLCollection 和 Array的解析
Atas ialah kandungan terperinci node爬取拉勾网数据并导出为excel文件. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!