5月13日
在之前的编码工作中,我实现了对招聘信息的爬取以及对爬取内容的数据清洗和数据库存储,初步地完成了招聘信息的获取功能。在近期的阶段,我与实现推荐算法的同学进行对接,优化了爬虫的模块,并且对此进行了最终的实现。
网页内容获取方式的最终设计:
由于我们的推荐集所需要的岗位信息包含各行各业,需要设计一种实现方式以一次性爬取到各种行业的数据。我再次对51job招聘网站的信息展现形式进行分析,明确我们需要获取到的数据均为应届毕业生岗位信息,需要加入爬取链接参数的设置为workyear=01;而51job提供的信息搜索可以通过热门城市、行业类别进行筛选,经过多次以不同条件进行搜索获取的数据可以突破每次只能获取到2000条数据的限制,但需要考虑到不同搜索结果重复的问题。我通过对网站的解析,选取了各大热门城市,获取到热门城市的链接参数,以jobArea数组的形式进行存储,在爬虫文件的调用中循环地获取数组的各项城市编码;另外,我搜索了行业类别的相关资料,以尽可能齐全且互相独立的行业类别进行搜索,在程序中以jobType数组的形式进行存储,在爬虫文件的调用中循环地获取数组的各个行业类别搜索内容。爬虫的调用一共需要进入到三重循环中,分别获取各个不同城市、不同行业、每次搜索中不同页面的信息,而为了防止无限循环的发生导致程序无法退出以及爬取数据重复等问题,在爬虫主体函数parse(self, response)中进行parse_page(self, response)函数的循环调用:
def parse(self, response):
jobArea = ["010000", "020000", "030200", "040000", "180200", "200200",
"080200", "070200", "090200", "060000", "030800", "230300",
"230200", "070300", "250200", "190200", "150200", "080300",
"170200", "050000", "120300", "120200", "220200", "240200", "110200", "110300"]
jobType = ["计算机", "互联网", "通信", "电子", "销售", "客服", "会计", "金融", "银行", "保险", "生产", "机械",
"汽车", "运营", "采购", "物流", "服装", "建筑", "房地产", "设计", "市场", "媒体", "广告", "人事",
"行政", "教育", "培训", "生物", "制药", "化工", "医疗", "顾问", "法律", "餐饮", "交通", "翻译"]
for keyword in jobType:
for areano in jobArea:
for pageno in range(5):
url = 'https://m.51job.com/search/joblist.php?jobarea=' + areano + '&keyword=' + keyword + \
'&workyear=01&pageno=' + str(pageno)
yield Request(url, meta={'keyword': keyword}, callback=self.parse_page, dont_filter=True)
而在每次进入到parse_page(self, response)函数以后,需要继续对于该搜索页面的每条岗位具体内容界面进入跳转,循环地获取所有岗位信息的具体内容链接,再进入到之前编写的parse_item(self, response)函数中。在这里item的初始化之后,可以将获取到的岗位信息中加入行业类别列:
def parse_page(self, response):
q = response.css
items = q(".list a").xpath("@href").extract()
item = PositionItem()
item['job_type'] = response.meta['keyword']
for url in items:
yield Request(url, meta={'item': item, 'url': url}, callback=self.parse_item, dont_filter=True)
与推荐算法所需数据结构的对接:
由于推荐模型中所需的数据项对比之前有所变更,在爬虫模块中需要改变获取到的数据列。需要加入行业类别信息;需要在获取数据的过程中就把岗位需求项完成初步解析,加入岗位需求解析之后的tfidf字段,这里加入了文本解析函数:
def cal_tf(self, row: str):
data = []
xx = jieba.cut(row)
for j in xx:
if j not in s:
data.append(j)
result = dict(Counter(data))
re = ""
for r in result:
if r in idf:
re += "/" + r + ":" + str((float(result[r])/len(result)) * float(idf[r]))
return re
在PositionItem(scrapy.Item)的声明中加入job_type项与tfidf项,并改变数据表结构,将cal_tf(self, row: str)函数返回的解析内容存入item的tfidf项中,使得最终获取到的数据可以直接被推荐算法模块所调用。
爬取信息的获得:
运行爬虫文件,将自动对51job招聘网站上26个热门城市、36种行业类别的应届毕业生岗位信息进行获取,并在爬取的过程中进行岗位描述数据的初步解析,同时清洗不符合结构规范的数据,再自动存储至数据库中。由于需要获取的数据量较大,爬虫运行的时间长达几十分钟,获取到已经过清洗的有效岗位数据共有十多万条。
最后,由于有些岗位链接在完成了招聘需求之后会失效,项目在运行的过程中需要随时进行最新岗位信息的获取,所以需要定期使用到爬虫模块进行推荐集的更新。设置定时事件可以完成程序对推荐集的自动更新功能,系统可以随着定时事件的发生来爬取到最新的招聘信息以更新推荐集,以实现我们的项目进行当周最新岗位内容的匹配与推荐。至此,爬虫模块的工作已全部完成。