博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
人才市场的IT职位分析
阅读量:7108 次
发布时间:2019-06-28

本文共 7809 字,大约阅读时间需要 26 分钟。

最近要找长沙的工作,于是通过湖南人才市场搜索了一下职位。结果得到的数据让我很难比较,作为一个 IT 业滚爬了多年的程序员,对这样的搜索结果很不满意。于是,我不得不自己来整理数据了。本文内容包括:网页数据抓取、网页数据分析、数据挖掘,python 的多线程,多进程应用等话题。

先上结论

先给出以上概略图,由图可以得出以下结论:

  • 长沙的的IT行业大多数工资开在 2500-3500 之间
  • 有 40% 左右的企业需要面谈工资或者对工资有自己的规定(不使用网站上设置的工资级别)
  • Java 语言的需求量远高于其它语言的需求量

当然,还可以挖掘出更多的有效信息。如按工资排名的职位:

这样,我就可以先快找到当前长沙的高工资职位及要求了。这就是做程序员的好处 ^_^。以下将介绍数据抓取及数据分析。

数据抓取

我写了一个 crawler.py 程序。内容如下:

# -*- coding: utf-8 -*-import sys, os, reimport http.clientimport threadingimport timeids = []# 生成要抓取的职位列表IDdef generate_list_ids():    global ids    for i in range(1, 77):        ids.append(i)# 生成要抓取的职位详情IDdef generate_detail_ids():    global ids    for i in range(1, 77):        f = open(str(i)+'.lst', 'r', encoding='gbk')        s = f.read(1024000)        inputs = re.findall(r"", s)        for inp in inputs:            m = re.match(r".*value='(.*)'", inp)            ids.append(m.group(1))        f.close()# 用多线程的方式抓取,单线程太慢了class Crawler(threading.Thread):    def __init__(self, islist=True):        self.h = http.client.HTTPConnection('www.hnrcsc.com')        self.islist = islist        threading.Thread.__init__(self)    # 抓取到的网页,将它存入文件    def write_file(self, filename):        o = open(filename, 'wb')        o.write(self.h.getresponse().read(1024000))        o.close()    # 抓取职位列表    def get_list(self, cid):        self.h.request('POST', '/Search/searchResult.asp?pagenum='+str(cid), 'flag=0&wkregion=430100&keywordtype=&postypesub=&postypemain=0100&keyword=%C7%EB%CA%E4%C8%EB%B9%D8%BC%FC%D7%D6&during=90&pagenum='+str(cid), {
'Content-Type': 'application/x-www-form-urlencoded'}) self.write_file(str(cid)+'.lst') def get_detail(self, cid): try: self.h.request('GET', '/jobs/posFiles/showPosDetail.asp?posid='+str(cid)) self.write_file(str(cid)+'.det') except: print(cid) self.h.close() time.sleep(3) self.h = http.client.HTTPConnection('www.hnrcsc.com') def run(self): global ids cid = ids.pop() if len(ids)>0 else None while cid: if self.islist: self.get_list(cid) else: self.get_detail(cid) cid = ids.pop() if len(ids)>0 else None print(self.name + ' Finished!') self.h.close()if len(sys.argv) != 2: print('''Usage: crawler.py commandlist: get listdetail: get detailclean: clean all webpage''') exit()if sys.argv[1] == 'detail': # 抓取职位详情 generate_detail_ids() for i in range(50): Crawler(False).start()elif sys.argv[1] == 'list': # 抓取职位列表 generate_list_ids() for i in range(10): Crawler().start()elif sys.argv[1] == 'clean': # 删除所有抓取到的文件 os.system('del *.lst') os.system('del *.det')else: print('''Usage: crawler.py commandlist: get listdetail: get detailclean: clean all webpage''')

以上是最终的程序,《黑客与画家》中提到,写程序是一个类似于绘画的过程,这里是可用的半成品了。我大概描述一下完成这个程序的过程:

  1. 用 http.client 取一个网页的数据,很快就可以了解 http.client 的用法
  2. 用一个 for 循环取 2 个页面的数据,测试一下 http.client 多次取数据的情况
  3. 已经完成的内容放到一个方法(get_list)中去,待用
  4. 写一个读文件,并且用正则表达式取出所有职位详情ID的代码,测试通过后,将这部分代码注释掉
  5. 用 http.client 取一个职位详情的数据
  6. 汇总三个内容,即可得到单线程抓取网页的程序

我运行这个程序,然后就跑出去吃饭去了,回来一看,还没有运行完,于是狠下心来看了一下 python 多线程的内容,把它改成多线程的代码,加上一些容错的处理,就完工了。

数据分析

数据分析实际上就是从职位列表文件与职位详情文件当中取到有效信息,将其放入关系数据库中,然后就可以利用关系数据库的强大查询语句来得到重要信息了。数据分析的关键,实际上是用正则表达式匹配关键的数据部分。以下是我写的 passer.py 代码

