中国招聘网:https://www.shixi.com/search/index
爬取每一个职位的详细信息:职位名、学历、薪资、工作地点、公司领域、公司规模、公司名、工作描述、时间
工作描述是较为特殊的需要用到正则表达式获取
时间也较为特殊,在跳转前的页面中才有,跳转后的页面除了时间其他详细信息都有
跳转前:
点击链接跳转后:
需要用到的库
import requests#用requests获取网页
from lxml import etree#用xpath解析
import pandas as pd#整理数据
import re#处理较为特殊的详细信息
import urllib3#忽略警告
urllib3.disable_warnings()
中国招聘网的链接因为证书问题,需要用到urllib3的disable_warnings()忽略警告
采用三个for循环分别负责不同的功能:获取链接、获取大部分详细信息(除时间)、获取时间信息
获取链接:
因为大多详细信息在跳转后的页面中,因此需要获得每个职位跳转的链接
观察如下链接:
视觉设计实习生:https://www.shixi.com/personals/jobshow/82245
综合岗实习生:https://www.shixi.com/personals/jobshow/82252
再观察它们的a标签:
<a href="/personals/jobshow/82245" target="_blank"
class="job-name">视觉设计实习生</a>
<a href="/personals/jobshow/82252" target="_blank"
class="job-name">综合岗实习生</a>
得到规律:
https://www.shixi.com/ + href属性便可以得到所有跳转后的链接
爬取1000条数据不可能在第一页就能有1000条,因此观察页数的链接
第一页:https://www.shixi.com/search/index?page=1
第二页:https://www.shixi.com/search/index?page=2
得到规律:https://www.shixi.com/search/index?page= + 页数得到不同页数的链接
第一个for循环代码如下:
urls = pd.DataFrame()
for i in range(1,101):# 包头不包尾
url = 'https://www.shixi.com/search/index?page='+str(i)#得到页面的所有跳转链接
print("正在解析第%d条:"%i+url) # 方便报错后可查看出错的网页链接
rq = requests.get(url, verify=False)
dom = etree.HTML(rq.text, etree.HTMLParser())
lin = dom.xpath('//*[@id="select-form"]/div[2]/div/div/div[1]/dl/dt/a/@href')
link = ['https://www.shixi.com' + i for i in lin]
d = pd.DataFrame({'详情链接':link})
urls = pd.concat([urls, d])#pd.concat()使urls与d连接起来
urls.reset_index(inplace=True, drop=True)#重置索引
urls
接下来逐句解析每一条语句:
1.urls = pd.DataFrame我理解为创建一个空的表格赋值给urls
2.range(1,101)是包括1单不包括101的
3.通过for循环灵活的得到不同页数的链接
4.用print的格式化方便知道正在解析第几条
5.用get方法获取网页,因为链接有证书认证便用verify=False关闭了认证
6.用etree进行xpath的解析,我理解为解析为让xpath能看得懂的格式
7.dom.xpath()得到a标签的href属性,复制xpath的路径,随机进入一个岗位的详细页面中,复制想要的文字的xpath路径
8.[‘https://www.shixi.com’ + i for i in lin]通过for循环得到一个数组,装的是第几页的所有跳转后的链接
9.定义一个表格为d,放入标题为‘详情链接’,内容为link的数据
10.urls = pd.concat([urls, d])将d表格的内容和urls表格的内容进行合并,并赋给urls表格
11.通过reset_index()方法使得索引号从本来的无序变成有序的,爬取到的数据存在urls本是无序的,此方法的进行重新的排序并且覆盖掉之前的索引列
重置前:
重置后:
12.查看urls表格中的数据
第二个for循环:
获取大部分详细信息(除时间):
data = pd.DataFrame()
a = 0
for i in urls.loc[:,'详情链接']:
a += 1
print("正在解析第%d条:"%a+i)
li = []# 要把列表清空
rq = requests.get(i, verify=False) # 发送请求
delete = rq.text.replace('<br>', '').replace('\n', '').replace('\t', '').replace('\r', '')#删除掉文本中的\n、\t、\r和<br>
dom = etree.HTML(rq.text, etree.HTMLParser()) # 解析网页
job_name = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[1]/h2/text()') # 职位名
education = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[2]/span[1]/text()') # 学历
name = dom.xpath('/html/body/div/div[3]/div[2]/div/div[1]/h4/a/text()')#公司名
pos = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[1]/h2/span/text()')#工作地点
# 用正则表达式得到工作描述+任职要求
about = re.findall('<div class="work_b">([\s\S]*?)</div>',delete)
# 把列表about变成str再加进新列表li,强制不会报错arrays must all be same length
li.append(str(about).replace("[']",""))#删除字符串中的[']
scope = dom.xpath('/html/body/div/div[3]/div[2]/div/div[1]/p[1]/span/text()') # 领域
area = dom.xpath('/html/body/div/div[3]/div[2]/div/div[1]/p[2]/span/text()') # 规模
pay = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[2]/span[@class="money"]/text()') # 薪资
d = pd.DataFrame({'职位名': job_name,'学历':education,'薪资': pay,'工作地点': pos,'领域':scope,'规模':area,'公司名': name,'工作描述':li})
data = pd.concat([data, d])
data.reset_index(inplace=True, drop=True)
data
逐句解析:
1.定义一个表格存于data中
2.a=0为了能方便知道正在解析第几条,在全局定义了a变量,每次循环+=1
3.urls.loc[:,‘详情链接’]是以切片的方式获取想要解析的范围,切片的冒号前无数据便是从0开始,冒号后也无数据就是解析得到的所有链接,逗号后是想要解析的“数据仓库”
4.用print的格式化方便知道正在解析第几条
5.复制xpath的路径,为了避免其的特殊性应该多打开几个岗位详细页面复制相同的信息进行xpath路径对比
例如工资的xpath有些是在span[3]有些是span[4],因此找到工资的特有属性class="money"使其适合于所有页面
6.同理获得所有想要的数据信息(职位名, 公司名, 学历, 工作地址, 工资,公司领域,公司规模)并将其放入DataFrame中
7.执行pd.concat([data, d]) 使得数据都保存至data中
8.执行data.reset_index(inplace=True, drop=True)重置索引列
9.工作描述的相关解析:
# 工作描述的特殊性
li = []
#删除不需要的文本信息
delete=rq.text.replace('<br>','').replace('\n','').replace('\t', '').replace('\r', '')
#通过正则表达式获得文本信息
about=re.findall('<div class="work_b">([\s\S]*?)</div>',delete)
#将得到的数组about由str()变为字符串再加到空数组li中
li.append(str(about))
问题1:“工作描述”的xpath解析获得的列表长度是不一的,有些是len(li)为3有些len(li)为4,因此在运行是便会报错arrays must all be same length
解决1:将xpath解析后得到的长度不一的列表整体通过str()方法强制变为字符串类型,再在循环中定义一个空列表li,将得到的字符串由li.append加入到列表中,此时列表的长度就为1与其他的信息一样,解决长度不一的报错。li = 0为局部变量,每次循环都要清空其中的数据,已达到和xpath解析的数据相同的过程
问题2:通过xpath地址无法获得“工作描述”的所有文本信息,并且许多xpath都不具有普遍性,无法通过xpath解析得到所有页面的“工作描述”
解决2:通过正则表达式解决了普遍性的问题,所有的文本的标签和属性都是一样的,因此匹配其中的文字信息即可,但仍然有无法解决的问题是:也匹配到相同class的其他不需要的文本信息
问题3:通过xpath地址获得的文本有许多其他不需要的信息,例如/n、/t、/r等等
解决3:通过.replace(‘/n’,’ ’)挨个将无用的文本删除
第三个for循环:
获得时间:
cv = pd.DataFrame()
for i in range(1,101):# 包头不包尾
url = 'https://www.shixi.com/search/index?page='+str(i)#得到所有列表页面的链接
print("正在解析第%d条:"%i+url)#方便报错后可查看出错的网页链接
rq = requests.get(url, verify=False)
dom = etree.HTML(rq.text, etree.HTMLParser())
time = dom.xpath('//*[@id="select-form"]/div[2]/div/div/div[2]/div[1]/span[2]/text()')
all_time = [i for i in time]
t = pd.DataFrame({'时间':all_time})
cv = pd.concat([cv, t])
cv.reset_index(inplace=True, drop=True)
data = pd.concat([cv,data],axis=1)#axis=1是在左右合并,=0是上下合并
逐句解析:
1.定义一个新的空表格cv
2.与第一个for循环很相似,获取所有列表页面的链接
3.用print的格式化方便知道正在解析第几条
4.用get方法获取页面以及用etree解析页面
5.获取时间的xpath地址,获取的是时间的文本信息因此尾部有text()
6.用for循环获得一个列表页面中的所有时间的信息
7.创建表格t赋值标题为时间内容为所有爬取到的时间
8.pd.concat()合并cv和t表格
9.cv.reset_index()重新整理索引号
10.pd.concat([cv,data],axis=1)多了一个属性axis,当axis=1时两个表格为左右合并,axis=0时为上下合并
问题:在没加axis=1时运行会报错,得出的时间结果很多为None,无法合并两个DataFrame
解决:进行很少数据量的爬取实验,发现导出的Excel表格是属于上下合并,上网搜索DataFrame的合并方式,发现有属性axis=1时为左右合并,axis=0时为上下合并,于是加上属性便解决问题
最后保存至Excel中:
#保存到excel中
data.to_excel('中国热门职位.xls',index=None)
# index=None删除第一列的索引列
1."中国热门职位.xls"为名字,默认保存至工作目录下
2.index=None为删除索引列
删除前:
删除后:
所有代码:
import requests
from lxml import etree
import pandas as pd
import re
# 忽略警告
import urllib3
#获得链接
urllib3.disable_warnings()
urls = pd.DataFrame()
for i in range(1,101):# 包头不包尾
url = 'https://www.shixi.com/search/index?page='+str(i)#得到页面的所有跳转链接
print("正在解析第%d条:"%i+url) # 方便报错后可查看出错的网页链接
rq = requests.get(url, verify=False)
dom = etree.HTML(rq.text, etree.HTMLParser())
lin = dom.xpath('//*[@id="select-form"]/div[2]/div/div/div[1]/dl/dt/a/@href')
link = ['https://www.shixi.com' + i for i in lin]
d = pd.DataFrame({'详情链接':link})
urls = pd.concat([urls, d])#pd.concat()使urls与d连接起来
urls.reset_index(inplace=True, drop=True)#重置索引
urls
#获得除了时间
data = pd.DataFrame() # 存储数据
a = 0
for i in urls.loc[:,'详情链接']:
a += 1
print("正在解析第%d条:"%a+i)
# 要把列表清空
li = []
rq = requests.get(i, verify=False) # 发送请求
delete = rq.text.replace('<br>', '').replace('\n', '').replace('\t', '').replace('\r', '')#删除掉文本中的\n、\t、\r和<br>
dom = etree.HTML(rq.text, etree.HTMLParser()) # 解析网页
job_name = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[1]/h2/text()') # 职位名
education = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[2]/span[1]/text()') # 学历
name = dom.xpath('/html/body/div/div[3]/div[2]/div/div[1]/h4/a/text()')#公司名
pos = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[1]/h2/span/text()')#工作地点
# 用正则表达式得到工作描述+任职要求
about = re.findall('<div class="work_b">([\s\S]*?)</div>',delete)
# 把列表about变成str再加进新列表li,强制不会报错arrays must all be same length
li.append(str(about).replace("[']",""))#删除字符串中的[']
scope = dom.xpath('/html/body/div/div[3]/div[2]/div/div[1]/p[1]/span/text()') # 领域
area = dom.xpath('/html/body/div/div[3]/div[2]/div/div[1]/p[2]/span/text()') # 规模
pay = dom.xpath('/html/body/div/div[3]/div[1]/div[1]/div[2]/span[@class="money"]/text()') # 薪资
d = pd.DataFrame({'职位名': job_name,'学历':education,'薪资': pay,'工作地点': pos,'领域':scope,'规模':area,'公司名': name,'工作描述':li})
data = pd.concat([data, d])
data.reset_index(inplace=True, drop=True)
data
#获得时间
cv = pd.DataFrame()
for i in range(1,101):# 包头不包尾
url = 'https://www.shixi.com/search/index?page='+str(i)#得到页面的所有跳转链接
print("正在解析第%d条:"%i+url) # 方便报错后可查看出错的网页链接
rq = requests.get(url, verify=False)
dom = etree.HTML(rq.text, etree.HTMLParser())
time = dom.xpath('//*[@id="select-form"]/div[2]/div/div/div[2]/div[1]/span[2]/text()')
all_time = [i for i in time]
t = pd.DataFrame({'时间':all_time})
cv = pd.concat([cv, t])
cv.reset_index(inplace=True, drop=True)
data = pd.concat([cv,data],axis=1)#axis=1是在左右合并,=0是上下合并
#保存到excel中
data.to_excel('中国热门职位.xls',index=None)