基于爬虫技术爬取拉勾网招聘信息分析软件开发行业走向

一、选题背景

       近年来,越来越多的大学生选择计算机行业作为自己的研究方向,但是大学生们对该行业所需要的技 术,以及一线、二线城市的薪资水准等可能不清楚。因此,我们通过爬取拉钩网,获取软件开发行业的 相关技术栈以及薪资标准,为大学生的就业以及方向选择进行导向。

二、设计方案

1.爬虫名称:拉勾网招聘信息自动化爬取

2.爬取内容: 公司名称、薪资标准、学历要求、所在城市

3.方案概述:

(1)首先,人工访问目标站点,通过F12查看,当前网页的请求,找到包含招聘信息的js链接,这个链 接是我们真正需要请求的目标地址。

(2)之后,通过爬虫爬取目标站点的内容,此时获取到的是原始数据内容

(3)对原始数据进行,数据清洗,获得到自己所需要的爬取内容

(4)数据分析

(5)绘制词云

4.技术难点:

(1)存在反爬机制,需要自己查找真正的请求地址

(2)需要设置请求头的相关信息。

(3)由于该站点div嵌套过多,所以针对数据清洗的时候,需要熟练掌握xpath相关语法才能迅速定位内容信息。

三、主题页面的结构特征分析

1.主题页面结构与特征分析

       通过F12我们查看目标站点的响应信息,发现positionAjax.json中包含我们的响应信息,通过该响 应,可以看到请求链接,此链接是我们真正的请求连接。

 2.Htmls 页面分析

通过F12,我们查看目标站点。通过点击console中左上角的选中按钮,我们选中包含我们需要信息的 div标签。通过控制台,我们可以发现,每一个工作信息,都存放在一个div中,class的名称为item__10RTO

 

3. 节点标签查找方法和遍历方法

     我们依次选中我们所需要的信息,查看所在的节点,然后通过xpath进行提取。

(1)爬取内容1: 公司名称

 通过F12,我们可以看到公司名称是在根div标签下,class=company-name__2-SjF下的a标签中。因此 遍历方法为:

1 company_list = html.xpath(
2 '//div[@class="content-left__31-
3 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
4 '//div[@class="itemtop__1Z3Zo"]//div[@class="company__2EsC8"]//div[@class="company-name__2-SjF"]'
5 '//a/text()'
6 )

(2)爬取内容2:薪资标准

 通过F12,我们可以看到公司名称是在根div标签下,span 标签中 class="money__3Lkgq"下的a标签中。 因此遍历方法为:

salary_list = html.xpath(
'//div[@class="content-left__31-
g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
'//div[2]//span[@class="money__3Lkgq"]/text()'
)

  (3)爬取内容3:学历要求

 通过F12,我们可以看到公司名称是在根div标签下,div标签中class=p-bom__JlNur的文本信息中。但 是,这时候获取到的数据信息还包含经验信息,因此需要做处理。 因此遍历方法为:

1 degree_raw_list = html.xpath(
2 '//div[@class="content-left__31-
3 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
4 '//div[@class="p-bom__JlNur"]/text()'
5 )

(4)爬取内容4:技术要求

 通过F12,我们可以看到公司名称是在根div标签下,div标签中class=ir___QwEG下的所有span标签的文 本信息中。但是,这时候获取到的数据信息还包含经验信息,因此需要做处理。 因此遍历方法为:

1 technology_list = html.xpath(
2 '//div[@class="content-left__31-
3 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
4 '//div[@class="itembom__cTJhu"]//div[@class="ir___QwEG"]//span/text()'
5 )

四、网络爬虫程序设计

