人海茫茫
相识真好

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

首先注明:感谢拉勾网提供的权威、质量的数据,本人抱着学习的态度,不愿增加其服务器负担,与dos攻击。

由于后面准备做一个大一点的数据分析项目,所以前提需要获取大量的有质量和权威的信息,其中一个获取点便是拉钩网,进入正题:

本片将介绍对拉钩网的招聘数据爬取,过程中包括了反爬虫post请求来获取数据文件。以及将所有的信息,保存到MySQL数据库中。

首先我们来分析一下我们需要爬取信息的网页信息:

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

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

下面这个招聘信息就是我们后面要获取的信息,获取的内容包括(城市、规模、学历、领域、职位、薪水、工作经验等7个内容)

但是我们马上就会发现下面这个问题:

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

这只有一页信息,当我们换页时,发现它并没有加载网页,而是直接就出来了,那不是就没法爬取更多的信息了吗,只有一页。这便是该网页的一个反爬虫措施,这是属于异步加载的网页,这里就不多讲它了。我们来获取数据

点击F12开发调试工具,选中Network,再点F5刷新网页,可以看到有很多的数据包出来了:

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

这些就是网页传输时的数据包(当然也可以使用fiddler这样的抓包工具来抓),一个小技巧,大部分网页传输的内容信息包含在后缀名为json的文件里,所以这里我们在上面输入json来过滤包,仔细查看并选中可能包含有传输数据的包,再打开它:

查看Response找到是否包含我们的数据:

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

在Preview中我们可以结构化的查看数据包。

找到信息后我们就要思考如何才能够拿到这些数据了。这样的数据一般都是post请求,我们查看Headers寻找信息:

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

在Heasers里面我们发现两个数据格式:Query string parameters和Form data,第一个是get请求明文附在请求链接上面的信息,后面的是post请求需要匿名传输的。

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

右键数据包,然后拷贝下它的链接,这个链接包含了明文信息,无需我们手动附加(你可以点击open link in new tab,应该是操作频繁),接着我们只需要post请求并传输匿名信息来获取数据包了。

在此之前我们先分析一下匿名传输信息到底代表什么意思哈:

上面的图可以看到有三个信息:first、pd、kd,其中first=True我也不是太明白意义,大概是为真吧;接着pd,这个很好理解page number页数;接着kd,key words关键字,也就是你要搜索的关键字,为空表示内容不限。这都是我个人的理解,应该差不多吧。

好了,所有的准备工作都做完了,现在就开始scrapy框架的爬取。

创建scrapy这里就不累述了(太基础的可以先去了解一下,以及需要了解一下scrapy的工作原理):

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

我们先编写容器文件,以便确定我们需要获取的信息(这里我在items里面新建的类,也可以使用原来的):

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

接着我们来编写spider文件:

spider源码:

# -*- coding: utf-8 -*-
import scrapy
import json
import time
import os
path = os.path.abspath(os.path.join(os.getcwd(), "../.."))
import sys
sys.path.append(path)
from get_lagouwang.items import LagouItem
import main
 
 
class LagouwangSpider(scrapy.Spider):
	name = 'lagouwang'
 
	def __init__(self):
		self.headers = {'Referer':'https://www.lagou.com/jobs/list_?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=','User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0'}
		self.cookie = {"user_trace_token":"20180526201245-0ea516fd-da30-4994-9995-5fa02a3eab36",
			"LGUID":"20180526201246-19bb1f81-60de-11e8-a18e-525400f775ce",
			"index_location_city":"%E5%85%A8%E5%9B%BD",
			"JSESSIONID":"ABAAABAABEEAAJAF96A0BE8427F1DD99F8C9B9EB6D7970D",
			"TG-TRACK-CODE":"search_code",
			"SEARCH_ID":"acc14cb01bbe44db9e430661bccdc92e",
			"_gid":"GA1.2.187875505.1527336770",
			"_ga":"GA1.2.467998237.1527336770",
			"Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6":"1527408192,1527408683,1527469793,1527486623",
			"Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6":"1527503666",
			"LGSID":"20180528183425-b12d579f-6262-11e8-ada5-525400f775ce",
			"PRE_UTM":"",
			"PRE_HOST":"",
			"PRE_SITE":"https%3A%2F%2Fwww.lagou.com%2F",
			"PRE_LAND":"https%3A%2F%2Fwww.lagou.com%2Fjobs%2Flist_%3FlabelWords%3D%26fromSearch%3Dtrue%26suginput%3D",
			"LGRID":"20180528183425-b12d59ef-6262-11e8-ada5-525400f775ce"
			}
		self.url = 'https://www.lagou.com/jobs/positionAjax.json?px=new&needAddtionalResult=false'
		self.total_page = 0
		self.first = 'true'
		self.kd = '' 
		self.pn = 1
 
	def parse(self, response):
		item = LagouItem()
		data = json.loads(response.body)
		data_position = data['content']['positionResult']
		data_result = data_position['result']
		self.total_page = data_position['totalCount']
		for i in range(len(data_result)):
			item['salary'] = data_result[i]['salary']
			item['city'] = data_result[i]['city']
			item['workYear'] = data_result[i]['workYear']
			item['education'] = data_result[i]['education']
			item['industryField'] = data_result[i]['industryField']
			item['companySize'] = data_result[i]['companySize']
			item['positionName'] = data_result[i]['positionName']
			sal = data_result[i]['salary']
			sal = sal.split('-')
			if(len(sal)==1):
				item['salary_min'] = sal[0][:sal[0].find('k')]
				item['salary_max'] = sal[0][:sal[0].find('k')]
			else:
				item['salary_min'] = sal[0][:sal[0].find('k')]
				item['salary_max'] = sal[1][:sal[1].find('k')]
			yield item
		if(self.pn <= self.total_page):    #当前页数小于总页数,那么我们就继续请求下一页,再爬取
			print("pn:{}    运行中请勿打断...".format(self.pn+1))
			time.sleep(0.5)
			self.pn +=1
			yield scrapy.http.FormRequest(self.url,cookies=self.cookie,headers=self.headers,formdata={'first':self.first,'kd':self.kd,'pn':str(self.pn)},callback=self.parse)
 
	def start_requests(self):
		return [scrapy.http.FormRequest(self.url,cookies=self.cookie,headers=self.headers,formdata={'first':self.first,'kd':self.kd,'pn':str(self.pn)},callback=self.parse)]

