nodejs 爬取智联招聘职位信息

1. 摘要

智联招聘汇聚了全国所有最新职位信息、最新招聘信息,是国内专业权威的招聘和职位搜索平台。本项目爬取了杭州地区软件/互联网开发/系统集成等相关职位类别的12000多条职位信息。

2. 工具

  • superagent
  • cheerio
  • async
  • mongodb

3. 前期准备

  1. 在页面选择相应职业类别发现有** 12000 **左右需要爬取的职位


    职业类别.png
  2. 一共只有90页,根据分析发现一个页面有60条数据,也就是只能访问到5400条职位信息,这与显示信息出入太大,调查发现此网站只能显示90页的表格数据
    页数.png
  3. 因此,确定爬取思路是:首先爬取职业类别的职位号,根据得到的职业号,爬取表格的链接,得到所有链接后即可访问所有职位的详情,并存入数据库


    链接.png
详情页png

4. 模块代码

4.1 获取职位类别名称及代码

function start() {
    var job=[];
    var opt = {
        Referer: url,
        'User-Agent': config.url.opt
    }
    superagent
        .get(url)
        .set(opt)
        .end(function (err,res) {
            if(err){
                console.log(err)
            }
            else{
                var $ = cheerio.load(res.text);
                var links = $('#search_jobtype_tag a').toArray();
                for(var i = 2 ; i < links.length ; i++){
                    var caree =  {};
                    var type=$(links[i]).attr('href').split("&");
                    caree.code=type[type.length-1].split('=')[1];
                    caree.name = $(links[i]).text().split('(')[0];
                    caree.number =parseInt( $(links[i]).text().split('(')[1].split(')')[0]);
                    job.push(caree)
                }
                console.log(job)
            }
        })
}

start()
  • 结果输出
var jobList = [ 
        { code: '045', name: '软件工程师', number: 2303},
        { code: '054', name: '网页设计/制作/美工', number: 1020 },
        { code: '2040', name: 'Java开发工程师', number: 1017 },
        { code: '864', name: 'WEB前端开发', number: 970 },
        { code: '044', name: '高级软件工程师', number: 950 },
        { code: '079', name: '软件研发工程师', number: 735 },
        { code: '669', name: '用户界面(UI)设计', number: 575 },
        { code: '2039', name: 'Android开发工程师', number: 455 },
        { code: '057', name: '游戏设计/开发', number: 445 },
        { code: '2041', name: 'PHP开发工程师', number: 366 },
        { code: '687', name: '嵌入式软件开发', number: 365 },
        { code: '047', name: '数据库开发工程师', number: 333 },
        { code: '679', name: '手机软件开发工程师', number: 329 },
        { code: '053', name: '互联网软件工程师', number: 200 },
        { code: '667', name: '系统架构设计师', number: 199 },
        { code: '2034', name: '算法工程师', number: 194 },
        { code: '2038', name: 'IOS开发工程师', number: 177 },
        { code: '2042', name: 'C语言开发工程师', number: 168 },
        { code: '672', name: '游戏界面设计', number: 136 },
        { code: '671', name: '游戏策划', number: 132 },
        { code: '666', name: '系统集成工程师', number: 101 },
        { code: '048', name: 'ERP技术/开发应用', number: 83 },
        { code: '665', name: '需求工程师', number: 83 },
        { code: '863', name: '移动互联网开发', number: 82 },
        { code: '861', name: '用户体验(UE/UX)设计', number: 81 },
        { code: '668', name: '系统分析员', number: 45 },
        { code: '317', name: '语音/视频/图形开发', number: 41 },
        { code: '2037', name: '网站架构设计师', number: 38 },
        { code: '2036', name: '计算机辅助设计师', number: 30 },
        { code: '2043', name: '脚本开发工程师', number: 16 },
        { code: '2035', name: '仿真应用工程师', number: 10 },
        { code: '060', name: '其他', number: 359 } ];
module.exports = jobList;

4.2 获取职位表格页面链接

根据上述得到的职位类别的代码以及职位数量计算页面个数从而得到所有表格页面的代码

var jobList = require("./data/jobs")
function getLink(cb) {
    var links = []
    for(var i = 0 ; i< jobList.length ; i++){
        var page=Math.ceil(jobList[i].number/60);
        for(var j = 1;j <= page;j++){
            var url = config.url.job_url + jobList[i].code +'&jl=%E6%9D%AD%E5%B7%9E&sm=0&p='+ j +"&kt=2&isfilter=1&fl=653&isadv=0";
            links.push(url)
        }
    }
    cb(null,links)
}

4.3 获取职位详情链接

抓取职位表格页面中每项中详情页的链接并保存

