0、前言

最近,博主面临着选方向的困难(唉,选择困难症患者 >﹏<),所以希望了解一下目前不同岗位的就业前景

这时,就不妨写个小爬虫,爬取一下 拉勾网 的职位数据,并用图形化的方法展示出来,一目了然

整体的 思路 是采用 selenium 模拟浏览器的行为,具体的步骤如下:

  1. 初始化
  2. 爬取数据,这里分为两个部分:一是爬取网页数据,二是进行翻页操作
  3. 保存数据,将数据保存到文件中
  4. 数据可视化

整体的 代码结构 如下:

class Lagou:
# 初始化
def init(self):
pass # 爬取网页数据
def parse_page(self):
pass # 进行翻页操作
def turn_page(self):
pass # 爬取数据,调用 parse_page 和 turn_page
def crawl(self):
pass # 保存数据,将数据保存到文件中
def save(self):
pass # 数据可视化
def draw(self):
pass if __name__ == '__main__':
obj = Lagou()
obj.init()
obj.crawl()
obj.save()
obj.draw()

好,下面我们一起来看一下整个爬虫过程的详细分析吧!!

1、初始化

在初始化的部分,我们完成的工作需要包括以下四个方面:

  1. 准备全局变量
  2. 启动浏览器
  3. 打开起始 URL
  4. 设置 cookie

(1)准备全局变量

所谓的全局变量,是指在整个爬虫过程中都需要用到的变量,这里我们定义两个全局变量:

  • data:储存爬取下来的数据
  • isEnd:判断爬取是否结束

(2)启动浏览器

启动浏览器的方式大致可以分为两种,一是普通启动,二是无头启动

在普通启动时,整个爬取过程可以可视化,方便调试的时候发现错误

from selenium import webdriver
self.browser = webdriver.Chrome()

而无头启动可以减少渲染时间,加快爬取过程,一般在正式爬取时使用

from selenium import webdriver
opt = webdriver.chrome.options.Options()
opt.set_headless()
self.browser = webdriver.Chrome(chrome_options = opt)

(3)打开起始 URL