注意这里我设置了cookie和headers,在post请求时一同传输的,伪造浏览器的信息,防止网站封IP。在Headers可以获取:

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

将里面的信息,获取到。这些信息有点多,在上面的代码中,其实真正的内容并不多。

首先我们使用start_requests来请求第一页信息,返回至parse函数中,然后在parse中通过判断当前页数,来确定是否继续爬取网页。需要注意的上面的文件中我们导入了之前创建的容器类LagouItem()。os.path.abspath是我用来导入上上层文件的路径,sys.path我用来添加main.py和items.py文件的路径,main.py中,就是执行scraoy的命令,你也可以在cmd中执行scrapy,我之前的文章有介绍很方便的。spider很简单,代码不多,我们介绍pipelines文件。

现在我们只是爬取到了文件,但是并没有保存它,我们可以将信息保存为csv文件或其他格式,这里我们使用MySQL数据库来存储。所以我们要编写pipelines文件,在里面利用item容器每次存入后传递过来的值。

pipelines源码:

import pymysql
import re
 
class GetLagouwangPipeline(object):
 
	def table_exists(self,con,table_name):    #这个函数用来判断数据库中是否存在此表,当然也可以不写,手动控制
		sql = "show tables;"    #sql语句
		con.execute(sql)
		tables = [con.fetchall()]
		table_list = re.findall('(\'.*?\')',str(tables))
		table_list = [re.sub("'",'',each) for each in table_list]
		if table_name in table_list:
			return 1
		else:
			return 0
 
	def process_item(self,item,spider):
		connect = pymysql.connect(
			user = 'root',
			password = 'root@123456',
			db = 'MYSQL',
			host = '127.0.0.1',
			port = 3306,
			charset = 'utf8'
			)
		con = connect.cursor()
		# con.execute("create database w_lagouwang")    #第一次使用需要创建数据库
		con.execute("use w_lagouwang")    #使用数据库
		table_name = 'lagouwang'        #表名
		if(self.table_exists(con,table_name) != 1):
			sql = '''create table lagouwang(city varchar(20),companySize varchar(20),education varchar(20),
			industryField varchar(30),positionName varchar(40),salary varchar(20),salary_max varchar(10),salary_min varchar(10),
			workYear varchar(20))'''
			con.execute(sql)    #创建数据库
		data = {'city':item['city'],'companySize':item['companySize'],'education':item['education'],
				'industryField':item['industryField'],'positionName':item['positionName'],'salary':item['salary'],
				'salary_max':item['salary_max'],'salary_min':item['salary_min'],'workYear':item['workYear']}
		city = data['city']
		companySize = data['companySize']       #提取item传来的值到对应的9个变量中
		education = data['education']
		industryField = data['industryField']
		positionName = data['positionName']
		salary = data['salary']
		salary_max = data['salary_max']
		salary_min = data['salary_min']
		workYear = data['workYear']            #保存到表中
		con.execute('insert into lagouwang(city,companySize,education,industryField,positionName,salary,salary_max,salary_min,workYear)values(%s,%s,%s,%s,%s,%s,%s,%s,%s)',
			[city,companySize,education,industryField,positionName,salary,salary_max,salary_min,workYear])
		connect.commit()    #保存到数据库后记得需要提交
		con.close()        #关闭连接
		connect.close()
		return data

其实很简单的,就是sql语句有点多,和一个判断表的函数,真正的代码不多。

好了,接下来就是运行了,等等我们先配置一下setting文件:

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

记得开启setting中的ITEM_PIPELINES通道,否则通道不能使用。

接下来就可以运行了。。。

python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

大致数据像上面,对于上面的数据我还做过一点处理,但是不影响上面的操作。

好了拉钩网反爬虫的项目就完成了,可以看出还是挺简单的,只是每一步都有需要细心,还有就是注意文件之间的关联性,其他的就没有了。反爬虫其实还有很多种,这里只是一种,在接下来的几篇文章中我还会介绍爬取 直聘网,猎聘网等,都有反爬虫机制。有问题的朋友可以留言交流。

 

赞(0) 打赏
未经允许不得转载:老康的学习空间 » python3 scrapy实战:爬取拉勾网招聘数据至数据库(反爬虫)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