表格.png
页面元素.png
//获取详细页面
var jobLink = []
function getDetailLink(links,cb) {
    async.eachLimit(links, 5, function(item, callback) {  
            superagent.get(item).end(function (err, res) {       
                try {
                    if(res.ok) {
                        console.log(item + ' 获取页面中...');
                        getJobLink(res.text);
                        callback();
                    } else {
                        console.log(item + ' 获取页面失败...');
                        callback();
                    }
                    sleep(0.1)
                } catch (e) {
                    console.log(e.message);
                    callback();
                }
            })
    }, function(err) {
        if (err) {
            console.log('Mession Failed!');
        } else {
            console.log('共获取到'+jobLink.length+'个具体页面');
            cb(null, jobLink);
        }
    });
}
function getJobLink(text) {
        var $ = cheerio.load(text);
        var jobs=$('.newlist_list_content table').toArray();
        for(var i=1;i<jobs.length;i++){
        var link=$(jobs[i]).find('div a').attr('href');
            jobLink.push(link)
        }
}

4.4 爬取职位详情并存入数据库

4.4.1 数据库设计

var jobSchema = new Schema({
    posname: String,    //职位类别
    salary: String,        //职位月薪
    pubdate: String,    //发布日期
    type: String,          //工作性质
    exp: String,          //工作经验
    edu: String,          //最低学历
    count: String,       //招聘人数
    company: String, //公司
    desc: String,        //职位描述
    url: {type:String,unique:true}  //详情地址
});

module.exports = mongoose.model('Jobs', jobSchema);

4.4.2 爬取信息

function saveDB(jobLink,cb) {
    async.eachLimit(jobLink,30,function (item,cb) {
        superagent.get(item).end(function (err,res) {
            try {
                if(res.ok) {
                    console.log(item + ' 获取页面中...');
                    grapData(res.text,item);
                    cb();
                } else {
                    console.log(item + ' 获取页面失败...');
                    cb();
                }
            } catch (e) {
                console.log(e.message);
                cb();
            }
        })
    })
}
function grapData(text ,url ){
    var $ = cheerio.load(text);
    var job = {};
    var detail = $('.terminalpage-left ul  li').toArray()
    //职位类别
    job.posname =  ($(detail[7]).text()).split(':')[1];
    //职位月薪
    job.salary = ($(detail[0]).text()).split(':')[1];
    //发布日期
    job.pubdate = ($(detail[2]).text()).split(':')[1];
    //工作性质
    job.type = ($(detail[3]).text()).split(':')[1];
    //工作经验
    job.exp = ($(detail[4]).text()).split(':')[1];
    //最低学历
    job.edu = ($(detail[5]).text()).split(':')[1];
    //招聘人数
    job.count = ($(detail[6]).text()).split(':')[1];
    //公司
    job.company = $('.inner-left h2').text();
    //职位描述
    job.desc = $('.tab-inner-cont').text().replace(/\s+/g, '');
    job.url = url;
    saveJob(job)
    // console.log(job)
}
function saveJob(job){
    var job = new Jobs({
        posname:job.posname,
        salary:job.salary ,
        pubdate:job.pubdate,
        type:job.type ,
        exp:job.exp,
        edu:job.edu,
        count:job.count ,
        company:job.company ,
        desc:job.desc ,
        url:job.url
    });

    job.save(function (err, doc) {
        if (err) {
            console.log("失败");
        }
        else {
            console.log("保存成功");
        }
    });
}

4.5 函数调用

function start() {
    async.waterfall([
        getLink,
        getDetailLink,
        saveDB
    ], function (err, result) {
        if(err) {
            console.log('error: ' + err);
        } else {
            console.log(jobLink.length)
            console.log('任务完成!!!');
        }
    });
}
start();

源码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,074评论 25 707
  • 每个人都有过第一份工作,不管是兼职也好,实习也好。相信一定是一次特别的经历吧。 5年前,我大学还没毕业,就已经有了...
    祎七阅读 424评论 0 2
  • 每个人的生命,时间都是属于他们自己的,我无权指责。但始终努力的生活显然更有价值。 利用白天时间 有阳光照耀的白天,...
    zhishijuncc阅读 471评论 0 1
  • 我们都在寻找着另一半,不管你是男是女,亦或是寻找同性伴侣。 在寻找的时候总是有很多标签和限制,来告诉自己要和什么样...
    长安笔客阅读 1,351评论 4 10
  • 去年的这个时候,我每天都听到隔壁高四楼的某一个班在每天早饭后都纵情地唱着《海阔天空》,一直唱到高考,不过今年,一次...
    清水靓荷阅读 294评论 2 0