# -*- encoding: utf-8 -*-import re, sqlite3, multiprocessingclass Passer(multiprocessing.Process):    def __init__(self, ids, datas):        self.ids = ids        self.datas = datas        multiprocessing.Process.__init__(self)    # 获取职位详情数据    def get_detail(self, fid):        f = open(str(fid)+'.det', 'r', encoding='gbk')        s = f.read(1024000)        out = re.match(r'''.*招聘人数.*.*人 .*    发布日期.*(.*) .*.*招聘部门.*(.*) .*截止日期.*(.*) .*发布单位.*target="_blank">(.*)  .*工作方式.*(.*) .*最低学历要求.*(.*) .*工作地区.*(.*) .*薪酬待遇.*(.*) .*.*详细待遇.*class="zhongxia2" colspan="3" align="left" >(.*) .*联系电话.*(.*) .*电子邮件.*(.*)  .*联 系 人.*class="zhongxia2" >(.*) .*通讯地址.*(.*?) .*岗位描述.*(.*).*岗位要求.*(.*).* 
''', s, re.S) if not out: out = re.match(r'''.*招聘人数.*.*人 .*    发布日期.*(.*) .*.*招聘部门.*(.*) .*截止日期.*(.*) .*发布单位.*target="_blank">(.*)  .*工作方式.*(.*) .*最低学历要求.*(.*) .*工作地区.*(.*) .*详细待遇.*class="zhongxia2" colspan="3" align="left" >(.*) .*联系电话.*(.*) .*电子邮件.*(.*)  .*联 系 人.*class="zhongxia2" >(.*) .*通讯地址.*(.*?) .*岗位描述.*(.*).*岗位要求.*(.*).*
''', s, re.S) f.close() return out.groups() # 处理职位列表 def handle_file(self, fileid): global datas f = open(str(fileid)+'.lst', 'r', encoding='gbk') a = re.findall('', f.read(1024000), flags=re.S) for i in a: out = re.match(r'''.*.*(.*).*(.*).*(.*).*(.*).*(.*).*(.*).*''', i, re.S) detail = self.get_detail(out.group(3)) if (len(detail) == 15): start,dep,end,comp,ptype,degree,area,money,salary,telephone,email,contact,address,desc,require = detail else: start,dep,end,comp,ptype,degree,area,salary,telephone,email,contact,address,desc,require = detail money = None cid,cname,pid,pname,reqnum,ldegree,larea,pubtime = out.groups() start = start.replace('.', '-') end = end.replace('.', '-') if money: submoney = re.match('(.*)-(.*)元', money) if submoney: lowmoney,highmoney = submoney.groups() else: lowmoney,highmoney = 0,0 else: lowmoney,highmoney = 0,0 telephone = telephone.strip() desc = desc.replace(' ', '').replace('
', '') require = require.replace(' ', '').replace('
', '') salary = salary.replace(' ', '').replace('
', '') pubtime = pubtime.split('-') month = pubtime[1] if len(pubtime) == 2 else '0'+pubtime[1] day = pubtime[2] if len(pubtime[2]) == 2 else '0'+pubtime[2] pubtime = pubtime[0] + '-' + month + '-' + day reqnum = reqnum if reqnum != '若干' else 0 self.datas.append((pid, pname, reqnum, pubtime, start, end, cid, comp, dep, ptype, degree, area, lowmoney, highmoney, salary, telephone, email, contact, address, desc, require, fileid)) f.close() def run(self): cid = self.ids.pop() if len(self.ids)>0 else None while cid: print('LEN: ', len(self.ids)) self.handle_file(cid) cid = self.ids.pop() if len(self.ids)>0 else None print(self.name + ' Finished!')# 使用多进程方法,只有主进程才执行以下部分if __name__ == '__main__': # 用 Manager 生成数据,供各个线程共享 manager = multiprocessing.Manager() ids = manager.list() datas = manager.list() passers = [] for i in range(1, 77): ids.append(i) # 开出 7 个进程来处理 for i in range(7): p = Passer(ids, datas) p.start() passers.append(p) # 等待所有的进程都完成之后,再将数据插入到 sqlite 中去 for p in passers: p.join() # 创建 sqlite 数据库 conn = sqlite3.connect('out.db') c = conn.cursor() # 初始化职位表 c.execute('drop table if exists position;') c.execute('''CREATE TABLE position (pid integer,pname text,reqnum integer,pubtime datetime,start datetime,end datetime,cid integer,comp text,dep text,ptype text,degree text,area text,lowmoney integer,highmoney integer,salary text,telephone text,email text,contact text,address text,desc text,require text,pagenum integer); ''') conn.commit() for d in datas: try: c.execute('insert into position values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', d) except: print(d) conn.commit() c.close() conn.close()

在分析抓取数据的时候,要用多进程,这是为了充分利用CPU。抓取数据用多线程,是因为延迟在网络IO上,所以,哪怕是多进程,也不会有多高的效率提升。

在多进程数据分析运行起来之后,我在电脑再做其它的事情,就响应缓慢了。双核CPU都是 100% 的使用率。这时才想起多核的好处^_^,要是我有八核CPU,好歹还可以空一个核来响应我的桌面操作。

转载于:https://www.cnblogs.com/1si2/p/analyse.html

你可能感兴趣的文章
让WebForm异步起来(转)
查看>>
jQuery UI Portlet 1.1.2 发布
查看>>
给字符数组赋值的方法
查看>>
Excel_replace
查看>>
Typescript Mixins(混合)
查看>>
C#Css/Js静态文件压缩--Yui.Compressor.Net
查看>>
配置tomcat的https通信(单向认证)
查看>>
Intellij Idea @Autowired取消提示
查看>>
httpclient调用https
查看>>
Oracle数据库中有关记录个数的查询
查看>>
sql语句查询出表里符合条件的第二条记录的方法
查看>>
题目1008:最短路径问题
查看>>
PHPExcel正确读取excel表格时间单元格(转载)
查看>>
继电器是如何成为CPU的
查看>>
分布式缓存
查看>>
Android-ViewPager+Fragment数据更新问题
查看>>
[WASM] Compile C Code into WebAssembly
查看>>
JAVA8-让代码更优雅之List排序
查看>>
为什么我的淘宝排名会突然下降?
查看>>
SecureCRT常用快捷键
查看>>