1.引言
** 本项目由GitHub项目改进,建议直接拉取到 2.2相关模块 开始阅读 **
1.1项目背景
临近毕业,毕业生都是在手机APP和电脑网站查询自己相应的招聘岗位,在这种情况下查看招聘岗位不但费时,而且费眼睛,还不能将已经查看过的数据进行可视化统计,所以想知道招聘岗位的具体情况很是麻烦。
1.2项目意义
此项目完成之后将大大节约我们查找招聘岗位的时间,它的重大意义是让我们查看工作岗位信息数据进行了数据化、规范化、自动化、可视化管理。它可以帮助我们了解行业的薪资分布、城市岗位分布、岗位要求关键字、岗位经验要求等等一系列的数据。
1.3实现功能
项目需要实现:①可由用户选择爬取哪种或哪些岗位的招聘信息、②可由用户选择爬取招聘信息的城市(全国可选)、③将爬取的数据存储到mysql或者csv文件中、④将mysql或csv文件中的数据取出并以字典的形式存在、⑤将数据进行可视化、⑥将可视化的数据进行分类。
2.系统结构
2.1相关技术
2.1.1系统开发语言:python
本系统采用的语言是python。
Python是一种跨平台的计算机程序设计语言。 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。
Python特点:
3.1 简单:Python是一种代表简单主义思想的语言。阅读一个良好的Python程序就感觉像是在读英语一样。它使你能够专注于解决问题而不是去搞明白语言本身。
3.2 易学:Python极其容易上手,因为Python有极其简单的说明文档。
3.3 速度快:Python 的底层是用 C语言写的,很多标准库和第三方库也都是用 C 写的,运行速度非常快。
3.4 免费、开源:Python是FLOSS(自由/开放源码软件)之一。使用者可以自由地发布这个软件的拷贝、阅读它的源代码、对它做改动、把它的一部分用于新的自由软件中。FLOSS是基于一个团体分享知识的概念。
3.5 高层语言:用Python语言编写程序的时候无需考虑诸如如何管理你的程序使用的内存一类的底层细节。
3.6 可移植性:由于它的开源本质,Python已经被移植在许多平台上(经过改动使它能够工作在不同平台上)。
3.7 解释性:一个用编译性语言比如C或C++写的程序可以从源文件(即C或C++语言)转换到一个你的计算机使用的语言(二进制代码,即0和1)。这个过程通过编译器和不同的标记、选项完成。
3.8 运行程序的时候,连接/转载器软件把你的程序从硬盘复制到内存中并且运行。而Python语言写的程序不需要编译成二进制代码。你可以直接从源代码运行 程序。
3.9 在计算机内部,Python解释器把源代码转换成称为字节码的中间形式,然后再把它翻译成计算机使用的机器语言并运行。这使得使用Python更加简单。也使得Python程序更加易于移植。
3.10 面向对象:Python既支持面向过程的编程也支持面向对象的编程。在“面向过程”的语言中,程序是由过程或仅仅是可重用代码的函数构建起来的。在“面向对象”的语言中,程序是由数据和功能组合而成的对象构建起来的。
3.11 可扩展性:如果需要一段关键代码运行得更快或者希望某些算法不公开,可以部分程序用C或C++编写,然后在Python程序中使用它们。
3.12 可嵌入性:可以把Python嵌入C/C++程序,从而向程序用户提供脚本功能。
3.13 丰富的库:Python标准库确实很庞大。它可以帮助处理各种工作,包括正则表达式、文档生成、单元测试、线程、数据库、网页浏览器、CGI、FTP、电子邮件、XML、XML-RPC、HTML、WAV文件、密码系统、GUI(图形用户界面)、Tk和其他与系统有关的操作。这被称作Python的“功能齐全”理念。除了标准库以外,还有许多其他高质量的库,如wxPython、Twisted和Python图像库等等。
3.14 规范的代码:Python采用强制缩进的方式使得代码具有较好可读性。而Python语言写的程序不需要编译成二进制代码。
2.1.2 MySQL数据库
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
MySQL的特点有很多:比如可移植性强、支持多种操作系统等等。
2.1.3 Pycharm开发工具
PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外,该IDE提供了一些高级功能,以用于支持Django框架下的专业Web开发。
特点:PyCharm拥有一般IDE具备的功能,比如, 调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制
2.1.4 cookie技术
Cookie 并不是它的原意“甜饼”的意思, 而是一个保存在客户机中的简单的文本文件, 这个文件与特定的 Web 文档关联在一起, 保存了该客户机访问这个Web 文档时的信息, 当客户机再次访问这个 Web 文档时这些信息可供该文档使用。由于“Cookie”具有可以保存在客户机上的神奇特性, 因此它可以帮助我们实现记录用户个人信息的功能, 而这一切都不必使用复杂的CGI等程序。
2.1.5 其他技术
CSS、HTML5、JS。
2.2相关模块
2.2.1 configures模块
configures模块主要用于存放配置文件以及cookie文件等。
2.2.2 lagou_data模块
lagou_data模块主要是存放csv文件,csv文件是将爬取的数据进行一个保存。
2.2.3 report模块
report模块主要是实现可视化功能,里面涉及一些html5、css、js等一系列文件。
2.2.4 utils模块
utils模块主要是存放一些基础类以及数据库类
2.2.5主模块
主模块主要是存放爬虫的类以及可视化类和依赖库的文本
图1系统模块结构
2.2.6数据库模块
2.2.6.1数据库代码
SET NAMES utf8mb4
;
SET FOREIGN_KEY_CHECKS
= 0;
DROP TABLE IF EXISTS `java
`;
CREATE TABLE `java
` (
`id
` int(11) NOT NULL AUTO_INCREMENT,
`jobId
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NOT NULL,
`jobUrl
` text CHARACTER SET utf8
COLLATE utf8_general_ci
NOT NULL,
`jobName
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`salary
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`experience
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`education
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`jobType
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`city
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`address
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`advantage
` text CHARACTER SET utf8
COLLATE utf8_general_ci
NULL,
`requirement
` text CHARACTER SET utf8
COLLATE utf8_general_ci
NULL,
`company_name
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_nickname
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_field
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_investment
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_scale
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_size
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_link
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`source
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
PRIMARY KEY (`id
`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8
COLLATE = utf8_general_ci ROW_FORMAT
= Dynamic
;
DROP TABLE IF EXISTS `python
`;
CREATE TABLE `python
` (
`id
` int(11) NOT NULL AUTO_INCREMENT,
`jobId
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NOT NULL,
`jobUrl
` text CHARACTER SET utf8
COLLATE utf8_general_ci
NOT NULL,
`jobName
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`salary
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`experience
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`education
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`jobType
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`city
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`address
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`advantage
` text CHARACTER SET utf8
COLLATE utf8_general_ci
NULL,
`requirement
` text CHARACTER SET utf8
COLLATE utf8_general_ci
NULL,
`company_name
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_nickname
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_field
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_investment
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_scale
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_size
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`company_link
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
`source
` varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci
NULL DEFAULT NULL,
PRIMARY KEY (`id
`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8
COLLATE = utf8_general_ci ROW_FORMAT
= Dynamic
;
SET FOREIGN_KEY_CHECKS
= 1;
2.2.6.2数据库表设计
字段名数据类型长度是否可null备注
idint11否Id主键jobIdvarchar255否岗位idjobUrltext0否岗位url(自动存储长度为0)jobNamevarchar255是岗位名称salaryvarchar255是薪资experiencevarchar255是工作经验educationvarchar255是学历要求jobTypevarchar255是工作性质cityvarchar255是城市addressvarchar255是公司地址advantagetext0是福利(优势)(自动存储长度为0)requirementtext0是招聘要求(自动存储长度为0)company_namevarchar255是公司全称company_nicknamevarchar255是公司别称company_fieldvarchar255是公司涉及领域company_investmentvarchar255是公司融资情况company_scalevarchar255是公司阶段company_sizevarchar255是公司规模company_linkvarchar255是公司链接sourcevarchar255是来源
3.相关代码
因涉及到原作者的代码,所以这里只展示我本人修改过的部分代码,由于篇幅和排版问题,本人修改过的其他代码将在给出,其他代码请移步到GitHub上面进行获取,GitHub链接将在本文最后给出,谢谢理解!
3.1 configures.yml
mysql_db
:
db_host
: '127.0.0.1'
db_port
: 3306
db_username
: 'root'
db_password
: '123456'
lagou
:
keyword
: [python
, java
]
username
: 13680320641
password
: q904881067
sleep_interval
: 1
save_method
: mysql_db
save_file_path
: lagou_data
visualize
:
keyword
: [java
, python
]
save_method
: mysql_db
3.2 base_helpers.py
COMMON_CITIES
= ['深圳', '广州']
3.3 mysql_helpers.py
import MySQLdb
import MySQLdb
.cursors
class MySQLHelper(object):
def __init__(self
, database
, db_username
='root', db_password
='123456', db_host
='127.0.0.1', db_port
=3306):
self
.database
= database
self
.db_username
= db_username
self
.db_password
= db_password
self
.db_host
= db_host
self
.db_port
= db_port
self
.conn
= self
.connect_db
()
print("connection successfully to MySQL")
def connect_db(self
):
conn
= MySQLdb
.connect
(host
=self
.db_host
, user
=self
.db_username
, passwd
=self
.db_password
,
database
=self
.database
, charset
="utf8")
return conn
def connect_close(self
):
self
.conn
.close
()
def insert_one(self
, data
, keyword
):
cursor
= self
.conn
.cursor
()
keys
= ','.join
(data
.keys
())
value_str
= '\',\''.join
(data
.values
())
values
= '\'' + value_str
+ '\''
db_sql
= f
"INSERT INTO {keyword} ({keys}) VALUES ({values})"
try:
cursor
.execute
(db_sql
)
self
.conn
.commit
()
except Exception
as error
:
self
.conn
.rollback
()
return error
def find_one(self
, keyword
, data_id
):
cursor
= self
.conn
.cursor
()
db_sql
= "SELECT * FROM {0} WHERE JobId = {1};".format(keyword
, data_id
)
cursor
.execute
(db_sql
)
library
= cursor
.fetchall
()
cursor
.close
()
return library
def find_all(self
, keyword
):
cursor
= self
.conn
.cursor
(cursorclass
=MySQLdb
.cursors
.DictCursor
)
db_sql
= "SELECT * FROM {0};".format(keyword
)
cursor
.execute
(db_sql
)
libraries
= cursor
.fetchall
()
cursor
.close
()
return libraries
3.4 visualize_data.py
3.4.1 read_data_to_frame
def read_data_to_frame(self
, keyword
, save_method
):
"""获取数据并转传成 DataFrame"""
if save_method
== 'file':
for key
in keyword
:
if key
:
base_dir
= os
.path
.dirname
(__file__
)
data_files
= [os
.path
.join
(base_dir
, 'lagou_data', f
'{key}.csv')]
else:
data_files
= glob
.glob
(os
.path
.join
('lagou_data', '*.csv'))
for data_file
in data_files
:
with open(data_file
, 'r', encoding
='utf-8') as f
:
reader
= csv
.DictReader
(f
)
for row
in reader
:
self
.data
.append
(dict(row
))
if save_method
== 'mysql_db':
mysql_db
= MySQLHelper
(database
='pythonworkdb')
for key
in keyword
:
data
= mysql_db
.find_all
(key
)
for row
in data
:
data_dict
= dict(row
)
del data_dict
['id']
self
.data
.append
(data_dict
)
self
.frame
= DataFrame
(self
.data
)
3.5 spiders.py
3.5.1 main
if __name__
== '__main__':
try:
configure_file
= sys
.argv
[1]
except:
configure_file
= 'configures/configure.yml'
configures
= yaml_configure_parser
(configure_file
)
mongo_db_configures
= configures
.get
('mongo_db', {})
mysql_db_configures
= configures
.get
('mysql_db', {})
webdriver_option_configures
= configures
.get
('webdriver_option', {})
lagou_configures
= configures
.get
('lagou', {})
if mongo_db_configures
.get
('host', ''):
mongo_db
= MonGoDBHelper
(database
='jobs', **mongo_db_configures
)
else:
mongo_db
= None
if mysql_db_configures
.get
('db_host', ''):
mysql_db
= MySQLHelper
(database
='pythonworkdb')
else:
mysql_db
= None
lagou_spider
= LaGouSpider
(proxy_server
=webdriver_option_configures
['proxy_server'],
headless
=webdriver_option_configures
['headless'],
executable_path
=webdriver_option_configures
['executable_path'],
sleep_interval
=lagou_configures
['sleep_interval'],
save_method
=lagou_configures
['save_method'],
save_file_path
=lagou_configures
['save_file_path'],
mysql_db
=mysql_db
,
)
lagou_spider
.run
(username
=lagou_configures
['username'], password
=lagou_configures
['password'],
keyword
=lagou_configures
['keyword'])
3.5.2 run
def run(self
, username
, password
, keyword
=[]):
"""运行入口"""
self
.login
(username
, password
)
common_cities
= COMMON_CITIES
all_cities
= ALL_CITIES
logger
.info
(f
'获取常用城市:{common_cities}')
logger
.info
(f
'获取全部城市:{all_cities}')
time
.sleep
(self
.sleep_interval
)
for key
in keyword
:
if key
:
categories
= [key
]
logger
.info
(f
'获取key:{keyword}')
logger
.info
(f
'获取key:{key}')
else:
categories
= CATEGORIES
logger
.info
(f
'获取全部搜索分类:{categories}')
for category
in categories
:
for common_city
in common_cities
:
districts
= self
.get_districts
(common_city
)
logger
.info
(f
'获取城市:{common_city}的行政区域:{districts}')
while True:
running
= self
.main
(keyword
=category
, city
=common_city
)
if running
:
break
else:
continue
for district
in districts
:
while True:
running
= self
.main
(keyword
=category
, city
=common_city
, district
=district
)
if running
:
break
else:
continue
self
.mysql_db
.connect_close
()
3.5.3 save_to_mysqldb
数据库中
def save_to_mysqldb(self
, keyword
, data_id
, url
):
saved_data
= self
.mysql_db
.find_one
(keyword
=keyword
, data_id
=data_id
)
if saved_data
:
logger
.warning
(f
'ID为:{data_id}的数据已经保存在MySqlDB, 不再保存!')
else:
data
= self
.parse_details
(url
)
error
= self
.mysql_db
.insert_one
(data
, keyword
)
if error
is not None:
logger
.error
(error
)
logger
.info
(f
'保存数据:{data_id}在MySqlDB')
4.实验结果
图2 即将开始爬取数据
图3数据库数据317条
图4开始爬取数据并保存数据
图5数据库数据321条
图6根据不同地区爬取数据
图7爬取不同城市的数据
图8爬取另一个关键字(岗位)数据
图9 程序正常结束,数据爬取完成
图10 数据可视化(实验报告每次只爬取一页数据,所以数据比较少)
图11数据可视化2
图12数据可视化3
5.总结与展望
通过这次实验,可以说把整个学期的课程知识都串联在了一起又重温了一遍,本次实验项目由于原作者是在Linux操作系统下完成的,所以本人在Windows操作系统中遇到了不少问题,其中最多的就是编码问题,其他问题还行,所幸所有问题都已经解决完毕。在本次实验的过程中,也学习到了很多新的知识,比如获取了数据库之后如何将数据转换成字典,再供数据解析方法使用、在项目拉取下来之后应把依赖库独立放到一个文件夹中以免“污染”原依赖库等等。 本次实验不足之处也有许多,比如:没有完成大量IP代理池进行爬取信息的功能,请大家见谅。 希望以后能自己编写一个完整的python爬虫项目并用本人最熟悉的java语言进行再次编写。
6.参考文献
[1].百度百科
Github项目链接:https://github.com/ScarecrowFu/crawllagou