首先,我们打开拉勾网的首页(URL:https://www.lagou.com/

在输入框中输入【python】进行搜索,可以发现网页跳转到如下的 URL:

https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=

然后,我们再次尝试在输入框中输入【爬虫】进行搜索,网页跳转到如下 URL:

https://www.lagou.com/jobs/list_爬虫?labelWords=&fromSearch=true&suginput=

从中,我们不难发现规律,对 URL 进行泛化后可以得到下面的结果(这个也就是我们的起始 URL):

https://www.lagou.com/jobs/list_{position}?labelWords=&fromSearch=true&suginput=

其中,参数 position 就是我们在输入框中输入的内容(需要进行 URL 编码)

(4)设置 cookie

由于拉勾网对未登录用户的访问数量做了限制,所以在浏览一定数量的网页后,网页会自动跳转到登陆界面:

这时,爬虫就不能正常工作了(当时博主就是在这个地方卡了好久,一直没找出原因)

为了解决上面的问题,我们可以使用 cookie 进行模拟登陆

方便起见,可以直接在浏览器中手动获取 cookie,然后将 cookie 信息添加到 browser 中

(5)初始化部分完整代码

# 初始化
def init(self):
# 准备全局变量
self.data = list()
self.isEnd = False
# 启动浏览器、初始化浏览器
opt = webdriver.chrome.options.Options()
opt.set_headless()
self.browser = webdriver.Chrome(chrome_options = opt)
self.wait = WebDriverWait(self.browser,10)
# 打开起始 URL
self.position = input('请输入职位:')
self.browser.get('https://www.lagou.com/jobs/list_' + urllib.parse.quote(self.position) + '?labelWords=&fromSearch=true&suginput=')
# 设置 cookie
cookie = input('请输入cookie:')
for item in cookie.split(';'):
k,v = item.strip().split('=')
self.browser.add_cookie({'name':k,'value':v})

2、爬取数据

在这一部分,我们需要完成以下的两个工作:

  1. 爬取网页数据
  2. 进行翻页操作

(1)爬取网页数据

在起始页面中,包含有我们需要的职位信息(可以使用 xpath 进行匹配):

  • 链接://a[@class="position_link"]

  • 职位://a[@class="position_link"]/h3

  • 城市://a[@class="position_link"]/span/em

  • 月薪、经验与学历://div[@class="p_bot"]/div[@class="li_b_l"]

  • 公司名称://div[@class="company_name"]/a

这里,我们需要使用 try - except - else 异常处理机制去处理异常,以保证程序的健壮性

(2)进行翻页操作

我们通过模拟点击【下一页】按钮,进行翻页操作

这里,我们同样需要使用 try - except - else 去处理异常

(3)爬取数据部分完整代码

# 爬取网页数据
def parse_page(self):
try:
# 链接
link = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//a[@class="position_link"]')))
link = [item.get_attribute('href') for item in link]
# 职位
position = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//a[@class="position_link"]/h3')))
position = [item.text for item in position]
# 城市
city = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//a[@class="position_link"]/span/em')))
city = [item.text for item in city]
# 月薪、经验与学历
ms_we_eb = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="p_bot"]/div[@class="li_b_l"]')))
monthly_salary = [item.text.split('/')[0].strip().split(' ')[0] for item in ms_we_eb]
working_experience = [item.text.split('/')[0].strip().split(' ')[1] for item in ms_we_eb]
educational_background = [item.text.split('/')[1].strip() for item in ms_we_eb]
# 公司名称
company_name = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="company_name"]/a')))
company_name = [item.text for item in company_name]
except TimeoutException:
self.isEnd = True
except StaleElementReferenceException:
time.sleep(3)
self.parse_page()
else:
temp = list(map(lambda a,b,c,d,e,f,g: {'link':a,'position':b,'city':c,'monthly_salary':d,'working_experience':e,'educational_background':f,'company_name':g}, link, position, city, monthly_salary, working_experience, educational_background, company_name))
self.data.extend(temp) # 进行翻页操作
def turn_page(self):
try:
pager_next = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'pager_next')))
except TimeoutException:
self.isEnd = True
else:
pager_next.click()
time.sleep(3) # 爬取数据,调用 parse_page 和 turn_page 方法
def crawl(self):
count = 0
while not self.isEnd :
count += 1
print('正在爬取第 ' + str(count) + ' 页 ...')
self.parse_page()
self.turn_page()
print('爬取结束')

3、保存数据

接下来,我们将数据储存到 JSON 文件中

# 将数据保存到文件中
def save(self):
with open('lagou.json','w',encoding='utf-8') as f:
for item in self.data:
json.dump(item,f,ensure_ascii=False)

这里,有两个需要注意的地方:

  • 在使用 open() 函数时,需要加上参数 encoding='utf-8'
  • 在使用 dump() 函数时,需要加上参数 ensure_ascii=False

4、数据可视化

数据可视化有利于更直观地展示数据之间的关系,根据爬取的数据,我们可以画出如下 4 个直方图:

  • 工作经验-职位数量
  • 工作经验-平均月薪
  • 学历-职位数量
  • 学历-平均月薪

这里,我们需要用到 matplotlib 库,需要注意一个中文编码的问题,可以使用以下的语句解决:

plt.rcParams['font.sans-serif'] = ['SimHei']

