?Echarts统计拉勾网招聘信息(scrapy 爬取)

news/2024/6/18 13:10:15

前言

图片描述

今天是2018的第一天,首先祝各位小伙伴元旦快乐!
又到了新的一年,虽然离春节还有一段时间,但是程序狗打工不易啊,不关注薪资怎么行。今天要做的就是用图表统计一下现在各公司的薪资状况(虽然很多公司不能按照招聘上他们给的薪资来给)。

数据爬取

本次使用scrapy来做数据爬取,这是一个python的框架。因为本人在成都从事web前端,所以这次爬取的关键词既是:成都,web前端

scrapy startproject lagou

首先通过运行命令,得到一个爬虫项目的基础结构。

接着按照scrapy的中文教程,通过在

start_urls = [
        "https://www.lagou.com/jobs/list_web%E5%89%8D%E7%AB%AF?labelWords=sug&fromSearch=true&suginput=web"
    ]

spider中的start_urls配置好,应该就能把拉勾网页面拉取下来,然后再分析dom,提取字符串就可以了,无奈这种方法并不行。

起初也不知道,就用xpath一直找,后来发现找不到会报错,这些各种错误对于我这个爬虫萌新还是懵逼的。仔细查看他的network发现,他的招聘信息都是在另外的ajax请求当中,并且还是整理好的。

图片描述

因为本人工作1年多,所以主要关注点是3年以下及3-5年,就提前选好了,城市和工作年限。该请求的传参是formdata,其中first是首页(其实写代码的时候并没有注意这个参数,所以一直传的是true,貌似也没什么影响),pn是当前页数,kd是关键词。

图片描述

于是乎就去文档查阅了一下,如何在scrapy中循环发送formdata请求。最终得到这样一段可以执行的代码。

def start_requests(self):
        url = "https://www.lagou.com/jobs/positionAjax.json?gj=3%E5%B9%B4%E5%8F%8A%E4%BB%A5%E4%B8%8B%2C3-5%E5%B9%B4&xl=%E6%9C%AC%E7%A7%91&px=default&city=%E6%88%90%E9%83%BD&needAddtionalResult=false&isSchoolJob=0"
        for i in range(1, 14):
            formdata = {'first': 'true', 'pn': str(i), 'kd': 'web前端'}
            yield scrapy.FormRequest(str(url), callback=self.parseJson, formdata=formdata)

start_requests是发送post请求的方法,FormRequest这个方法接收请求url,传递数据formdata,以及回调函数parseJson。parseJson在这里主要是接收获取的数据。

仅仅有这个是不够的,因为貌似拉勾网有反爬虫,没有header好像得不到数据(这个还待论证,至少我这边是)。然后再settings.py文件中做了一些配置,配置主要有:

  • 请求的header(主要是这几项)