1.数据爬取与采集

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : crawlsite.py
 5 @Project : coursedesign
 6 @Time : 2021/12/30 19:57
 7 @Author : TheNorth
 8 @Function :
 9 """
10 from config import Config
11 import requests
12 from tools.log import logger
13 from lxml import etree
14 from service.datastore import DataStore
15 class CrawlSite(object):
16 """
17 爬取拉钩网的核心代码
18 """
19 def __init__(self):
20 """
21 爬虫的相关参数的初始化
22 :param
23 """
24 self.headers = Config.REQUEST_HEADERS
25 self.baseurl = Config.BASE_URL
26 self.logger = logger
27 self.data_store = DataStore()
28 self.city = ""
29 self.world_cloud_content = ""
30 def crawlTarget(self, query_content: str, page_num: int, city: str):
31 """
32 :param query_content: 要查询的内容
33 :param page_num: 查询的页数
34 :param city: 查询的城市
35 :return:
36 """
37 target_url = f"{self.baseurl}?px=new&pn={page_num}&fromSearch=true&kd=
38 {query_content}&city={city}"
39 try:
40 self.logger.info(f"开始请求{target_url}")
41 resp = requests.get(url=target_url, headers=self.headers, timeout=3)
42 self.city = city
43 if resp.status_code != 200:
44 self.logger.warning(f"{target_url}目标状态码错误,状态码为
45 {resp.status_code}")
46 else:
47 self.logger.info(f"目标地址请求成功")
48 self.logger.info(f"开始解析")
49 result_list = self.parse_html(resp.text)
50 self.logger.info(f"数据持久化")
51 self.data_store.createTable(table_name=query_content)
52 self.data_store.insertdata(result_list)
53 except Exception as error:
54 self.logger.error(f"爬虫爬取目标站点报错{error}")

2.数据清洗

 1 def parse_html(self, html_content) -> list:
 2 """
 3 使用xpath 解析html语法树
 4 :param html_content:
 5 :return 返回解析列表
 6 """
 7 try:
 8 html = etree.HTML(html_content)
 9 company_list = html.xpath(
10 '//div[@class="content-left__31-
11 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
12 '//div[@class="itemtop__1Z3Zo"]//div[@class="company__2EsC8"]//div[@class="company-name__2-SjF"]'
13 '//a/text()'
14 )
15 if not company_list:
16 self.logger.warning("全部页数解析完毕")
17 return []
18 salary_list = html.xpath(
19 '//div[@class="content-left__31-
20 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
21 '//div[2]//span[@class="money__3Lkgq"]/text()'
22 )
23 degree_raw_list = html.xpath(
24 '//div[@class="content-left__31-
25 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
26 '//div[@class="p-bom__JlNur"]/text()'
27 )
28 technology_list = html.xpath(
29 '//div[@class="content-left__31-
30 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
31 '//div[@class="itembom__cTJhu"]//div[@class="ir___QwEG"]//span/text()'
32 )
33 for technology in technology_list:
34 self.world_cloud_content += technology + " "degree_list = []
35 for degree_raw in degree_raw_list:
36 degree = degree_raw.split("/")[-1]
37 degree_list.append(degree)
38 result_list = []
39 for index in range(len(company_list)):
40 result_dict = {}
41 result_dict.update({"company": company_list[index].strip(' ')})
42 result_dict.update({"salary": salary_list[index].strip(' ')})
43 result_dict.update({"degree": degree_list[index].strip(' ')})
44 result_dict.update({"city": self.city.strip(' ')})
45 result_list.append(result_dict)
46 self.logger.info(company_list)
47 self.logger.info(salary_list)
48 self.logger.info(degree_list)
49 self.logger.info(result_list)
50 return result_list
51 except Exception as error:
52 self.logger.error(f"解析html文件报错,错误信息{error}")
53 return []

 数据爬虫和数据清洗是在一块写的,所以,放一张截图

3.文本分析

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : worldcloudgene.py
 5 @Project : coursedesign
 6 @Time : 2021/12/30 23:35
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import wordcloud
11 class WorldCloudGene(object):
12 """
13 生成词云
14 """
15 def generateWorldCloud(self, world_cloud_content: str, png_name: str):
16 w = wordcloud.WordCloud(width=1000, height=700,
17 background_color='white', font_path='msyh.ttc')
18 # 调用词云对象的generate方法,将文本传入
19 w.generate(world_cloud_content)
20 # 将生成的词云保存为output2-poem.png图片文件,保存到当前文件夹中
21 w.to_file(f'{png_name}.png')

使用词云对文本进行分析

3.python的技术分析

4.数据分析和可视化

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : dataanalyze.py
 5 @Project : coursedesign
 6 @Time : 2021/12/31 0:05
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import matplotlib.pyplot as plt
11 from service.datastore import DataStore
12 from tools.log import logger
13 class DataAnalyze(object):
14 """
15 数据图表分析
16 """
17 def __init__(self):
18 pass
19 def analyzeDegree(self):
20 datadao = DataStore()
21 try:
22 city_list = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
23 labels = '大专', '本科', '硕士'
24 label_data = [0, 0, 0]
25 for city in city_list:degree_city_list = datadao.searchdata(query_column='degree',
26 table_name='python', city_name=city)
27 for degree in degree_city_list:
28 if degree[0] == "大专":
29 label_data[0] += 1
30 elif degree[0] == "本科":
31 label_data[1] += 1
32 elif degree[0] == "硕士":
33 label_data[2] += 1
34 print(label_data)
35 percent_list = []
36 sum_label = label_data[0] + label_data[1] + label_data[2]
37 for lable_num in label_data:
38 percent_list.append((lable_num / sum_label) * 100)
39 plt.rcParams['font.sans-serif'] = ['FangSong']
40 explode = [0.1, 0.1, 0.1]
41 plt.axes(aspect=1)
42 plt.pie(x=percent_list, labels=labels, autopct='%.0f%%',
43 explode=explode, shadow=True)
44 plt.show()
45 except Exception as error:
46 logger.error(f"查询学位字段出现错误{error}")

通过饼状图,可视化的分析了,软件开发行业目前针对学历的需求,绝大多数还是要求本科以上的。

 

 1 class DataAnalyze(object):
 2 """
 3 数据图表分析
 4 """
 5 def __init__(self):
 6 pass
 7 def analyzeSalary(self):
 8 """
 9 :return:
