首先注明:感谢拉勾网提供的权威、质量的数据,本人抱着学习的态度,不愿增加其服务器负担,与dos攻击。
由于后面准备做一个大一点的数据分析项目,所以前提需要获取大量的有质量和权威的信息,其中一个获取点便是拉钩网,进入正题:
本片将介绍对拉钩网的招聘数据爬取,过程中包括了反爬虫post请求来获取数据文件。以及将所有的信息,保存到MySQL数据库中。
首先我们来分析一下我们需要爬取信息的网页信息:
https://www.lagou.com/jobs/list_?labelWords=&fromSearch=true&suginput=
下面这个招聘信息就是我们后面要获取的信息,获取的内容包括(城市、规模、学历、领域、职位、薪水、工作经验等7个内容)
但是我们马上就会发现下面这个问题:
这只有一页信息,当我们换页时,发现它并没有加载网页,而是直接就出来了,那不是就没法爬取更多的信息了吗,只有一页。这便是该网页的一个反爬虫措施,这是属于异步加载的网页,这里就不多讲它了。我们来获取数据
点击F12开发调试工具,选中Network,再点F5刷新网页,可以看到有很多的数据包出来了:
这些就是网页传输时的数据包(当然也可以使用fiddler这样的抓包工具来抓),一个小技巧,大部分网页传输的内容信息包含在后缀名为json的文件里,所以这里我们在上面输入json来过滤包,仔细查看并选中可能包含有传输数据的包,再打开它:
查看Response找到是否包含我们的数据:
在Preview中我们可以结构化的查看数据包。
找到信息后我们就要思考如何才能够拿到这些数据了。这样的数据一般都是post请求,我们查看Headers寻找信息:
在Heasers里面我们发现两个数据格式:Query string parameters和Form data,第一个是get请求明文附在请求链接上面的信息,后面的是post请求需要匿名传输的。
右键数据包,然后拷贝下它的链接,这个链接包含了明文信息,无需我们手动附加(你可以点击open link in new tab,应该是操作频繁),接着我们只需要post请求并传输匿名信息来获取数据包了。
在此之前我们先分析一下匿名传输信息到底代表什么意思哈:
上面的图可以看到有三个信息:first、pd、kd,其中first=True我也不是太明白意义,大概是为真吧;接着pd,这个很好理解page number页数;接着kd,key words关键字,也就是你要搜索的关键字,为空表示内容不限。这都是我个人的理解,应该差不多吧。
好了,所有的准备工作都做完了,现在就开始scrapy框架的爬取。
创建scrapy这里就不累述了(太基础的可以先去了解一下,以及需要了解一下scrapy的工作原理):
我们先编写容器文件,以便确定我们需要获取的信息(这里我在items里面新建的类,也可以使用原来的):
接着我们来编写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可以获取:
将里面的信息,获取到。这些信息有点多,在上面的代码中,其实真正的内容并不多。
首先我们使用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文件:
记得开启setting中的ITEM_PIPELINES通道,否则通道不能使用。
接下来就可以运行了。。。
大致数据像上面,对于上面的数据我还做过一点处理,但是不影响上面的操作。
好了拉钩网反爬虫的项目就完成了,可以看出还是挺简单的,只是每一步都有需要细心,还有就是注意文件之间的关联性,其他的就没有了。反爬虫其实还有很多种,这里只是一种,在接下来的几篇文章中我还会介绍爬取 直聘网,猎聘网等,都有反爬虫机制。有问题的朋友可以留言交流。