初识爬虫
工作期间,初识爬虫,结合自己工作需要遂萌生了一个有趣的想法----通过爬取招聘网站某些岗位的信息,来了解该岗位在招聘市场以及如今的大环境下的需求及基本情况。
项目准备
1.数据来源:
boss直聘网站,"www.zhipin.com"
2.需要的数据:
发现界面内的:岗位名称、薪酬、工作地点、工作经验、学历、企业信息(公司名、行业、阶段、规模)都是需要的信息。更详细的信息可以通过进入岗位具体页面获得。
3.思路
那么知道需要获得的信息后要如何获取信息呢?先下载页面,后解析。需要得到每个页面的url,观察发现:
https://www.zhipin.com/c100010000/h_100010000/?query=%E6%95%B0%E6%8D%AE&page=2&ka=page-2
1."https://www.zhipin.com/c100010000/h_100010000/?query="
2.%E6%95%B0%E6%8D%AE
3.&page=2&ka=page-2
url由可以拆分成1,2,3这三部分,2是检索岗位中文字符的转码,3是页码,在1,2固定的前提下,只要改变页码就能不断生成新的url。
但是问题来了,当给定一个岗位时,页面最多只能显示10页的数据,这个数据量明显是不够的,那么如何解决这个问题来获得大量更多的数据呢?
我们发现,在工作经验等类别标签下有许多详细的选项,每个选项都能有一堆页码可以显示,于是爬虫思路改为:先获得各个类别标签下的url,再拼入岗位名称和页码。
基于此,接下来就是见证奇迹的时刻了!
4.操作
4.1爬取网页信息
import requests
from bs4 import BeautifulSoup
import re
import pandas as pd
import os
import glob
import collections
#获得网页数据
def get_web(url):
#伪装自己,不被反爬
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2'
}
req = requests.get(url,headers = headers)
text = req.text
return text
'''
请自行更改文件路径
'''
#获得url-list,input:soup后的lxml文件
def get_info(List):
url = []
for x in List.find_all('a'):
url_raw = x['href'].split('%')[0]
for i in range(1,11):
mid = 'http://www.zhipin.com'+url_raw+'='
url.append(mid)
return url[0:15]
#得到url-list,input:是下载的一个页面文件;output:两个字典url字典和详细类别字典
def get_url_list(file):
f = open('/Users/tony/Desktop/招聘岗位数据分析/raw_data/'+file)
text = f.read()
soup = BeautifulSoup(text,'lxml')
soup2 = soup.find_all('ul')[4:9]
#类别下详细分类
soup3 = [x.get_text().split('\n')[1:-1] for x in soup2]
#类别
soup4 = ['exp','degree','salary','stage','scale']
#得到url
num = 0
dt_url={}
Dict={}
for i in soup2:
url_raw = get_info(i)
key = soup4[num]
dt_url[key] = url_raw
Dict[key] = soup3[num]
num+=1
return dt_url,Dict
#得到字典{'exp':[],'salary':[].....}
dt_url,Dict = get_url_list('数据page1.txt')
'''
因为不考虑类别抓的数据很少,所以需要考虑类别,按'exp','degree','salary','stage','scale'来取数据,则需要获得它们对应的url
以上两个函数最终得到类别对应的初始url和类别中包含的详细分类情况。
'''
#拼接url,dt_url是一个字典;参数分别是:检索文字,页码,类别,类别url字典。
def get_url(strings,page_num,types,dt_url):
url = [x+strings+'&page='+str(page_num)+'&ka=page-'+str(page_num) for x in dt_url[types]]
return url
#strings-岗位关键词;page_num-要爬的总页码;types-按照那些属性爬取:exp,salary....参数:同上,开始结束页码。
def get_txt(strings,types,dt_url,start,end):
for x in range(start,end+1):
#得到一个types下某一页的URL列表
URL = get_url(strings,x,types,dt_url)
count = 0
for url in URL:
count+=1
text = get_web(url)
with open('/Users/tony/Desktop/招聘岗位数据分析/raw_data/'+strings+'_'+str(count)+types+'page'+str(x) + '.txt','w',encoding = 'utf-8') as f:
f.write(text)
print('已抓取'+'第'+str(x)+'页'+'的网页信息')
#可以写个random函数,随机间隔爬取,偷懒间隔10s一次
time.sleep(10)
#爬取每一个标签下的网页,数据量巨大。按工作经验爬取工作经验标签下1-10页的数据,每页30条,有6个标签;一类属性有至少1.8k条记录;有exp,salary,等5个属性标签。
#input:岗位;分类标签,输入参数:类别字典、检索文字、类别url字典。
def get_all(types_list,strings,dt_url,start,end):
for types in types_list.keys():
get_txt(strings,types,dt_url,start,end)
print('已抓取'+types+'类')
'''
分别是url拼接函数,某一类别下所有子项某一页的url;抓取某个类别某一页的函数;获得所有类别页面函数。
'''
爬的时候发现,网站也不是傻子,大概每爬100-200条数据,就会出现验证码,由于是初级爬虫,我也就没有去设置代理ip或者解决验证码这一问题,只能呆呆的每次手动输入验证码,然后从暂停的页面开始重新爬取;此外优化过代码以后,可以定制检索的职位信息,和爬取的初始和结束页码,也算是一种解决方法吧。
4.2解析网页
根据页面信息,想要得到的数据无非就是:岗位、薪水、位置、学历、经验、企业信息(企业名称、行业、阶段、规模)这些。用bs解析,可以得到。此外还获取了每个岗位的url详情,方便之后继续进入爬取岗位描述、职责、公司简介、岗位技能等信息。
#解析网页得到列表result0;input:lxml职位信息list
def get_list0(list0):
result0 = []
for item in list0:
detail_url = 'https://www.zhipin.com'+item.find('a')['href']
mid = item.p.contents
#location详细程度不同,只保留主城市名字
location = mid[0].split(' ')[0]
experience = mid[2]
education = mid[-1]
#company规模
company = item.find('div',class_='company-text').a.get_text()
industry = item.find('div',class_='company-text').p.contents[0]
stage = item.find('div',class_='company-text').p.contents[2]
scale = item.find('div',class_='company-text').p.contents[-1]
mid2 = [location,experience,education,industry,stage,scale,company,detail_url]
result0.append(mid2)
return result0
#解析网页得到列表item[0:2],包括岗位,薪酬
def get_list1(list1):
result1 = []
for item in list1:
#最低最高岗位薪酬
salary_scale = item[1].replace('k','').split('-')
post = [item[0]]
post.extend(salary_scale)
result1.append(post)
return result1
#得到dataframe,input:文件,output:数据框
def get_data(file):
with open('/Users/tony/Desktop/招聘岗位数据分析/raw_data/'+file) as f:
text = f.read()
soup = BeautifulSoup(text,'lxml')
list0 = soup.find_all('div',class_ = 'job-primary')
list1 = [x.a.get_text().split('\n')[1:-2] for x in list0]
#得到两个list
item_info1 = get_list1(list1)
item_info0 = get_list0(list0)
#得到一个职位、薪酬、地点,工作经验,学历的pandas
dt1 = pd.DataFrame(data=item_info1,columns = ['post','salary_low','salary_high'])
dt0 = pd.DataFrame(data=item_info0,columns = ['location','experience','education','industry','stage','scale','company','detail_url'])
result = pd.concat([dt1,dt0],axis = 1)
return result
#得到目录下包含txt文件list
os.chdir('/Users/tony/Desktop/招聘岗位数据分析/raw_data/')
file_list = glob.glob('*.txt')
#得到某一岗位的所有信息的dataframe,input:文件名list,output:dataframe,csv
def get_dataframe(file_list):
data = []
for file in file_list:
mid = get_data(file)
data.append(mid)
frame = data
result = pd.concat(frame,ignore_index=True)
result.to_csv('/Users/tony/Desktop/招聘岗位数据分析/dt_boss.csv',header=True,index=False)
return result
以下是爬虫的结果和解析得到的结果
在得到Dataframe后,就可以对这些数据进行分析啦啦啦啦~
emm,也存在一些小问题,一不小心抓了将近2w条数据,要逐条得到详细的岗位信息,是相当漫长的过程,万恶的根源在于我这种爬虫小白,还是无法突破人家反爬的壁垒。但是这些数据已经足够进行进一步的分析啦。
此外,代码缺少通用性,这一点仍然需要改进,如果换了网页,那么又需要重新编写代码,emm,这是不是需要看一些scrapy框架来解决了呢==
补充一些中间遇到的小问题
1.刚开始用requests.get,直接报错,response 403;查询结果发现是user-agent有问题,随便找了一个常用的。
原因是默认参数不设置headers,会出现python字样,会被拒绝访问;user-agent用于隐藏自己爬虫的身份,从而访问网站。
2.在解析完页面后,想要传入文件名,如何获得某一目录下的所有文件名列表?
导入os模块,os.chdir()改需要的文件路径,os.listdir(),得到录下的所有文件,但会有一个额外文件,所以用glob.glob()来解决,获取*.txt所有文件。只负责匹配文件。
3.在浏览网页的过程中发现,鼠标移动到每条岗位信息时会出现悬浮框,如下:
而且检查元素发现:
但是下载该页面后并无法得到这部分信息。目前想到的解决办法是模拟浏览器登录,然后模拟一系列鼠标的行为;但是仔细想来这样的步骤从打开浏览器,获取每一页每一行的信息,其速度之慢不亚于获取该岗位的详细信息的url再获取岗位信息。于是介于数据获取量之大且缓慢,放弃了该部分数据的获取:包括公司介绍、岗位表述、工作要求、基本技能等。本来该部分数据将用于文本挖掘方面的分析,就此作罢。
下一篇,我会重点对这批数据进行分析,欲知后事如何,请看下回分解~