10 """
11 datadao = DataStore()
12 city_list = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
13 aver_salary_list = []
14 for city in city_list:
15 salary_list = datadao.searchdata(query_column='salary',
16 city_name=city, table_name="python")
17 tmp_salary = 0
18 if len(salary_list) == 0:
19 logger.info(city)
20 for salary in salary_list:
21 salary_str = salary[0].replace('k', '')
22 tmp_salary_list = salary_str.split('-')
23 tmp_salary += int(int(tmp_salary_list[0]) +
24 int(tmp_salary_list[1])) // 2aver_salary_list.append(tmp_salary // len(salary_list))
25 x = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
26 y = aver_salary_list
27 plt.rcParams['font.sans-serif'] = ['FangSong']
28 plt.title('一、二线城市python开发薪资水平', fontsize=20, color='black') #
29 title默认不支持中文 写的话会出现乱码
30 plt.xlabel('城市名称', fontsize=20, color='black')
31 plt.ylabel('薪资水平', fontsize=20, color='black')
32 """
33 只要是设置字体那么都可以使用
34 fontsize
35 color
36 """
37 plt.plot(x, y)
38 plt.show()

 通过折线图分析了一 二线python开发的薪资水准

5.回归方程建立

 

 由于python开发的薪资水平,取决于该城市所在位置,以及互联网发达水平,无法通过单一元素建立回 归方程,所以在这边未建立回归方程。

6.数据持久化

 使用sqlite数据库作为数据持久化的数据库。

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : datastore.py
 5 @Project : coursedesign
 6 @Time : 2021/12/30 22:57
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import sqlite3
11 from tools.log import logger
12 class DataStore(object):
13 """
14 数据持久化,使用sqlite数据库
15 """
16 def __init__(self):
17 # 可以指定创建数据库的路径,比如可以写成sqlite3.connect(r"E:\DEMO.db")
18 try:
19 self.logger = logger
20 self.con =
21 sqlite3.connect("E:\\pycharmproject\\coursedesign\\jobresult.db")
22 self.cur = self.con.cursor()
23 except Exception as error:
24 self.logger.error(f"连接数据库出现错误,错误信息为{error}")
25 def createTable(self, table_name: str):
26 """
27 创建相关的数据库表名
28 :param table_name: 数据表的表名
29 :return:
30 """
31 try:
32 sql = f"CREATE TABLE IF NOT EXISTS {table_name}(id INTEGER PRIMARY
33 KEY AUTOINCREMENT,company VARCHAR(255)," \
34 f"salary VARCHAR(255), degree VARCHAR(255), city VARCHAR(255)
35 )"
36 self.cur.execute(sql)
37 self.table_name = table_name
38 self.logger.info("创建数据表成功")
39 except Exception as error:
40 self.logger.warning("创建表出现错误")
41 def insertdata(self, result_list: list):
42 """插入数据到数据表中
43 :param result_list:
44 :return:
45 """
46 for result_dict in result_list:
47 company = result_dict.get("company", " ")
48 salary = result_dict.get("salary", " ")
49 degree = result_dict.get("degree", " ")
50 city = result_dict.get("city", " ")
51 try:
52 self.cur.execute(f"INSERT INTO {self.table_name}
53 values(?,?,?,?,?)",
54 (None, company, salary, degree, city))
55 except Exception as error:
56 self.logger.warning(f"插入数据报错,报错信息{error}")
57 self.con.commit()
58 def searchdata(self, query_column: str, table_name: str, city_name: str =
59 None) -> list:
60 """
61 查询数据库的薪资数据
62 :param query_column: 查询的字段
63 :param city_name: 查询的城市名称
64 :param table_name: 查询的表名
65 :return:
66 """
67 try:
68 self.cur.execute(f"select {query_column} from {table_name} where
69 city='{city_name}'")res_list = self.cur.fetchall()
70 return res_list
71 # print(type(res), res)
72 except Exception as error:
73 self.logger.error(f"查询数据库出现错误,错误信息为{error}")
74 return []

 

 7.完整代码

(1)目录结构

 核心代码都在service中

(2)完整代码

crawlsite.py 爬取网站代码

  1 # !/usr/bin/env python3
  2 # _*_ coding:utf-8 _*_
  3 """
  4 @File : crawlsite.py
  5 @Project : coursedesign
  6 @Time : 2021/12/13 19:57
  7 @Author : TheNorth
  8 @Function :
  9 """
 10 from config import Config
 11 import requests
 12 from tools.log import logger
 13 from lxml import etree
 14 from service.datastore import DataStore
 15 class CrawlSite(object):
 16 """
 17 爬取拉钩网的核心代码
 18 """def __init__(self):
 19 """
 20 爬虫的相关参数的初始化
 21 :param
 22 """
 23 self.headers = Config.REQUEST_HEADERS
 24 self.baseurl = Config.BASE_URL
 25 self.logger = logger
 26 self.data_store = DataStore()
 27 self.city = ""
 28 self.world_cloud_content = ""
 29 def crawlTarget(self, query_content: str, page_num: int, city: str):
 30 """
 31 :param query_content: 要查询的内容
 32 :param page_num: 查询的页数
 33 :param city: 查询的城市
 34 :return:
 35 """
 36 target_url = f"{self.baseurl}?px=new&pn={page_num}&fromSearch=true&kd=
 37 {query_content}&city={city}"
 38 try:
 39 self.logger.info(f"开始请求{target_url}")
 40 resp = requests.get(url=target_url, headers=self.headers, timeout=3)
 41 self.city = city
 42 if resp.status_code != 200:
 43 self.logger.warning(f"{target_url}目标状态码错误,状态码为
 44 {resp.status_code}")
 45 else:
 46 self.logger.info(f"目标地址请求成功")
 47 self.logger.info(f"开始解析")
 48 result_list = self.parse_html(resp.text)
 49 self.logger.info(f"数据持久化")
 50 self.data_store.createTable(table_name=query_content)
 51 self.data_store.insertdata(result_list)
 52 except Exception as error:
 53 self.logger.error(f"爬虫爬取目标站点报错{error}")
 54 def parse_html(self, html_content) -> list:
 55 """
 56 使用xpath 解析html语法树
 57 :param html_content:
 58 :return 返回解析列表
 59 """
 60 try:
 61 html = etree.HTML(html_content)
 62 company_list = html.xpath(
 63 '//div[@class="content-left__31-
 64 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 65 '//div[@class="itemtop__1Z3Zo"]//div[@class="company__2EsC8"]//div[@class="company-name__2-SjF"]'
 66 '//a/text()'
 67 )
 68 if not company_list:
 69 self.logger.warning("全部页数解析完毕")
 70 return []salary_list = html.xpath(
 71 '//div[@class="content-left__31-
 72 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 73 '//div[2]//span[@class="money__3Lkgq"]/text()'
 74 )
 75 degree_raw_list = html.xpath(
 76 '//div[@class="content-left__31-
 77 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 78 '//div[@class="p-bom__JlNur"]/text()'
 79 )
 80 technology_list = html.xpath(
 81 '//div[@class="content-left__31-
 82 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 83 '//div[@class="itembom__cTJhu"]//div[@class="ir___QwEG"]//span/text()'
 84 )
 85 for technology in technology_list:
 86 self.world_cloud_content += technology + " "
 87 degree_list = []
 88 for degree_raw in degree_raw_list:
 89 degree = degree_raw.split("/")[-1]
 90 degree_list.append(degree)
 91 result_list = []
 92 for index in range(len(company_list)):
 93 result_dict = {}
 94 result_dict.update({"company": company_list[index].strip(' ')})
 95 result_dict.update({"salary": salary_list[index].strip(' ')})
 96 result_dict.update({"degree": degree_list[index].strip(' ')})
 97 result_dict.update({"city": self.city.strip(' ')})
 98 result_list.append(result_dict)
 99 self.logger.info(company_list)
100 self.logger.info(salary_list)
101 self.logger.info(degree_list)
102 self.logger.info(result_list)
103 return result_list
104 except Exception as error:
105 self.logger.error(f"解析html文件报错,错误信息{error}")
106 return []
107 if __name__ == '__main__':
108 spider = CrawlSite()
109 spider.crawlTarget(query_content="java开发", page_num=2, city="北京")

dataanalyze.py 数据分析代码

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : dataanalyze.py
 5 @Project : coursedesign
 6 @Time : 2021/12/15 0:05
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import matplotlib.pyplot as plt
11 from service.datastore import DataStore
12 from tools.log import logger
13 class DataAnalyze(object):
14 """
15 数据图表分析
16 """
17 def __init__(self):
18 pass
19 def analyzeSalary(self):
20 """
21 :return:
22 """
23 datadao = DataStore()
24 city_list = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
25 aver_salary_list = []
26 for city in city_list:
27 salary_list = datadao.searchdata(query_column='salary',
28 city_name=city, table_name="python")
29 tmp_salary = 0
30 if len(salary_list) == 0:
31 logger.info(city)
32 for salary in salary_list:
33 salary_str = salary[0].replace('k', '')
34 tmp_salary_list = salary_str.split('-')
35 tmp_salary += int(int(tmp_salary_list[0]) +
36 int(tmp_salary_list[1])) // 2
37 aver_salary_list.append(tmp_salary // len(salary_list))
38 x = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
39 y = aver_salary_list
40 plt.rcParams['font.sans-serif'] = ['FangSong']
41 plt.title('一、二线城市python开发薪资水平', fontsize=20, color='black') #
42 title默认不支持中文 写的话会出现乱码plt.xlabel('城市名称', fontsize=20, color='black')
43 plt.ylabel('薪资水平', fontsize=20, color='black')
44 """
45 只要是设置字体那么都可以使用
46 fontsize
47 color
48 """
49 plt.plot(x, y)
50 plt.show()
51 def analyzeDegree(self):
52 datadao = DataStore()
53 try:
54 city_list = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
55 labels = '大专', '本科', '硕士'
56 label_data = [0, 0, 0]
57 for city in city_list:
58 degree_city_list = datadao.searchdata(query_column='degree',
59 table_name='python', city_name=city)
60 for degree in degree_city_list:
61 if degree[0] == "大专":
62 label_data[0] += 1
63 elif degree[0] == "本科":
64 label_data[1] += 1
65 elif degree[0] == "硕士":
66 label_data[2] += 1
67 print(label_data)
68 percent_list = []
69 sum_label = label_data[0] + label_data[1] + label_data[2]
70 for lable_num in label_data:
71 percent_list.append((lable_num / sum_label) * 100)
72 plt.rcParams['font.sans-serif'] = ['FangSong']
73 explode = [0.1, 0.1, 0.1]
74 plt.axes(aspect=1)
75 plt.pie(x=percent_list, labels=labels, autopct='%.0f%%',
76 explode=explode, shadow=True)
77 plt.show()
78 except Exception as error:
79 logger.error(f"查询学位字段出现错误{error}")if __name__ == '__main__':
80 dataanlyze = DataAnalyze()
81 dataanlyze.analyzeDegree()

datastore.py文件 数据持久化代码

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : datastore.py
 5 @Project : coursedesign
 6 @Time : 2021/12/16 22:57
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import sqlite3
11 from tools.log import logger
12 class DataStore(object):
13 """
14 数据持久化,使用sqlite数据库
15 """
16 def __init__(self):# 可以指定创建数据库的路径,比如可以写成sqlite3.connect(r"E:\DEMO.db")
17 try:
18 self.logger = logger
19 self.con =
20 sqlite3.connect("E:\\pycharmproject\\coursedesign\\jobresult.db")
21 self.cur = self.con.cursor()
22 except Exception as error:
23 self.logger.error(f"连接数据库出现错误,错误信息为{error}")
24 def createTable(self, table_name: str):
25 """
26 创建相关的数据库表名
27 :param table_name: 数据表的表名
28 :return:
29 """
30 try:
31 sql = f"CREATE TABLE IF NOT EXISTS {table_name}(id INTEGER PRIMARY
32 KEY AUTOINCREMENT,company VARCHAR(255)," \
33 f"salary VARCHAR(255), degree VARCHAR(255), city VARCHAR(255)
34 )"self.cur.execute(sql)
35 self.table_name = table_name
36 self.logger.info("创建数据表成功")
37 except Exception as error:
38 self.logger.warning("创建表出现错误")
39 def insertdata(self, result_list: list):
40 """
41 插入数据到数据表中
42 :param result_list:
43 :return:
44 """
45 for result_dict in result_list:
46 company = result_dict.get("company", " ")
47 salary = result_dict.get("salary", " ")
48 degree = result_dict.get("degree", " ")
49 city = result_dict.get("city", " ")
50 try:
51 self.cur.execute(f"INSERT INTO {self.table_name}
52 values(?,?,?,?,?)",
53 (None, company, salary, degree, city))
54 except Exception as error:
55 self.logger.warning(f"插入数据报错,报错信息{error}")
56 self.con.commit()
57 def searchdata(self, query_column: str, table_name: str, city_name: str =
58 None) -> list:
59 """
60 查询数据库的薪资数据
61 :param query_column: 查询的字段
62 :param city_name: 查询的城市名称
63 :param table_name: 查询的表名
64 :return:
65 """
66 try:
67 self.cur.execute(f"select {query_column} from {table_name} where
68 city='{city_name}'")
69 res_list = self.cur.fetchall()
70 return res_list
71 # print(type(res), res)
72 except Exception as error:
73 self.logger.error(f"查询数据库出现错误,错误信息为{error}")
74 return []
75 if __name__ == '__main__':
76 test = DataStore()
77 test.createTable("java")
78 test.searchdata(query_column='salary', city_name="北京", table_name="java")

worldcloudgene.py 词云生成代码

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : worldcloudgene.py
 5 @Project : coursedesign
 6 @Time : 2021/12/23 23:35
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import wordcloud
11 class WorldCloudGene(object):
12 """
13 生成词云
14 """
15 def generateWorldCloud(self, world_cloud_content: str, png_name: str):
16 w = wordcloud.WordCloud(width=1000, height=700,
17 background_color='white', font_path='msyh.ttc')
18 # 调用词云对象的generate方法,将文本传入
19 w.generate(world_cloud_content)
20 # 将生成的词云保存为output2-poem.png图片文件,保存到当前文件夹中
21 w.to_file(f'{png_name}.png')

config.py 配置文件

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : config.py
 5 @Project : coursedesign
 6 @Time : 2021/12/30 19:51
 7 @Author : TheNorth
 8 @Function :
 9 """
