这篇文章主要介绍了关于node爬取拉勾网数据并导出为excel文件,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
前言
之前断断续续学习了node.js,今天就拿拉勾网练练手,顺便通过数据了解了解最近的招聘行情哈!node方面算是萌新一个吧,希望可以和大家共同学习和进步。
一、概要
我们首先需要明确具体的需求:
可以通过
node index 城市 职位
来爬取相关信息也可以输入node index start直接爬取我们预定义好的城市和职位数组,循环爬取不同城市的不同职位信息
将最终爬取的结果存储在本地的
./data
目录下生成对应的excel文件,并存储到本地
二、爬虫用到的相关模块
fs: 用于对系统文件及目录进行读写操作
async:流程控制
superagent:客户端请求代理模块
node-xlsx:将一定格式的文件导出为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循环调用主程序。关于命令分析的主页代码如下:
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"') }
预定义好的城市和职位数组如下:
{ "city": ["北京","上海","广州","深圳","杭州","南京","成都","西安","武汉","重庆"], "position": ["前端","java","php","ios","android","c++","python",".NET"] }
接下来就是爬虫主程序部分的分析了。
分析页面,找到请求地址
首先我们打开拉勾网首页,输入查询信息(比如node),然后查看控制台,找到相关的请求,如图:
这个post请求https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false
就是我们所需要的,通过三个请求参数来获取不同的数据,简单的分析就可得知:参数first
是标注当前是否是第一页,true为是,false为否;参数pn
是当前的页码;参数kd
是查询输入的内容。
通过superagent请求数据
首先需要明确得是,整个程序是异步的,我们需要用async.series来依次调用。
查看分析返回的response:
可以看到content.positionResult.totalCount就是我们所需要的总页数
我们用superagent直接调用post请求,控制台会提示如下信息:
{'success': False, 'msg': '您操作太频繁,请稍后再访问', 'clientIp': '122.xxx.xxx.xxx'}
这其实是反爬虫策略之一,我们只需要给其添加一个请求头即可,请求头的获取方式很简单,如下:
然后在用superagent调用post请求,主要代码如下:
// 先获取总页数 (cb) => { superagent .post(`https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false&city=${city}&kd=${position}&pn=1`) .send({ 'pn': 1, 'kd': position, 'first': true }) .set(options.options) .end((err, res) => { if (err) throw err // console.log(res.text) let resObj = JSON.parse(res.text) if (resObj.success === true) { totalPage = resObj.content.positionResult.totalCount; cb(null, totalPage); } else { console.log(`获取数据失败:${res.text}}`) } }) },
拿到总页数后,我们就可以通过总页数/15
获取到pn参数,循环生成所有url并存入urls中:
(cb) => { for (let i=0;Math.ceil(i<totalPage/15);i++) { urls.push(`https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false&city=${city}&kd=${position}&pn=${i}`) } console.log(`${city}的${position}职位共${totalPage}条数据,${urls.length}页`); cb(null, urls); },
有了所有的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传递回去,否则无法进行下一次循环
主要代码如下:
// 控制并发为3 (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'); } // 将数据以.json格式储存在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; // 这个arguments是调用controlRequest函数的参数,可以区分是那种爬取(循环还是单个) if (arguments[2]) { ok = 1; } cb(null, ok) }) }, () => { if (ok) { setTimeout(function () { console.log(`${city}的${position}数据请求完成`); indexCallback(null); }, 5000); } else { console.log(`${city}的${position}数据请求完成`); } // exportExcel.exportExcel() // 导出为excel }
导出的json文件如下:
json文件导出为excel
将json文件导出为excel有多种方式,我使用的是node-xlsx
这个node包,这个包需要将数据按照固定的格式传入,然后导出即可,所以我们首先做的就是先拼出其所需的数据格式:
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 data = [ // { // name : 'sheet1', // data : [ // [ // 'ID', // 'Name', // 'Score' // ], // [ // '1', // 'Michael', // '99' // // ], // [ // '2', // 'Jordan', // '98' // ] // ] // }, // { // name : 'sheet2', // data : [ // [ // 'AA', // 'BB' // ], // [ // '23', // '24' // ] // ] // } // ] // 写xlsx var buffer = xlsx.build(dataArr) fs.writeFile('./result.xlsx', buffer, function (err) { if (err) throw err; console.log('Write to xls has finished'); // 读xlsx // var obj = xlsx.parse("./" + "resut.xls"); // console.log(JSON.stringify(obj)); } ); }
导出的excel文件如下,每一页的数据都是一个sheet,比较清晰明了:
我们可以很清楚的从中看出目前西安.net的招聘情况,之后也可以考虑用更形象的图表方式展示爬到的数据,应该会更加直观!
总结
其实整个爬虫过程并不复杂,注意就是注意的小点很多,比如async的各个方法的使用以及导出设置header等,总之,也是收获满满哒!
源码
gitbug地址: https://github.com/fighting12...
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
以上是node爬取拉勾网数据并导出为excel文件的详细内容。更多信息请关注PHP中文网其他相关文章!

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务

本文展示了与许可证确保的后端的前端集成,并使用Next.js构建功能性Edtech SaaS应用程序。 前端获取用户权限以控制UI的可见性并确保API要求遵守角色库

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3 Linux新版
SublimeText3 Linux最新版

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中