# 数据可视化
def draw(self):
count_we = {'经验不限':0,'经验应届毕业生':0,'经验1年以下':0,'经验1-3年':0,'经验3-5年':0,'经验5-10年':0}
total_we = {'经验不限':0,'经验应届毕业生':0,'经验1年以下':0,'经验1-3年':0,'经验3-5年':0,'经验5-10年':0}
count_eb = {'不限':0,'大专':0,'本科':0,'硕士':0,'博士':0}
total_eb = {'不限':0,'大专':0,'本科':0,'硕士':0,'博士':0}
for item in self.data:
count_we[item['working_experience']] += 1
count_eb[item['educational_background']] += 1
try:
li = [float(temp.replace('k','000')) for temp in item['monthly_salary'].split('-')]
total_we[item['working_experience']] += sum(li) / len(li)
total_eb[item['educational_background']] += sum(li) / len(li)
except:
count_we[item['working_experience']] -= 1
count_eb[item['educational_background']] -= 1
# 解决中文编码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
# 工作经验-职位数量
plt.title(self.position)
plt.xlabel('工作经验')
plt.ylabel('职位数量')
x = ['经验不限','经验应届毕业生','经验1-3年','经验3-5年','经验5-10年']
y = [count_we[item] for item in x]
plt.bar(x,y)
plt.show()
# 工作经验-平均月薪
plt.title(self.position)
plt.xlabel('工作经验')
plt.ylabel('平均月薪')
x = list()
y = list()
for item in ['经验不限','经验应届毕业生','经验1-3年','经验3-5年','经验5-10年']:
if count_we[item] != 0:
x.append(item)
y.append(total_we[item]/count_we[item])
plt.bar(x,y)
plt.show()
# 学历-职位数量
plt.title(self.position)
plt.xlabel('学历')
plt.ylabel('职位数量')
x = ['不限','大专','本科','硕士','博士']
y = [count_eb[item] for item in x]
plt.bar(x,y)
plt.show()
# 学历-平均月薪
plt.title(self.position)
plt.xlabel('学历')
plt.ylabel('平均月薪')
x = list()
y = list()
for item in ['不限','大专','本科','硕士','博士']:
if count_eb[item] != 0:
x.append(item)
y.append(total_eb[item]/count_eb[item])
plt.bar(x,y)
plt.show()

5、大功告成

(1)完整代码

至此,整个爬虫过程已经分析完毕,完整的代码如下:

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.common.exceptions import StaleElementReferenceException
import urllib.parse
import time
import json
import matplotlib.pyplot as plt class Lagou:
# 初始化
def init(self):
self.data = list()
self.isEnd = False
opt = webdriver.chrome.options.Options()
opt.set_headless()
self.browser = webdriver.Chrome(chrome_options = opt)
self.wait = WebDriverWait(self.browser,10)
self.position = input('请输入职位:')
self.browser.get('https://www.lagou.com/jobs/list_' + urllib.parse.quote(self.position) + '?labelWords=&fromSearch=true&suginput=')
cookie = input('请输入cookie:')
for item in cookie.split(';'):
k,v = item.strip().split('=')
self.browser.add_cookie({'name':k,'value':v}) # 爬取网页数据
def parse_page(self):
try:
link = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//a[@class="position_link"]')))
link = [item.get_attribute('href') for item in link]
position = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//a[@class="position_link"]/h3')))
position = [item.text for item in position]
city = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//a[@class="position_link"]/span/em')))
city = [item.text for item in city]
ms_we_eb = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="p_bot"]/div[@class="li_b_l"]')))
monthly_salary = [item.text.split('/')[0].strip().split(' ')[0] for item in ms_we_eb]
working_experience = [item.text.split('/')[0].strip().split(' ')[1] for item in ms_we_eb]
educational_background = [item.text.split('/')[1].strip() for item in ms_we_eb]
company_name = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="company_name"]/a')))
company_name = [item.text for item in company_name]
except TimeoutException:
self.isEnd = True
except StaleElementReferenceException:
time.sleep(3)
self.parse_page()
else:
temp = list(map(lambda a,b,c,d,e,f,g: {'link':a,'position':b,'city':c,'monthly_salary':d,'working_experience':e,'educational_background':f,'company_name':g}, link, position, city, monthly_salary, working_experience, educational_background, company_name))
self.data.extend(temp) # 进行翻页操作
def turn_page(self):
try:
pager_next = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'pager_next')))
except TimeoutException:
self.isEnd = True
else:
pager_next.click()
time.sleep(3) # 爬取数据
def crawl(self):
count = 0
while not self.isEnd :
count += 1
print('正在爬取第 ' + str(count) + ' 页 ...')
self.parse_page()
self.turn_page()
print('爬取结束') # 保存数据
def save(self):
with open('lagou.json','w',encoding='utf-8') as f:
for item in self.data:
json.dump(item,f,ensure_ascii=False) # 数据可视化
def draw(self):
count_we = {'经验不限':0,'经验应届毕业生':0,'经验1年以下':0,'经验1-3年':0,'经验3-5年':0,'经验5-10年':0}
total_we = {'经验不限':0,'经验应届毕业生':0,'经验1年以下':0,'经验1-3年':0,'经验3-5年':0,'经验5-10年':0}
count_eb = {'不限':0,'大专':0,'本科':0,'硕士':0,'博士':0}
total_eb = {'不限':0,'大专':0,'本科':0,'硕士':0,'博士':0}
for item in self.data:
count_we[item['working_experience']] += 1
count_eb[item['educational_background']] += 1
try:
li = [float(temp.replace('k','000')) for temp in item['monthly_salary'].split('-')]
total_we[item['working_experience']] += sum(li) / len(li)
total_eb[item['educational_background']] += sum(li) / len(li)
except:
count_we[item['working_experience']] -= 1
count_eb[item['educational_background']] -= 1
# 解决中文编码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
# 工作经验-职位数量
plt.title(self.position)
plt.xlabel('工作经验')
plt.ylabel('职位数量')
x = ['经验不限','经验应届毕业生','经验1-3年','经验3-5年','经验5-10年']
y = [count_we[item] for item in x]
plt.bar(x,y)
plt.show()
# 工作经验-平均月薪
plt.title(self.position)
plt.xlabel('工作经验')
plt.ylabel('平均月薪')
x = list()
y = list()
for item in ['经验不限','经验应届毕业生','经验1-3年','经验3-5年','经验5-10年']:
if count_we[item] != 0:
x.append(item)
y.append(total_we[item]/count_we[item])
plt.bar(x,y)
plt.show()
# 学历-职位数量
plt.title(self.position)
plt.xlabel('学历')
plt.ylabel('职位数量')
x = ['不限','大专','本科','硕士','博士']
y = [count_eb[item] for item in x]
plt.bar(x,y)
plt.show()
# 学历-平均月薪
plt.title(self.position)
plt.xlabel('学历')
plt.ylabel('平均月薪')
x = list()
y = list()
for item in ['不限','大专','本科','硕士','博士']:
if count_eb[item] != 0:
x.append(item)
y.append(total_eb[item]/count_eb[item])
plt.bar(x,y)
plt.show() if __name__ == '__main__':
obj = Lagou()
obj.init()
obj.crawl()
obj.save()
obj.draw()

