利用Python语言采集拉勾网的Python职位信息,存到MySQL,然后利用PHP做数据可视化处理。本文主要是谈及Python爬虫部分,涉及到Python的requests,sqlalchemy,redis,lxml等库。
一. 需求分析
把需要采集的职位信息和公司信息整理出来。由于我是把数据存到了MySQL,就用到了Python中非常出名的orm框架SQLAlchemy,所以就把需要采集的职位信息和公司信息的字段,存到models.py里了。
从职位列表里获取职位的简要信息和职位对应公司的简要信息,然后再分别获取职位的详细信息和公司的详细信息。
二. 爬虫架构
用requests请求接口获取职位的列表信息,列表信息里的positionId(职位id)和companyId(公司id)存到redis的集合里,集合能起到一个简单去重的功能。然后分别取职位集合和公司集合的id,构造相应的职位详情页的url和公司详情页的url,为了反爬虫,采用了selenium+Chrome(无头模式)采集详情页数据。
二. 代码实现
1. 职位列表数据
拉勾网的职位信息,每个城市每个分类有很多页职位信息,每一页的数据是ajax动态获取的,所以就需要找到这个数据接口。
找到这个接口地址,去看这个请求的几个关键点,请求URL,请求方式,请求头和请求参数。找到这些信息后,就可以用超级好用的requests去构造请求获取数据了,其中请求参数里的pn是页码(第几页),kd是职位关键字。
def get_job_company(self): fields = ["companyId", "companyShortName", "financeStage", "district", "companySize", "positionLables", "industryField", "positionId", "positionName", "createTime", "positionAdvantage", "salary", "workYear", "education", "city", "companyFullName"] url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E4%B8%8A%E6%B5%B7&needAddtionalResult=false' for i in range(19): res = requests.post(url, data={'first': 'false', 'pn': str(i + 1), 'kd': 'Python'}, headers=self.headers).content.decode() res = json.loads(res) print(res) for job in res['content']['positionResult']['result']: item = {k: v for k, v in job.items() if k in fields} self.redis_db.lpush('lg_job_list', json.dumps(item)) # print(i + 1) time.sleep(5)
这里要注意的是:
(1). requests发送请求时,携带参数必须是字符串,所以请求参数里的pn要转成str;
(2). 拉勾的这个接口请求时,必须带上浏览器的完整请求头信息,只设置User-Agent是不行的,所以直接把浏览器中这个请求的完整的请求头信息拿过来就行了;
(3). 每请求一次后,尽量加上延时,避免请求过快,IP被封。
我这里get_job_company方法是把请求数据接口来的数据存放到redis的列表中,另外一个方法store_job_company取列表中的数据,过滤并保存数据到MySQL,并且数据里的positionId(职位id)和companyId(公司id)存到redis的集合供后续采集详情信息时使用。
2. 职位详情数据和公司详情数据
获取上一步存在集合里的职位id和公司id,构造职位详情页的url和公司详情页的url,使用selenium+Chrome去采集详情数据。
def get_job_info(self): # 初始化chrome浏览器无头模式 chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') while True: job_id = self.redis_db.spop('lg_job_ids') if job_id is None: print('over') break driver = webdriver.Chrome(chrome_options=chrome_options) job_url = 'https://www.lagou.com/jobs/{}.html'.format(job_id) driver.get(job_url) time.sleep(2) element = etree.HTML(driver.page_source) desc = element.xpath('//dd[@class="job_bt"]/div//text()') desc_str = '' for item in desc: desc_str += self.content_pattern.sub('', item) + '*_*' address = element.xpath('//div[@class="work_addr"]//text()') address_str = '' for item in address: address_str += self.content_pattern.sub('', item) driver.quit() # 数据过滤 if address_str == '' or desc_str == '': print({'msg': '失败', 'url': job_url, 'address': address_str[:-4], 'description': desc_str}) # self.redis_db.sadd('lg_job_ids', job_id) continue # 保存数据库 self.session.query(Job).filter(Job.lagou_id == job_id).\ update({'url': job_url, 'address': address_str[:-4], 'description': desc_str}) self.session.commit() print({'url': job_url, 'address': address_str[:-4], 'description': desc_str})
之后保存到mysql中数据就是这样的
四. 总结
主要用到requests去请求拉勾的接口,利用redis的list做一个队列和set做一个简单的去重,然后使用selenium+Chrome(无头模式,新版本的selenium中已不能使用phantomJS)采集详情数据,数据保存到mysql。
我这里贴的代码比较少,完整代码请看访问我的码云上的拉勾爬虫。