python-scrapy爬虫框架爬取拉勾网招聘信息
本文实例为爬取拉勾网上的python相关的职位信息, 这些信息在职位详情页上, 如职位名, 薪资, 公司名等等.
分析思路
分析查询结果页
在拉勾网搜索框中搜索'python'关键字, 在浏览器地址栏可以看到搜索结果页的url为: 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=', 尝试将?后的参数删除, 发现访问结果相同.
打开Chrome网页调试工具(F12), 分析每条搜索结果(即每个职位)在html中所处的元素定位, 发现每条结果都在<ul class="item_con_list">下的li标签中.
因为我们需要每个职位的具体信息, 因此需要获取到每条搜索结果的详情url, 即点击搜索结果后进入的详情页的url.
继续查看li标签中的元素, 找到想要的详情url, 找到后的url为: href="https://www.lagou.com/jobs/6945237.html?show=b6e8e778fcae4c2aa2111ba58f9ebfa0"
查看其它搜索结果的详情url, 发现其格式都为: href="https://www.lagou.com/jobs/{某个id}.html?show={show_id}"
对于第一个ID, 每条结果的id都不一样, 猜想其为标记每个职位的唯一id, 对于show_id, 每条结果的id都是一样的, 尝试删除show参数, 发现一样可以访问到具体结果详情页
那么我们直接通过xpath提取到每个职位的第一个ID即可, 但是调试工具的elements标签下的html是最终网页展示的html, 并不一定就是我们访问 https://www.lagou.com/jobs/list_python 返回的response的html, 因此点到Network标签, 重新刷新一下页面, 找到 https://www.lagou.com/jobs/list_python 对应的请求, 查看其对应的response, 搜索 'position_link'(即前面我们在elements中找到的每条搜索结果的详情url), 发现确实返回了一个网址, 但是其重要的两个ID并不是直接放回的, 而是通过js生成的, 说明我们想要的具体数据并不是这个这个请求返回的.
那么我们就需要找到具体是那个请求会返回搜索结果的信息, 一般这种情况首先考虑是不是通过ajax获取的数据, 筛选类型为XHR(ajax)的请求, 可以逐个点开查看response, 发现 positionAjax.json 返回的数据中就存在我们想要的每条搜索结果的信息. 说明确实是通过ajax获取的数据, 其实点击下一页, 我们也可以发现地址栏url地址并没有发生变化, 只是局部刷新了搜索结果的数据, 也说明了搜索结果是通过ajax返回的.
分析上面ajax的response, 查看其中是否有我们想要的职位ID, 在preview中搜索之前在elements中找到的某个职位的url的两个ID, 确实两个ID都存在response中, 分析发现第一个ID即为positionId, 第二个即为showId, 我们还可以发现response中返回了当前的页码数pageNo
因此我们只需要访问上面ajax对应的url: https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false 就可以拿到我们想要的ID, 然后填入详情url模板: https://www.lagou.com/jobs/{position_id}.html?show={show_id}中即可访问详情页了.
但是当我们直接访问 https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false 时 ,返回的结果却是: {"status":false,"msg":"您操作太频繁,请稍后再访问","clientIp":"139.226.66.44","state":2402}
经过百度查询后发现原来直接访问上述地址是不行的, 这也是拉钩的一个反爬策略, 需要我们带上之前访问查询结果页(https://www.lagou.com/jobs/list_python?)的cookie才行, 因为我们这里使用的是scrapy框架, 该框架是能够自带上次请求的cookie来访问下一个请求的, 所以我们这里不需要手动去添加cookie信息, 只需要首先访问一下查询结果页就可以了. 即start_url = https://www.lagou.com/jobs/list_python
此外发现这个ajax请求是通过POST方式发送的, 因此还需要分析它提交的form数据, 在第一页中有三条数据信息, first为true, pn为1 kd为python , 第二页中first为false, pn为2, kd同样为python, 且多了一个sid
分析这四个参数, 第一个first为表示是否是第一页, 第二个pn为表示当前页码数, 第三个kd为表示搜索的关键字, 第四个sid经过和上面showId对比发现其值就为showId
分析职位详情页
前面分析完后就可以拼接出职位详情页url了, 点开详情页, 同样的思路分析我们想要的数据是不是就在详情页的url中, 这里想要职位名称, 工资, 地点, 经验, 关键字, 公司信息等
在network中查找对应的response, 发现数据确实就存在response中, 因此直接通过xpath就可以提取想要的数据了
编写爬虫代码
具体代码在github:
这里只放出关键代码
创建scrapy项目
scrapy startproject LaGou
创建爬虫
scrapy genspider lagou www.lagou.com
编写items.py, 设置要想爬取的字段
# -*- coding: utf-8 -*- # Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html import scrapy class LagouItem(scrapy.Item):
# define the fields for your item here like:
job_url = scrapy.Field()
job_name = scrapy.Field()
salary = scrapy.Field()
city = scrapy.Field()
area = scrapy.Field()
experience = scrapy.Field()
education = scrapy.Field()
labels = scrapy.Field()
publish_date = scrapy.Field()
company = scrapy.Field()
company_feature = scrapy.Field()
company_public = scrapy.Field()
company_size= scrapy.Field()
编写爬虫代码 lagou.py
# -*- coding: utf-8 -*-
import scrapy
from LaGou.items import LagouItem
import json
from pprint import pprint
import time class LagouSpider(scrapy.Spider):
name = 'lagou'
allowed_domains = ['www.lagou.com']
start_urls = ['https://www.lagou.com/jobs/list_python?'] def __init__(self):
# 设置头信息, 若不设置的话, 在请求第二页时即被拉勾网认为是爬虫而不能爬取数据
self.headers = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"Connection": "keep-alive",
"Host": "www.lagou.com",
"Referer": 'https://www.lagou.com/jobs/list_Python?',
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"referer": "https://www.lagou.com/jobs/list_python?"
}
self.sid = ''
self.job_url_temp = 'https://www.lagou.com/jobs/{}.html?show={}'
# 清空文件
with open('jobs.json', 'w') as f:
f.truncate() def parse(self, response):
"""
解析起始页
"""
# response为GET请求的起始页, 自动获取cookie
# 提交POST带上前面返回的cookies, 访问数据结果第一页
yield scrapy.FormRequest(
'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false',
callback=self.parse_list,
formdata={"first": "false",
"pn": "",
"kd": "python",
},
headers=self.headers
)
def parse_list(self, response):
"""
解析结果列表页的json数据
"""
# 获取返回的json,转为字典
res_dict = json.loads(response.text)
# 判断返回是否成功
if not res_dict.get('success'):
print(res_dict.get('msg', '返回异常'))
else:
# 获取当前页数
page_num = res_dict['content']['pageNo']
print('正在爬取第{}页'.format(page_num))
# 获取sid
if not self.sid:
self.sid = res_dict['content']['showId']
# 获取响应中的职位url字典
part_url_dict = res_dict['content']['hrInfoMap']
# 遍历职位字典
for key in part_url_dict:
# 初始化保存职位的item
item = LagouItem()
# 拼接完整职位url
item['job_url'] = self.job_url_temp.format(key, self.sid)
# 请求职位详情页
yield scrapy.Request(
item['job_url'],
callback=self.parse_detail,
headers=self.headers,
meta={'item': item}
) # 获取下一页
if page_num < 30:
# time.sleep(2)
yield scrapy.FormRequest(
'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false',
callback=self.parse_list,
formdata={"first": "false",
"pn": str(page_num+1),
"kd": "python",
"sid": self.sid
},
headers=self.headers
) def parse_detail(self, response):
"""
解析职位详情页
"""
# 接收item
item = response.meta['item']
# 解析数据
# 获取职位头div
job_div = response.xpath('//div[@class="position-content-l"]')
if job_div:
item['job_name'] = job_div.xpath('./div/h1/text()').extract_first()
item['salary'] = job_div.xpath('./dd/h3/span[1]/text()').extract_first().strip()
item['city'] = job_div.xpath('./dd/h3/span[2]/text()').extract_first().strip('/').strip()
item['area'] = response.xpath('//div[@class="work_addr"]/a[2]/text()').extract_first()
item['experience'] = job_div.xpath('./dd/h3/span[3]/text()').extract_first().strip('/').strip()
item['education'] = job_div.xpath('./dd/h3/span[4]/text()').extract_first().strip('/').strip()
item['labels'] = response.xpath('//ul[@class="position-label clearfix"]/li/text()').extract()
item['publish_date'] = response.xpath('//p[@class="publish_time"]/text()').extract_first()
item['publish_date'] = item['publish_date'].split('&')[0]
# 获取公司dl
company_div = response.xpath('//dl[@class="job_company"]')
item['company'] = company_div.xpath('./dt/a/img/@alt').extract_first()
item['company_feature'] = company_div.xpath('./dd//li[1]/h4[@class="c_feature_name"]/text()').extract_first()
item['company_feature'] = item['company_feature'].split(',')
item['company_public'] = company_div.xpath('./dd//li[2]/h4[@class="c_feature_name"]/text()').extract_first()
item['company_size'] = company_div.xpath('./dd//li[4]/h4[@class="c_feature_name"]/text()').extract_first()
yield item
编写middlewares.py, 自定义downloadermiddleware, 用来每次发送请求前, 随机设置user-agent, 这里使用了第三方库 fake_useragent, 能够随机提供user-agent, 使用前先安装: pip install fake_useragent
from fake_useragent import UserAgent
import random class RandomUserAgentDM:
"""
随机获取userAgent
"""
def __init__(self):
self.user_agent = UserAgent() def process_request(self, request, spider):
request.headers['User-Agent'] = self.user_agent.random
编写pipelines.py, 将数据存为json文件
import json class LagouPipeline:
def process_item(self, item, spider):
with open('jobs.json', 'a', encoding='utf-8') as f:
item_json = json.dumps(dict(item), ensure_ascii=False, indent=2)
f.write(item_json)
f.write('\n')
编写settings.py
# 设置日志显示
LOG_LEVEL = 'WARNING' # 设置ROBOTSTXT协议, 若为true则不能爬取数据
ROBOTSTXT_OBEY = False # 设置下载器延迟, 反爬虫的一种策略
DOWNLOAD_DELAY = 0.25 # 开启DOWNLOADER_MIDDLEWARES
DOWNLOADER_MIDDLEWARES = {
# 'LaGou.middlewares.LagouDownloaderMiddleware': 543,
'LaGou.middlewares.RandomUserAgentDM' :100,
} # 开启ITEM_PIPELINES
ITEM_PIPELINES = {
'LaGou.pipelines.LagouPipeline': 300,
}
启动爬虫
scrapy crawl lagou
发现依然只能5 6页, 说明拉勾网的反爬确实做得比较好, 还可以继续通过使用代理来进行反反爬, 这里就不再演示了,
查看爬取结果
python-scrapy爬虫框架爬取拉勾网招聘信息的更多相关文章
- 爬取拉勾网招聘信息并使用xlwt存入Excel
xlwt 1.3.0 xlwt 文档 xlrd 1.1.0 python操作excel之xlrd 1.Python模块介绍 - xlwt ,什么是xlwt? Python语言中,写入Excel文件的扩 ...
- Python爬取拉勾网招聘信息并写入Excel
这个是我想爬取的链接:http://www.lagou.com/zhaopin/Python/?labelWords=label 页面显示如下: 在Chrome浏览器中审查元素,找到对应的链接: 然后 ...
- 手把手教大家如何用scrapy爬虫框架爬取王者荣耀官网英雄资料
之前被两个关系很好的朋友拉入了王者荣耀的大坑,奈何技术太差,就想着做一个英雄的随查手册,这样就可以边打边查了.菜归菜,至少得说明咱打王者的态度是没得说的,对吧?大神不喜勿喷!!!感谢!!废话不多说,开 ...
- scrapy爬虫框架爬取招聘网站
目录结构 BossFace.py文件中代码: # -*- coding: utf-8 -*-import scrapyfrom ..items import BossfaceItemimport js ...
- 【图文详解】scrapy爬虫与动态页面——爬取拉勾网职位信息(2)
上次挖了一个坑,今天终于填上了,还记得之前我们做的拉勾爬虫吗?那时我们实现了一页的爬取,今天让我们再接再厉,实现多页爬取,顺便实现职位和公司的关键词搜索功能. 之前的内容就不再介绍了,不熟悉的请一定要 ...
- [Python] Scrapy爬虫框架入门
说明: 本文主要学习Scrapy框架入门,介绍如何使用Scrapy框架爬取页面信息. 项目案例:爬取腾讯招聘页面 https://hr.tencent.com/position.php?&st ...
- node.js爬虫爬取拉勾网职位信息
简介 用node.js写了一个简单的小爬虫,用来爬取拉勾网上的招聘信息,共爬取了北京.上海.广州.深圳.杭州.西安.成都7个城市的数据,分别以前端.PHP.java.c++.python.Androi ...
- Python Scrapy爬虫框架之初次使用
此篇博客为本人对小甲鱼的课程的总结. 关于Scrapy的安装网上都有方法,这里便不再叙述. 使用Scrapy抓取一个网站一共需要四个步骤: 0.创建一个Scrapy项目: 1.定义Item容器: 2. ...
- Scrapy实战篇(七)之Scrapy配合Selenium爬取京东商城信息(下)
之前我们使用了selenium加Firefox作为下载中间件来实现爬取京东的商品信息.但是在大规模的爬取的时候,Firefox消耗资源比较多,因此我们希望换一种资源消耗更小的方法来爬取相关的信息. 下 ...
随机推荐
- JavaWeb网上图书商城完整项目--25.注册页面之隐藏没有内容的错误信息实现
在上一章中我们显示的效果如下所示: 上面后面都有错误的红色×的显示,这样是不对的,我们要解决该问题 我们要循环遍历每一个错误的信息,看它的内容有没有,如果有内容我们就显示错误的×,如果没有就不显示× ...
- h5请求签名加密
签名说明 签名对 url + method + 业务参数 进行统一签名,防止重放和篡改 客户端js对加密逻辑和appSecret进行混淆加密处理,增加破解难度 客户端本地存储appid 和 appSe ...
- jQuery实现全选、反选、删除
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...
- asp .net core发布订阅kafka
Kafka是一种高吞吐量的分布式发布订阅消息系统,有如下特性: 通过O的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能. 高吞吐量:即使是非常普通的硬件Ka ...
- P1131 [ZJOI2007]时态同步【树形dp】
时态同步 从叶子到根节点统计修改次数.树形\(dp\)思想. 题目描述 小\(Q\)在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字\(1,2,3-\). ...
- FocusBI:《商业智能7B理论模型》创造者
<商业智能7B理论模型>专门为培养企业级BI人才<如何一个人完成BI项目,成为企业级BI人才>课程而创造,历经我7年的商业智能项目实施工作和经验的提炼与总结,分别深入在甲方公司 ...
- 洛谷 P6082 [JSOI2015]salesman
题意 给定一棵\(n\)个点的树,有点权,你从\(1\)号点开始一次旅行,最后回到\(1\)号点.每到达一个点,你就能获得等于该点点权的收益, 但每个点都有进入该点的次数限制,且每个点的收益只能获得一 ...
- 深度学习论文翻译解析(九):Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
论文标题:Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition 标题翻译:用于视觉识别的深度卷积神 ...
- 链表中倒数第k个节点(剑指offer-14)
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ ...
- Zip破解工具Fcrackzip使用简介
0x00 fcrackzip简单介绍 fcrackzip是一款专门破解zip类型压缩文件密码的工具,工具小巧方便.破解速度快,能使用字典和指定字符集破解,适用于linux.mac osx 系统 0x0 ...