10 class Config(object):
11 """
12 爬虫的相关配置的定义
13 """
14 BASE_URL = "https://www.lagou.com/wn/jobs"
15 REQUEST_HEADERS = {
16 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0)
17 Gecko/20100101 Firefox/34.0',
18 'Accept': '*/*',
19 'Connection': 'keep-alive',
20 'Accept-Language': 'zh-CN,zh;q=0.8',
21 "Content-Type": "application/json"
22 }

log.py 日志文件

 1 # !/usr/bin/env python3
 2 # _*_ coding:utf-8 _*_
 3 """
 4 @File : log.py
 5 @Project : coursedesign
 6 @Time : 2021/12/30 20:17
 7 @Author : TheNorth
 8 @Function :
 9 """
10 import sys
11 import pathlib
12 from loguru import logger
13 # 路径设置
14 relative_directory = pathlib.Path(__file__).parent.parent # 代码相对路径
15 result_save_dir = relative_directory.joinpath('results') # 结果保存目录
16 log_path = result_save_dir.joinpath('crawlsite.log') # 爬虫爬取日志保存路径
17 # 日志配置
18 # 终端日志输出格式
19 stdout_fmt = '<cyan>{time:HH:mm:ss,SSS}</cyan> ' \
20 '[<level>{level: <5}</level>] ' \
21 '<blue>{module}</blue>:<cyan>{line}</cyan> - ' \
22 '<level>{message}</level>'
23 # 日志文件记录格式
24 logfile_fmt = '<light-green>{time:YYYY-MM-DD HH:mm:ss,SSS}</light-green> ' \
25 '[<level>{level: <5}</level>] ' \
26 '<cyan>{process.name}({process.id})</cyan>:' \
27 '<cyan>{thread.name: <18}({thread.id: <5})</cyan> | ' \
28 '<blue>{module}</blue>.<blue>{function}</blue>:' \
29 '<blue>{line}</blue> - <level>{message}</level>'
30 logger.remove()
31 logger.level(name='TRACE', color='<cyan><bold>', icon='')
32 logger.level(name='DEBUG', color='<blue><bold>', icon='🐞 ')
33 logger.level(name='INFOR', no=20, color='<green><bold>', icon='')
34 logger.level(name='QUITE', no=25, color='<green><bold>', icon='🤫 ')
35 logger.level(name='ALERT', no=30, color='<yellow><bold>', icon='')
36 logger.level(name='ERROR', color='<red><bold>', icon='')
37 logger.level(name='FATAL', no=50, color='<RED><bold>', icon='')
38 # 如果你想在命令终端静默运行datacollect,可以将以下一行中的level设置为QUITE
39 # 命令终端日志级别默认为INFOR
40 logger.add(sys.stderr, level='INFOR', format=stdout_fmt, enqueue=True)
41 # 日志文件默认为级别为DEBUG
42 logger.add(log_path, level='DEBUG', format=logfile_fmt, enqueue=True,
43 encoding='utf-8')