(2)运行过程

下面,我们一起来运行代码看看!

在运行代码时,程序会要求输入【职位】和【cookie】,其中,cookie 的获取方法如下:

进入 拉勾网首页,并登陆

使用快捷键 Ctrl+Shift+IF12 打开开发者工具

在输入框中输入【职位(这里的示例为 python)】进行搜索,抓包分析,可以看到 cookie 信息就包含在其中

完整的运行过程如下:

(3)运行结果

注意:本项目代码仅作学习交流使用!!!

爬虫实战(三) 用Python爬取拉勾网的更多相关文章

  1. 爬虫实战(二) 用Python爬取网易云歌单

    最近,博主喜欢上了听歌,但是又苦于找不到好音乐,于是就打算到网易云的歌单中逛逛 本着 "用技术改变生活" 的想法,于是便想着写一个爬虫爬取网易云的歌单,并按播放量自动进行排序 这篇 ...

  2. 爬虫实战(一) 用Python爬取百度百科

    最近博主遇到这样一个需求:当用户输入一个词语时,返回这个词语的解释 我的第一个想法是做一个数据库,把常用的词语和词语的解释放到数据库里面,当用户查询时直接读取数据库结果 但是自己又没有心思做这样一个数 ...

  3. Python3爬虫:(一)爬取拉勾网公司列表

    人生苦短,我用Python 爬取原因:了解一下Python工程师在北上广等大中城市的薪资水平与入职前要求. Python3基础知识 requests,pyquery,openpyxl库的使用 爬取前的 ...

  4. Python爬虫学习三------requests+BeautifulSoup爬取简单网页

    第一次第一次用MarkDown来写博客,先试试效果吧! 昨天2018俄罗斯世界杯拉开了大幕,作为一个伪球迷,当然也得为世界杯做出一点贡献啦. 于是今天就编写了一个爬虫程序将腾讯新闻下世界杯专题的相关新 ...

  5. python爬取拉勾网数据并进行数据可视化

    爬取拉勾网关于python职位相关的数据信息,并将爬取的数据已csv各式存入文件,然后对csv文件相关字段的数据进行清洗,并对数据可视化展示,包括柱状图展示.直方图展示.词云展示等并根据可视化的数据做 ...

  6. 【Python爬虫案例】用Python爬取李子柒B站视频数据

    一.视频数据结果 今天是2021.12.7号,前几天用python爬取了李子柒的油管评论并做了数据分析,可移步至: https://www.cnblogs.com/mashukui/p/1622025 ...

  7. Python网络爬虫第三弹《爬取get请求的页面数据》

    一.urllib库 urllib是Python自带的一个用于爬虫的库,其主要作用就是可以通过代码模拟浏览器发送请求.其常被用到的子模块在Python3中的为urllib.request和urllib. ...

  8. (转)python爬取拉勾网信息

    学习Python也有一段时间了,各种理论知识大体上也算略知一二了,今天就进入实战演练:通过Python来编写一个拉勾网薪资调查的小爬虫. 第一步:分析网站的请求过程 我们在查看拉勾网上的招聘信息的时候 ...

  9. python爬取拉勾网职位数据

    今天写的这篇文章是关于python爬虫简单的一个使用,选取的爬取对象是著名的招聘网站--拉钩网,由于和大家的职业息息相关,所以爬取拉钩的数据进行分析,对于职业规划和求职时的信息提供有很大的帮助. 完成 ...

