全部的代码见我的GitHub
这里改进了一下之前文章-拉勾网职位数据爬取,由于拉勾网最多只会显示30页的职位信息,为了获取更多的职位信息,就要分类爬取。
由于北京的Python职位很多,超过了30页的部分就不显示了,我为了能够比较全的爬取数据,就进行了分类爬取。这里我选择公司规模这个类别:
小于15人 15-50人 50-150人 150-500人 500-2000人 2000人以上
这个类别不会重复,而且每个小类下的数据也不会超过30页。
类别分析
这个类别不同体现在URL上,下面是小于15人
的URL:
https://www.lagou.com/jobs/positionAjax.json?px=default&gm=%E5%B0%91%E4%BA%8E15%E4%BA%BA&city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false&isSchoolJob=1
其中gm=%E5%B0%91%E4%BA%8E15%E4%BA%BA
就是小于15人
。
下面是全部类别的公司规模部分的网址:
小于15人 gm=%E5%B0%91%E4%BA%8E15%E4%BA%BA
15-15人 gm=15-50%E4%BA%BA
50-150人 gm=50-150%E4%BA%BA
150-500人 gm=150-500%E4%BA%BA
500-2000人 gm=500-2000%E4%BA%BA
2000人以上 gm=2000%E4%BA%BA%E4%BB%A5%E4%B8%8A
页数分析
总页面数其实在POST请求后发回来的JSON数据中。
result['content']['positionResult']['totalCount']
代码实现
循环公司规模这个类别,然后在每个分类下找到页面数,然后用之前写好的方法进行爬取。
import requests
from fake_useragent import UserAgent
from lxml import etree
import csv
import json
import time
import pandas as pd
请求头Headers、表单Form Data补全
请求头是为了反反爬虫的,表单是用来POST请求时提交的。
Host = "www.lagou.com"
Origin = "https://www.lagou.com"
Referer = "https://www.lagou.com/jobs/list_Python?px=default&gx=&isSchoolJob=1&city=%E6%9D%AD%E5%B7%9E"
ua = UserAgent()
headers = {
'User-Agent':ua.random,
'Host':Host,
'Origin':Origin,
'Referer':Referer
}
data= {
'first': False,
'pn': "1",
'kd': 'Python'
}
URL构造
下面的URL中的gm部分被替换为{}
,后面根据类别来补全。
url = "https://www.lagou.com/jobs/positionAjax.json?px=default&gm={}&city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false&isSchoolJob=1"
类别字典
下面是公司规模的类别字典列表。这是用来补全到URL的gm参数中的。
gm_list = [{'class':'小于15人','url':'%E5%B0%91%E4%BA%8E15%E4%BA%BA'},
{'class':'15-50人','url':'15-50%E4%BA%BA'},
{'class':'50-150人','url':'50-150%E4%BA%BA'},
{'class':'150-500人','url':'150-500%E4%BA%BA'},
{'class':'500-2000人','url':'500-2000%E4%BA%BA'},
{'class':'2000人以上','url':'2000%E4%BA%BA%E4%BB%A5%E4%B8%8A'}]
职位信息、总页数获取函数
def getPosition(url,headers,data,page):
data['pn'] = str(page)
response = requests.post(url,headers = headers,data = data)
print(response.status_code)
result = response.json()
if result['success']:
print("正常获取")
position = result['content']['positionResult']['result']
totalCount = result['content']['positionResult']['totalCount'] #返回的职位总数(用来计算页数)
pageCount = int(totalCount)//15 + 1 #每页15条职位,这里向下整除15
time.sleep(1) # 获取正常的情况下延时1s请求一次
return position,pageCount
else:
print("您操作太频繁,请稍后再访问")
time.sleep(10) # 出现异常时,间隔10s后再获取
position,pageCount = getPosition(url,headers,data,page) #递归获取
return position,pageCount
主函数
这里通过循环整个类别字典来依次爬取对应类别的全部职位信息。主要就是:
for gm in gm_list:
gm_url = url.format(gm['url'])
...
#对应类别的职位信息爬取过程
...
#下面是保存到csv文件
total_df.to_csv('Python-School-Beijing-{}.csv'.format(gm['class']), sep = ',', header = True, index = False)
for gm in gm_list:
gm_url = url.format(gm['url'])
#下面是寻找“总页面”(pageCount)的过程
position,pageCount = getPosition(gm_url,headers,data,page = 1)
#下面是初始化total_df过程
df = pd.DataFrame(position)
total_df = df
#如果只有一页,那么直接得到了所有的职位数据total_df
if pageCount > 1:
for page in range(2,pageCount+1): #注意:第一页的数据已经存储起来了,这里从第二页开始
position,_ = getPosition(gm_url,headers,data,page)
df = pd.DataFrame(position)
total_df = pd.concat([total_df,df],axis = 0)
#下面输出一定信息到控制台
print("{}".format(gm['class']) + '\n' + '总页数为:' + str(pageCount))
total_df.info()
#下面是保存到csv文件
total_df.to_csv('Python-School-Beijing-{}.csv'.format(gm['class']), sep = ',', header = True, index = False)
全部的代码见我的GitHub