DEFAULT_REQUEST_HEADERS={
Accept:application/json, text/javascript, */*; q=0.01
Host:www.lagou.com
Origin:https://www.lagou.com
Referer:https://www.lagou.com/jobs/list_web%E5%89%8D%E7%AB%AF?px=default&gj=3%E5%B9%B4%E5%8F%8A%E4%BB%A5%E4%B8%8B,3-5%E5%B9%B4&city=%E6%88%90%E9%83%BD
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
}
  • FEED_EXPORT_ENCODING(因为爬取到的中文是unicode字符)
FEED_EXPORT_ENCODING = 'utf-8'
  • ROBOTSTXT_OBEY(这是一个爬虫机器的协议,如果是true,表示遵守,有些网站禁止爬取的话,这个如果是true就爬不到了)
ROBOTSTXT_OBEY = False

  • DOWNLOAD_DELAY(延时,这个也是去避免被反爬虫,我这边直接设置了比较长的时间,也没有去测试多少合适,因为不设置也是会报错的)
DOWNLOAD_DELAY = 10

基础的配置项配置完毕之后,就是写数据存储的模型了,因为我只想去简单统计一下,所以只存了薪资和工资这两个字段,想要统计更多的信息,就直接继续加就好了,这个比较简单,在items.py中编写

class LaGou(scrapy.Item):
    salary = scrapy.Field()
    company = scrapy.Field()

经过这几项配置,运行命令

scrapy crawl lagou -o a.json

就可以得到一份a.json,里面就是成都web前端相关,工作年限为0-5年的数据信息了。有了这份数据,接下来要做的就是数据处理了。

数据处理

在之前的a.json当中,大致可以得到一份之下的数据,总计195条

[
{"salary": "8k-16k", "company": "xx有限公司"},
......
]

为了前端处理方便,直接改为js文件加一个变量引入html,即

var a = [
    {"salary": "8k-16k", "company": "xx有限公司"},
    ......
    ]

这组数据的薪资是一个范围,不方便我统计,于是为了便于操作数据把薪资取平均值,并统计提供相同的薪资的公司数目。
js代码如下:

var arr = data.map(function (value) {
        return value.salary && value.salary.replace(/k|K/g, "").split('-').reduce(function (pV, nV) {
            return pV + nV / 2
        }, 0)
    }).reduce(function (pV, nV) {
        nV in pV ? pV[nV]++ : (pV[nV] = 1);
        return pV;
    }, {})
    //这里的data既是上边的a变量

这段代码主要作用是把薪资范围计算成平均数,然后再统计数组中相同的平均数的个数。代码写的随意,可读性较差,见谅。这段代码处理过后,可得到类似如下数据:

{'8':1,'8.5':3}

key是薪资均值,value是个数。

于是将key,value分别存入数组。这里遇到一个问题,就是开始我是这样操作的

var xData=[...Object.keys(arr)]
var yData=[...Object.values(arr)]

这么做有一个问题就是浏览器对于对象的遍历规则,导致输出的数组,小数都到了最外边(比如这样[1,2,1.5]),这样在echarts下的图表是乱序的。也没有想到好的办法去解决,就是对数组进行一次排序,然后再根据排好的key生成相对应的value数组,最终代码:

    var xData = [...Object.keys(arr).sort(function (a, b) {
        return a - b
    })]
    var yData = xData.map(function (v) {
        return arr[v]
    })

echarts比较简单不赘述。将这两组横纵坐标输入echarts,得到最终效果:
图片描述

总结

本次做这个统计很多地方没想清楚怎么更好的去表现,所以做的很简单,其实细致一点还可以去分类统计,按照公司融资情况,领域等等内容,只要数据拿到都好说。另外很多地方可能写的不够好,主要我目前也不太会写,比如之前反爬虫那块,貌似去做动态的用户代理也能行,但我还是增加了延时,选择了比较笨的方法。另外也不会python,但还好python比较好读。因为这一块才开始学习,相信以后会越写越好的,新的一年,加油!

update 2018/01/03

昨天又把爬虫优化了一下,去掉了之前的延时,增加了动态用户代理和动态IP代理,解决了之前爬虫的效率问题,也扩大了数据量。

动态IP代理

通过网上搜索免费的ip代理,获取了如下一组ip:

PROXIES = [
    {'ip_port': '106.39.179.244:80'},
    {'ip_port': '65.52.223.99:80'},
    {'ip_port': '1.52.248.207:3128'},
    {'ip_port': '45.77.198.207:3128'},
    {'ip_port': '177.125.119.16:8080'},
    {'ip_port': '174.138.65.233:3128'},
]

该IP过一段时间可能会失效,请自行搜索,如http://www.xicidaili.com/。
在middlewares.py中声明该IP,之后声明动态IP代理类

    import random
    class ProxyMiddleware(object):
        def process_request(self, request, spider):
            proxy = random.choice(PROXIES)
                request.meta['proxy'] = "http://%s" % proxy['ip_port']
                print("**************ProxyMiddleware no pass************" + proxy['ip_port'])

在settings.py文件中声明该中间件

DOWNLOADER_MIDDLEWARES = {
    'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 110,
    'tutorial.middlewares.ProxyMiddleware': 100,
}

动态用户代理

在middlewares.py中声明动态用户代理类

class RandomUserAgent(object):
    """Randomly rotate user agents based on a list of predefined ones"""

    def __init__(self, agents):
        self.agents = agents

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.getlist('USER_AGENTS'))

    def process_request(self, request, spider):
        # print "**************************" + random.choice(self.agents)
        request.headers.setdefault('User-Agent', random.choice(self.agents))

同样在settings.py的中间件里声明
DOWNLOADER_MIDDLEWARES = {

'tutorial.middlewares.RandomUserAgent': 1,
'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 110,
'tutorial.middlewares.ProxyMiddleware': 100,

}
再次运行scrapy crawl lagou,即可得到新的数据。

增加薪资筛选

在原有基础上增加了对于工作年限和公司规模的筛选,并计算了平均值。
更新代码如下:

// 指定图表的配置项和数据
initData();  
function initData() {
        average = 0;
        arr = temData.map(function (value) { //之前正则筛选字符串有点问题,没有考虑到有些公司格式为10k以上这种。
            return value.salary && value.salary.replace(/[k|K\u4e00-\u9fa5]/g, "").split('-').reduce(function (pV, nV, i, array) {
                if (array.length > 1) {
                    average = Number(average) + pV + nV / 2
                    return pV + nV / 2
                } else {
                    average = +average + Number(nV)
                    return nV
                }
                // return array.length > 1 ? pV + nV / 2 : nV
            }, 0)
        }).reduce(function (pV, nV) {
            nV in pV ? pV[nV]++ : (pV[nV] = 1);
            return pV;
        }, {})
        average = (average / temData.length).toFixed(2)
    }

暂时这样,通过之后的学习,还会不断的优化。

展示效果:
图片描述

源码地址:https://github.com/jiwenjiang...


http://www.niftyadmin.cn/n/3569836.html

相关文章

HTML游戏开发实践之跨平台开发

在上一篇的HTML5游戏开发实践之HTML5框架的使用(一)中我们开头提到了跨平台开发,众所周知跨平台特性是HTML5与身俱来的, 常常只需付出少量的额外工作便可做到。然而, 在实际工作中有几个你需要认真考虑的事情... 首先也是最重要的, 屏幕尺寸可…

http缓存提高性能

秋招也算是正式结束了,现在整理一下笔记,当作巩固一下知识,也希望这个对大家有帮助 http 缓存 和 cdn 缓存可以说是面试必问的问题,竟然是必问的问题,那就总结全面一点~ http缓存机制 缓存分为服务端侧&…

基于matlab数字处理系统设计新颖,数字信号处理——基于数值计算

郑佳春、陈仅星、陈金西主编的《数字信号处理--基于数值计算》系统地介绍了数字信号处理的基本理论和算法,给出了利用MATLAB在计算机上实现数字信号处理系统分析、设计、计算的实例。全书包括三大部分:①离散时间信号和离散时间系统分析,主要…

跟着郝斌学数据结构(10)——链式二叉树

# include <stdio.h> # include <malloc.h> struct BTNode { char data; struct BTNode * pLchild; //p是指针 L是左 child是孩子 struct BTNode * pRchild; }; void PostTraverseBTree(struct BTNode * pT); struct BTNode * CreateBTree(void); void PreTra…

HTML5游戏开发实战之游戏自动保存进度

大概玩游戏的时候最讨厌的就是游戏没打完退出之后的状态了&#xff0c;但是在html5游戏上则不会出现这种情况。像site pinning特性, 试图给web浏览器web应用程序和常规的桌面应用同样的地位。 然而, 作为应用程序运行的网站想法太新了, 网页要维持客户端状态。 关闭Microsoft W…

Oracle的ha模式启停,Oracle 启停

Oracle 启停步骤一: 以管理员方式连接 oracle(1)oracle$ sqlplus /nolog; -- 不在 cmd 或者 terminal 当中暴露密码的登陆方式SQL> conn / as sysdba;(2)oracle$ sqlplus "/as sysdba" ; 或者 sqlplus / as sysdba;(3)oracle$ sqlplus scott/tiger; -- 非管理员用…

客户端和服务端对异常的不同处理态度【JFinal】

服务端后台管理这种如果代码中抛出异常&#xff0c;其Tx事务能起作用&#xff0c;但是在客户端与服务端交互的时候很多都是通过JSON或者XML格式&#xff0c;为了用户体验&#xff0c;成功的话就正常返回&#xff0c;不成功也应该给出失败的信息&#xff0c;而不是直接报505这种…

HTML5游戏开发实践之使用监控器

开发游戏的一个最大挑战是加入越来越多的游戏特性的同时能保持高帧率。好消息是, 浏览器比过去几年快了很多,HTML5开发游戏运行在恒定的60fps已经成为现实。这实属不易。 对于IE9,意味着要编写一个全新的JavaScript引擎, 使用多CPU内核和基于Direct2D的完全的硬件加速渲染管道。…