** 本项目纯属技术探讨,仅供学习和参考,禁止用于非法用途** 如果你是Node初学者,那么看完该篇文章你将能学会以下东西:
- node环境的搭建和mysql本地环境的搭建
- express项目搭建并使用babel支持ES6语法
- �将项目部署到服务器
- 如遇到不懂或有问题,欢迎大家添加qq群 787275772 进行讨论
- 本文章后期将持续更新,可以收藏本文章
$ node -v
v10.8.0
$ npm -v
6.2.0
能顺利打印出版本号则表示node安装成功了;
当mysql安装成功后,我们打开shell 输入
mysql -h localhost -u 用户名 -p
Enter password: 输入你数据库设置的密码
进来后 先创建数据库
mysql -> CREATE Database IF NOT EXISTS boss_zhiping Character Set UTF8;
npm install express -g
推荐使用cnpm或yarn安装依赖。安装速度比较快。
使用cnpm: cnpm install express -g
使用yarn: yarn add -global express
待安装完毕后,使用git跳转到存放项目的位置 执行
express projectName && cd projectName
注: projectName是你的项目名字,cd是跳转到你的项目名字的根目录下; 进入根目录后执行安装依赖命令 此处使用yarn安装依赖,可根据自己喜好使用
yarn install
此时,一个基于express的项目就搭建好了,我们来给他完善一下目录分层结构,git输入命令
mkdir common // 公共文件夹
mkdir controllers // 控制器
mkdir database // 数据库
mkdir model // 数据库模型
为了支持es6 7的最新语法,我们使用了babel做支持,让express也支持使用import export 语法;
首先安装babel的各种插件;
cnpm install --save-dev babel babel-cli babel-core babel-helpers babel-preset-es2015 babel-preset-stage-3 babel-register babel-plugin-transform-async-to-generator babel-plugin-transform-es2015-classes babel-plugin-transform-es2015-modules-commonjs babel-plugin-transform-export-extensions
我们在根目录文件创建.babelrc文件,输入以下配置;
{
"presets": ["stage-3"],
"plugins": [
"transform-async-to-generator",
"transform-es2015-modules-commonjs",
"transform-export-extensions"
]
}
然后就可以愉快的在express中使用import export 语法了;
- superagent superagent是一个请求插件
- superagent-proxy superagent的代理插件,配合superagent使用
- mysql mysql数据库插件 用于数据的增删改查
- supervisor 一个非常好用的热更新插件 代码更改后自动帮你重启node服务
- node-schedule 用于执行定时任务 例如定时爬虫 更新数据等等
- cheerio cheerio用于解析响应头的html结构,爬虫必备插件
我们分别安装他们:
yarn add superagent superagent-proxy mysql cheerio node-schedule supervisor
具体文件结构我已经写好了,大家可以在github上克隆下来 点击此处
git clone git@github.com:phonycode/wuss-weapp.git
克隆下来大体的目录结构如下
|----bin
|---- www 项目的创建服务器端口
|----common 公共文件
|---- city_data.js boss直聘的城市数据
|---- menu_data.js boss直聘职位数据
|----controllers 控制器
|---- city.js 城市模块
|---- index.js 公共模块/继承
|---- menu.js 职位菜单模块
|---- offer.js 职位模块
|---- proxy.js 代理模块
|---- reptile.js 爬虫
|---- statistics.js 数据统计模块
|---- task.js 自动任务
|----database
|---- mysql.js mysql数据库配置
|----models
|---- initialize.js 初始化数据库表模块
|----public
|----routes 路由
|----views 视图
|----.babelrc babel配置
|----app.js express入口文件
|----package.json
首先我们进入boss直聘官网,选择web前端岗位进入,你会发现链接上多了一个参数:
https://www.zhipin.com/c101280100-p100901/
看到没c101280100-p100901
就是他的岗位代码.然后继续点击下一页,我们现在进入第二页,发现url又多了个参数
https://www.zhipin.com/c101280100-p100901/?page=2&ka=page-2
没错,多了个分页信息 ?page=2&ka=page-2
,然后我们看职位信息。
看到没,当前页的所有职位信息他都用一个ul元素吧每个li渲染出来了。
根据上面的分析,我们知道要去请求一个url访问 才能获取到他的职位信息。这时候我们就要用到superagent
这个插件了,他可以发起一个请求并获取静态网页数据。
import request from 'superagent';
const baseUrl = 'https://www.zhipin.com/c101280100-p100901/?page=1&ka=page-1';
request.get(baseUrl).end((error, response) => {
// response.text ... 请求完成获取到的响应数据
});
如何去解析这些字符串数据呢?接下来我们就会用到cheerio
这个神器,他可以帮助我们解析html,并且可以进行Jquery的一系列操作,例如元素的查询$(element).text()
等等,现在我们加上cheerio
插件。
import request from 'superagent';
const baseUrl = 'https://www.zhipin.com/c101280100-p100901/?page=1&ka=page-1';
request.get(baseUrl).end((error, response) => {
// response.text ... 请求完成获取到的响应数据
if (error) {
throw Error(error);
}
const $ = cheerio.load(response.text, {
decodeEntities: false
});
});
有了cheerio
我们就可以抓取网页上的数据了,现在我们尝试发起请求,我们遍历li下面的数据
$('.job-box .job-list ul').find('li').each((index,element) => {
const el = $(jobList[job_index]);
const salary = el.find('.info-primary .name .job-title').siblings('span').text().split('-'); // 薪资待遇
const job_info = el.find('.job-primary .info-primary p').html().replace(/<em class="vline"><\/em>/g, '$%').split('$%');
const job_company = el.find('.job-primary .info-company .company-text p').html().replace(/<em class="vline"><\/em>/g, '$%').split('$%');
const detail_url = el.find('.job-primary .info-primary .name').find('a').attr('data-jid');
const offer = {
jobId: detail_url,
name: el.find('.info-primary .name .job-title').text(), // 职位名称
companyName: el.find('.info-company .company-text .name').find('a').text(), // 公司名称
minSalary: salary[0], // 薪资待遇
maxSalary: salary[1], // 薪资待遇
education: job_info[2], // 学历要求
experience: job_info[1], // 工作经验
industry: job_company[0], // 行业类型
financing: job_company[1], // 融资情况
companySize: job_company[2], // 公司人数
};
})
上面代码中,我们已经可以拿到当前页的所有职位数据信息了,那如何抓取全部分页的职位数据呢?可以使用递归来进行下一页的抓取,直到抓到最后一页为止。
import request from 'superagent';
let pageIndex = 1;
const recursive = async () => {
const baseUrl = `https://www.zhipin.com/c101280100-p100901/?page=${pageIndex}&ka=page-${pageIndex}`;
request.get(baseUrl).end((error, response) => {
// response.text ... 请求完成获取到的响应数据
if (error) {
throw Error(error);
}
const $ = cheerio.load(response.text, {
decodeEntities: false
});
$('.job-box .job-list ul').find('li').each((index, element) => {
const el = $(jobList[job_index]);
const salary = el.find('.info-primary .name .job-title').siblings('span').text().split('-'); // 薪资待遇
const job_info = el.find('.job-primary .info-primary p').html().replace(/<em class="vline"><\/em>/g, '$%').split('$%');
const job_company = el.find('.job-primary .info-company .company-text p').html().replace(/<em class="vline"><\/em>/g, '$%').split('$%');
const detail_url = el.find('.job-primary .info-primary .name').find('a').attr('data-jid');
const offer = {
jobId: detail_url,
name: el.find('.info-primary .name .job-title').text(), // 职位名称
companyName: el.find('.info-company .company-text .name').find('a').text(), // 公司名称
minSalary: salary[0], // 薪资待遇
maxSalary: salary[1], // 薪资待遇
education: job_info[2], // 学历要求
experience: job_info[1], // 工作经验
industry: job_company[0], // 行业类型
financing: job_company[1], // 融资情况
companySize: job_company[2], // 公司人数
};
})
})
}
recursive();
当每次请求完成后让pageindex +1 最后每次请求后判断分页
!$('.home-inner .page').find('.next').hasClass('disabled')
是否为disabled状态 disabled状态说明已经没有下一页了。则停止抓取。