所有代码

  1 # !/usr/bin/env python3
  2 # _*_ coding:utf-8 _*_
  3 """
  4 @File : crawlsite.py
  5 @Project : coursedesign
  6 @Time : 2021/12/13 19:57
  7 @Author : TheNorth
  8 @Function :
  9 """
 10 from config import Config
 11 import requests
 12 from tools.log import logger
 13 from lxml import etree
 14 from service.datastore import DataStore
 15 class CrawlSite(object):
 16 """
 17 爬取拉钩网的核心代码
 18 """
 19 def __init__(self):
 20 """
 21 爬虫的相关参数的初始化
 22 :param
 23 """
 24 self.headers = Config.REQUEST_HEADERS
 25 self.baseurl = Config.BASE_URL
 26 self.logger = logger
 27 self.data_store = DataStore()
 28 self.city = ""
 29 self.world_cloud_content = ""
 30 def crawlTarget(self, query_content: str, page_num: int, city: str):
 31 """
 32 :param query_content: 要查询的内容
 33 :param page_num: 查询的页数
 34 :param city: 查询的城市
 35 :return:
 36 """
 37 target_url = f"{self.baseurl}?px=new&pn={page_num}&fromSearch=true&kd=
 38 {query_content}&city={city}"
 39 try:
 40 self.logger.info(f"开始请求{target_url}")
 41 resp = requests.get(url=target_url, headers=self.headers, timeout=3)
 42 self.city = city
 43 if resp.status_code != 200:
 44 self.logger.warning(f"{target_url}目标状态码错误,状态码为
 45 {resp.status_code}")
 46 else:
 47 self.logger.info(f"目标地址请求成功")
 48 self.logger.info(f"开始解析")
 49 result_list = self.parse_html(resp.text)
 50 self.logger.info(f"数据持久化")
 51 self.data_store.createTable(table_name=query_content)
 52 self.data_store.insertdata(result_list)
 53 except Exception as error:
 54 self.logger.error(f"爬虫爬取目标站点报错{error}")
 55 def parse_html(self, html_content) -> list:
 56 """
 57 使用xpath 解析html语法树
 58 :param html_content:
 59 :return 返回解析列表
 60 """
 61 try:
 62 html = etree.HTML(html_content)
 63 company_list = html.xpath(
 64 '//div[@class="content-left__31-
 65 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 66 '//div[@class="itemtop__1Z3Zo"]//div[@class="company__2EsC8"]//div[@class="company-name__2-SjF"]'
 67 '//a/text()'
 68 )
 69 if not company_list:
 70 self.logger.warning("全部页数解析完毕")
 71 return []
 72 salary_list = html.xpath(
 73 '//div[@class="content-left__31-
 74 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 75 '//div[2]//span[@class="money__3Lkgq"]/text()'
 76 )
 77 degree_raw_list = html.xpath(
 78 '//div[@class="content-left__31-
 79 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 80 '//div[@class="p-bom__JlNur"]/text()'
 81 )
 82 technology_list = html.xpath(
 83 '//div[@class="content-left__31-
 84 g5"]//div[@class="list__YibNq"]//div[@class="item__10RTO"]'
 85 '//div[@class="itembom__cTJhu"]//div[@class="ir___QwEG"]//span/text()'
 86 )
 87 for technology in technology_list:
 88 self.world_cloud_content += technology + " "
 89 degree_list = []
 90 for degree_raw in degree_raw_list:
 91 degree = degree_raw.split("/")[-1]
 92 degree_list.append(degree)
 93 result_list = []
 94 for index in range(len(company_list)):
 95 result_dict = {}
 96 result_dict.update({"company": company_list[index].strip(' ')})
 97 result_dict.update({"salary": salary_list[index].strip(' ')})
 98 result_dict.update({"degree": degree_list[index].strip(' ')})
 99 result_dict.update({"city": self.city.strip(' ')})
100 result_list.append(result_dict)self.logger.info(company_list)
101 self.logger.info(salary_list)
102 self.logger.info(degree_list)
103 self.logger.info(result_list)
104 return result_list
105 except Exception as error:
106 self.logger.error(f"解析html文件报错,错误信息{error}")
107 return []
108 if __name__ == '__main__':
109 spider = CrawlSite()
110 spider.crawlTarget(query_content="java开发", page_num=2, city="北京")# !/usr/bin/env python3
111 # _*_ coding:utf-8 _*_
112 """
113 @File : dataanalyze.py
114 @Project : coursedesign
115 @Time : 2021/12/15 0:05
116 @Author : TheNorth
117 @Function :
118 """
119 import matplotlib.pyplot as plt
120 from service.datastore import DataStore
121 from tools.log import logger
122 class DataAnalyze(object):
123 """
124 数据图表分析
125 """
126 def __init__(self):
127 pass
128 def analyzeSalary(self):
129 """
130 :return:
131 """
132 datadao = DataStore()
133 city_list = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
134 aver_salary_list = []
135 for city in city_list:
136 salary_list = datadao.searchdata(query_column='salary',
137 city_name=city, table_name="python")
138 tmp_salary = 0
139 if len(salary_list) == 0:
140 logger.info(city)
141 for salary in salary_list:salary_str = salary[0].replace('k', '')
142 tmp_salary_list = salary_str.split('-')
143 tmp_salary += int(int(tmp_salary_list[0]) +
144 int(tmp_salary_list[1])) // 2
145 aver_salary_list.append(tmp_salary // len(salary_list))
146 x = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
147 y = aver_salary_list
148 plt.rcParams['font.sans-serif'] = ['FangSong']
149 plt.title('一、二线城市python开发薪资水平', fontsize=20, color='black') #
150 title默认不支持中文 写的话会出现乱码
151 plt.xlabel('城市名称', fontsize=20, color='black')
152 plt.ylabel('薪资水平', fontsize=20, color='black')
153 """
154 只要是设置字体那么都可以使用
155 fontsize
156 color
157 """
158 plt.plot(x, y)
159 plt.show()
160 def analyzeDegree(self):
161 datadao = DataStore()
162 try:
163 city_list = ["北京", "上海", "深圳", "合肥", "武汉", "南京"]
164 labels = '大专', '本科', '硕士'
165 label_data = [0, 0, 0]
166 for city in city_list:
167 degree_city_list = datadao.searchdata(query_column='degree',
168 table_name='python', city_name=city)
169 for degree in degree_city_list:
170 if degree[0] == "大专":
171 label_data[0] += 1
172 elif degree[0] == "本科":
173 label_data[1] += 1
174 elif degree[0] == "硕士":
175 label_data[2] += 1
176 print(label_data)
177 percent_list = []
178 sum_label = label_data[0] + label_data[1] + label_data[2]
179 for lable_num in label_data:
180 percent_list.append((lable_num / sum_label) * 100)
181 plt.rcParams['font.sans-serif'] = ['FangSong']
182 explode = [0.1, 0.1, 0.1]
183 plt.axes(aspect=1)
184 plt.pie(x=percent_list, labels=labels, autopct='%.0f%%',
185 explode=explode, shadow=True)
186 plt.show()
187 except Exception as error:
188 logger.error(f"查询学位字段出现错误{error}")
189 if __name__ == '__main__':
190 dataanlyze = DataAnalyze()
191 dataanlyze.analyzeDegree()
192 # !/usr/bin/env python3
193 # _*_ coding:utf-8 _*_
194 """
195 @File : datastore.py
196 @Project : coursedesign
197 @Time : 2021/12/16 22:57
198 @Author : TheNorth
199 @Function :
200 """
201 import sqlite3
202 from tools.log import logger
203 class DataStore(object):
204 """
205 数据持久化,使用sqlite数据库
206 """
207 def __init__(self):
208 # 可以指定创建数据库的路径,比如可以写成sqlite3.connect(r"E:\DEMO.db")
209 try:
210 self.logger = logger
211 self.con =
212 sqlite3.connect("E:\\pycharmproject\\coursedesign\\jobresult.db")
213 self.cur = self.con.cursor()
214 except Exception as error:
215 self.logger.error(f"连接数据库出现错误,错误信息为{error}")
216 def createTable(self, table_name: str):
217 """
218 创建相关的数据库表名
219 :param table_name: 数据表的表名
220 :return:
221 """
222 try:
223 sql = f"CREATE TABLE IF NOT EXISTS {table_name}(id INTEGER PRIMARY
224 KEY AUTOINCREMENT,company VARCHAR(255)," \
225 f"salary VARCHAR(255), degree VARCHAR(255), city VARCHAR(255)
226 )"
227 self.cur.execute(sql)
228 self.table_name = table_name
229 self.logger.info("创建数据表成功")
230 except Exception as error:
231 self.logger.warning("创建表出现错误")
232 def insertdata(self, result_list: list):
233 """
234 插入数据到数据表中
235 :param result_list:
236 :return:
237 """
238 for result_dict in result_list:
239 company = result_dict.get("company", " ")
240 salary = result_dict.get("salary", " ")
241 degree = result_dict.get("degree", " ")
242 city = result_dict.get("city", " ")
243 try:
244 self.cur.execute(f"INSERT INTO {self.table_name}
245 values(?,?,?,?,?)",
246 (None, company, salary, degree, city))
247 except Exception as error:
248 self.logger.warning(f"插入数据报错,报错信息{error}")
249 self.con.commit()
250 def searchdata(self, query_column: str, table_name: str, city_name: str =
251 None) -> list:
252 """
253 查询数据库的薪资数据
254 :param query_column: 查询的字段
255 :param city_name: 查询的城市名称
256 :param table_name: 查询的表名:
257 :return:
258 """
259 try:
260 self.cur.execute(f"select {query_column} from {table_name} where
261 city='{city_name}'")
262 res_list = self.cur.fetchall()
263 return res_list
264 # print(type(res), res)
265 except Exception as error:
266 self.logger.error(f"查询数据库出现错误,错误信息为{error}")
267 return []
268 if __name__ == '__main__':
269 test = DataStore()
270 test.createTable("java")
271 test.searchdata(query_column='salary', city_name="北京", table_name="java")
272 # !/usr/bin/env python3
273 # _*_ coding:utf-8 _*_
274 """
275 @File : worldcloudgene.py
276 @Project : coursedesign
277 @Time : 2021/12/23 23:35
278 @Author : TheNorth
279 @Function :
280 """
281 import wordcloud
282 class WorldCloudGene(object):
283 """
284 生成词云
285 """
286 def generateWorldCloud(self, world_cloud_content: str, png_name: str):
287 w = wordcloud.WordCloud(width=1000, height=700,
288 background_color='white', font_path='msyh.ttc')
289 # 调用词云对象的generate方法,将文本传入
290 w.generate(world_cloud_content)
291 # 将生成的词云保存为output2-poem.png图片文件,保存到当前文件夹中
292 w.to_file(f'{png_name}.png')
293 # !/usr/bin/env python3
294 # _*_ coding:utf-8 _*_
295 """
296 @File : config.py
297 @Project : coursedesign
298 @Time : 2021/12/30 19:51
299 @Author : TheNorth
300 @Function :
301 """
302 class Config(object):
303 """
304 爬虫的相关配置的定义
305 """
306 BASE_URL = "https://www.lagou.com/wn/jobs"
307 REQUEST_HEADERS = {
308 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0)
309 Gecko/20100101 Firefox/34.0',
310 'Accept': '*/*',
311 'Connection': 'keep-alive',
312 'Accept-Language': 'zh-CN,zh;q=0.8',
313 "Content-Type": "application/json"
314 }
315 # !/usr/bin/env python3
316 # _*_ coding:utf-8 _*_
317 """
318 @File : log.py
319 @Project : coursedesign
320 @Time : 2021/12/30 20:17
321 @Author : TheNorth
322 @Function :
323 """
324 import sys
325 import pathlib
326 from loguru import logger
327 # 路径设置
328 relative_directory = pathlib.Path(__file__).parent.parent # 代码相对路径
329 result_save_dir = relative_directory.joinpath('results') # 结果保存目录
330 log_path = result_save_dir.joinpath('crawlsite.log') # 爬虫爬取日志保存路径
331 # 日志配置
332 # 终端日志输出格式
333 stdout_fmt = '<cyan>{time:HH:mm:ss,SSS}</cyan> ' \
334 '[<level>{level: <5}</level>] ' \
335 '<blue>{module}</blue>:<cyan>{line}</cyan> - ' \
336 '<level>{message}</level>'
337 # 日志文件记录格式
338 logfile_fmt = '<light-green>{time:YYYY-MM-DD HH:mm:ss,SSS}</light-green> ' \
339 '[<level>{level: <5}</level>] ' \
340 '<cyan>{process.name}({process.id})</cyan>:' \
341 '<cyan>{thread.name: <18}({thread.id: <5})</cyan> | ' \
342 '<blue>{module}</blue>.<blue>{function}</blue>:' \
343 '<blue>{line}</blue> - <level>{message}</level>'
344 logger.remove()
345 logger.level(name='TRACE', color='<cyan><bold>', icon='')
346 logger.level(name='DEBUG', color='<blue><bold>', icon='🐞 ')
347 logger.level(name='INFOR', no=20, color='<green><bold>', icon='')
348 logger.level(name='QUITE', no=25, color='<green><bold>', icon='🤫 ')
349 logger.level(name='ALERT', no=30, color='<yellow><bold>', icon='')
350 logger.level(name='ERROR', color='<red><bold>', icon='')
351 logger.level(name='FATAL', no=50, color='<RED><bold>', icon='')
352 # 如果你想在命令终端静默运行datacollect,可以将以下一行中的level设置为QUITE
353 # 命令终端日志级别默认为INFOR
354 logger.add(sys.stderr, level='INFOR', format=stdout_fmt, enqueue=True)
355 # 日志文件默认为级别为DEBUG
356 logger.add(log_path, level='DEBUG', format=logfile_fmt, enqueue=True,
357 encoding='utf-8')

 

 

五、总结

1.经过本次针对拉勾网的数据分析。我们发现以下结论:

(1)软件开发对开发者的学历还是有要求的,90%的公司都要求开发者学历在本科以上。

(2)通过词云分析,我们发现无论那种语言都可以应用在后台开发,都涉及到数据库。数据库的学习在 软件开发行业有着很重要的作用。 (3)通过分析一、二线薪资水准。我们发现一二线计算机行业的薪资水准差距还是蛮大的。大城市消费 高,对应的薪资标准也高,小城市消费低,互联网发展慢,薪资水准也较低。

2. 在本次课程设计的收获以及要改进的建议:

通过本次课程设计巩固了python的基础知识,同时对python爬虫有了更深一步的理解。希望以后的 课程能够也像这次这样将现实中的案例结合到实际课程中。

 

posted @ 2021-12-31 10:35  “Luats”  阅读(264)  评论(0编辑  收藏  举报