随机推荐

  1. YTU 2632: B2 友元光顾

    2632: B2 友元光顾 时间限制: 1 Sec  内存限制: 128 MB 提交: 378  解决: 241 题目描述 定义一个平面上的点类Point,其中设置成员函数distance1求当前对象 ...

  2. Tensorflow学习笔记——占位符和feed_dict(二)

    创建了各种形式的常量和变量后,但TensorFlow 同样还支持占位符.占位符并没有初始值,它只会分配必要的内存.在会话中,占位符可以使用 feed_dict 馈送数据. feed_dict是一个字典 ...

  3. python中如何理解if __name__ == '__main__'

    __name__ 是当前模块名,当模块被直接运行时模块名为 __main__ .这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行.我们通过一个简单的例子来理解 ...

  4. [Codeforces 986E] Prince's Problem

    [题目链接] https://codeforces.com/contest/986/problem/E [算法] X到Y的路径积 , 可以转化为X到根的路径积乘Y到根的路径积 , 除以LCA到根的路径 ...

  5. TI BLE : GAP Bond Manager

    // Setup the GAP Bond Manager { uint32 passkey = 0; // passkey "000000" uint8 pairMode = G ...

  6. Linux 系统管理命令 - lsof - 查看进程打开的文件

    命令详解 重要星级: ★★★★★ 功能说明: 全名为 list open files,也就是列举系统中已经被打开的文件,通过 lsof 命令,就可以根据文件找到对应的进程信息,也可以根据进程信息找到进 ...

  7. bzoj 1642: [Usaco2007 Nov]Milking Time 挤奶时间【dp】

    这不就是个n方dp吗--看了眼洛谷题解简直神仙打架 我全程没用到n-- 把休息时间并入产奶时间,注意"结束时间不挤奶",所以ei=ei+r-1,注意这个-1! 然后按r排序,设f[ ...

  8. [NOI2004]cashier 郁闷的出纳员

    Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常 ...

  9. 二分搜索 POJ 2456 Aggressive cows

    题目传送门 /* 二分搜索:搜索安排最近牛的距离不小于d */ #include <cstdio> #include <algorithm> #include <cmat ...

  10. ACM_下一个排列

    The Next Permutation Time Limit: 2000/1000ms (Java/Others) Problem Description: For this problem, yo ...