Scrapy 教程(七)-架构与中间件
Scrapy 使用 Twisted 这个异步框架来处理网络通信,架构清晰,并且包含了各种中间件接口,可以灵活的完成各种需求。
Scrapy 架构
其实之前的教程都有涉及,这里再做个系统介绍
- Engine :Scrapy 引擎,即控制中心,负责控制数据流在系统的各个组件中流动,并根据相应动作触发事件;引擎首先从爬虫获取初始request请求(1)
- Scheduler : 调度器,调度器从引擎接收request请求(2),并存入队列,在需要时再将request请求提供给引擎(3)
- Downloade : 下载器,负责下载页面;从引擎获取request请求(4),并将下载内容返回给引擎(5)
- Spider: 爬虫,解析html,提取数据;从引擎获取下载器返回的页面内容(6),提取item数据返回给引擎(7)
- Item Pipeline: 数据处理管道,负责处理提取好的item,如清洗,存储等;从引擎获取item,并将新的url传递给调度器(8)
- 下载器中间件: 引擎与下载器之间特定钩子,其提供了一个简单机制,通过插入自定义代码来扩展Scrapy;可自定义 request 请求的方式及 response 的处理
- 爬虫中间件: 引擎与爬虫之间的特点钩子,机制同上;处理 输入 request 和输出 response【如给url加个数字,response 编码等】
中间件详解
Scrapy 自带很多中间件,这些中间件都可以被重写,重写的中间件需要手动激活。
中间件激活
下载中间件激活方法是设定 settings 文件中 DOWNLOADER_MIDDLEWARES 关键字
DOWNLOADER_MIDDLEWARES = {
'qiushi.middlewares.UserAgent':1,
'qiushi.middlewares.ProxyMiddleware':100,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware':None}
数字代表优先级,通常取值为1-1000,数字越小,越靠近引擎,故数字越小,request 优先处理,数字越大,response 优先处理
爬虫中间件激活方式类,只是设定关键字为 SPIDER_MIDDLEWARES
自定义下载中间件
在创建爬虫项目时,框架自动生成了一个 middlewares.py 文件,里面定义了两个中间件,XXXSpiderMiddleware 和 XXXDownloaderMiddleware,但这两个中间件基本只是个空架子;
我们可以直接修改这两个中间件来实现自定义,也可以在这个文件中重新写,也可以自己创建文件重新写;
重写中间件时方法名是固定的;
process_request(self, request, spider) 处理请求的中间件,最常用,如代理,伪装浏览器,headers等
process_response(self, request, response, spider): 处理响应的中间件
process_exception(self, request, exception, spider):处理异常的中间件
process_request
每次request请求都会自动调用这个方法;
该方法一般返回None,也可以返回 Response 对象、request 对象、IgnoreRequest对象
- None:继续处理该 request,执行其他中间件中的该方法,
- Response:请求结束,不再执行其他中间件的该方法,
- request:结束本请求,重新执行返回的request
- IgnoreRequest:执行 process_exception 方法;如果没有相应的方法处理异常,则调用 request 的 errback 方法;如果没有代码处理该异常,则忽略
具体例子可参考我的博客 https://www.cnblogs.com/yanshw/p/10858087.html
这里再展示一个 scrapy 与 requests 结合实现代理中间件的例子;
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
# 'Connection': 'close'
} class Proxy_Middleware(): def __init__(self):
self.s = requests.session() def process_request(self, request, spider):
try:
xdaili_url = spider.settings.get('XDAILI_URL') # 应该是返回代理ip的url,买的代理
r = self.s.get(xdaili_url, headers= headers)
proxy_ip_port = r.text
request.meta['proxy'] = 'http://' + proxy_ip_port
except requests.exceptions.RequestException:
print('***get xdaili fail!')
spider.logger.error('***get xdaili fail!') def process_response(self, request, response, spider):
if response.status != 200:
try:
xdaili_url = spider.settings.get('XDAILI_URL') r = self.s.get(xdaili_url, headers= headers)
proxy_ip_port = r.text
request.meta['proxy'] = 'http://' + proxy_ip_port
except requests.exceptions.RequestException:
print('***get xdaili fail!')
spider.logger.error('***get xdaili fail!') return request
return response def process_exception(self, request, exception, spider): try:
xdaili_url = spider.settings.get('XDAILI_URL') r = self.s.get(xdaili_url, headers= headers)
proxy_ip_port = r.text
request.meta['proxy'] = 'http://' + proxy_ip_port
except requests.exceptions.RequestException:
print('***get xdaili fail!')
spider.logger.error('***get xdaili fail!') return request
重试中间件
有时候代理会被远程拒绝或者超时,此时需要换个代理重新请求,就是重写中间件 scrapy.downloadermiddlewares.retry.RetryMiddleware
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message class My_RetryMiddleware(RetryMiddleware): def process_response(self, request, response, spider):
if request.meta.get('dont_retry', False):
return response if response.status in self.retry_http_codes:
reason = response_status_message(response.status)
try:
xdaili_url = spider.settings.get('XDAILI_URL') r = requests.get(xdaili_url)
proxy_ip_port = r.text
request.meta['proxy'] = 'https://' + proxy_ip_port
except requests.exceptions.RequestException:
print('获取讯代理ip失败!')
spider.logger.error('获取讯代理ip失败!') return self._retry(request, reason, spider) or response
return response def process_exception(self, request, exception, spider):
if isinstance(exception, self.EXCEPTIONS_TO_RETRY) and not request.meta.get('dont_retry', False):
try:
xdaili_url = spider.settings.get('XDAILI_URL') r = requests.get(xdaili_url)
proxy_ip_port = r.text
request.meta['proxy'] = 'https://' + proxy_ip_port
except requests.exceptions.RequestException:
print('获取讯代理ip失败!')
spider.logger.error('获取讯代理ip失败!') return self._retry(request, exception, spider)
selenium 中间件
用 selenium 实现中间件
from scrapy.http import HtmlResponse
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from gp.configs import * class ChromeDownloaderMiddleware(object): def __init__(self):
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 设置无界面
if CHROME_PATH:
options.binary_location = CHROME_PATH
if CHROME_DRIVER_PATH:
self.driver = webdriver.Chrome(chrome_options=options, executable_path=CHROME_DRIVER_PATH) # 初始化Chrome驱动
else:
self.driver = webdriver.Chrome(chrome_options=options) # 初始化Chrome驱动 def __del__(self):
self.driver.close() def process_request(self, request, spider):
try:
print('Chrome driver begin...')
self.driver.get(request.url) # 获取网页链接内容
return HtmlResponse(url=request.url, body=self.driver.page_source, request=request, encoding='utf-8',
status=200) # 返回HTML数据
except TimeoutException:
return HtmlResponse(url=request.url, request=request, encoding='utf-8', status=500)
finally:
print('Chrome driver end...')
process_response
当请求发出去返回时会调用该方法;
- 返回 Response,由下个中间件或者解析器parse处理;
- 返回 Request,说明没有成功返回,中间链停止,重新request;
- 返回 IgnoreRequest,回调函数 Request.errback将会被调用处理,若没处理,将会忽略
process_exception
处理异常
- 返回 None:调用其他中间件的该方法处理异常
- 返回 Response:异常已被处理,交给中间件的 process_response 方法处理
- 返回 Request:结束该 request,重新执行新的 request
from_crawler
(cls, crawler)
这个函数通常是 访问 settings 和 signals 的入口;类方法
@classmethod
def from_crawler(cls, crawler):
return cls(
mysql_host = crawler.settings.get('MYSQL_HOST'),
mysql_db = crawler.settings.get('MYSQL_DB'),
mysql_user = crawler.settings.get('MYSQL_USER'),
mysql_pw = crawler.settings.get('MYSQL_PW')
)
scrapy 自带下载中间件
{
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
RobotsTxtMiddleware 这个中间件会查看 settings 中 ROBOTSTXT_OBEY 是True还是False,如果是True,表示要遵守 Robots.txt 协议;
中间件验证
你可以通过自己认为可行的方式验证;
这里介绍一个网站,可以验证中间件是否生效
http://exercise.kingname.info
http://exercise.kingname.info/exercise_middleware_ua 验证User-Agent
http://exercise.kingname.info/exercise_middleware_ip 验证代理
都可以翻页的,只需在url后加/page,如exercise.kingname.info/exercise_middleware_ip/3
自定义爬虫中间件
不是很常用,也是实现几个固定方法
process_spider_input(response, spider)
response 通过爬虫中间件时,该方法被调用
process_spider_output(response, result, spider)
爬虫处理完 response 时,调用该方法,必须返回 Request或者 Item对象的可迭代对象
process_spider_exception(response, exception, spider)
当spider中间件抛出异常时,这个方法被调用,返回None或可迭代对象的Request、dict、Item
参考资料:
https://zhuanlan.zhihu.com/p/42498126
https://www.jianshu.com/p/70af817db7df
http://www.cnblogs.com/xieqiankun/p/know_middleware_of_scrapy_1.html
https://blog.csdn.net/on_the_road_2018/article/details/80985524 这个教程有详细的cookie登录
Scrapy 教程(七)-架构与中间件的更多相关文章
- CRL快速开发框架系列教程七(使用事务)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- Laravel教程 七:表单验证 Validation
Laravel教程 七:表单验证 Validation 此文章为原创文章,未经同意,禁止转载. Laravel Form 终于要更新这个Laravel系列教程的第七篇了,期间去写了一点其他的东西. 就 ...
- 无废话ExtJs 入门教程七[登陆窗体Demo:Login]
无废话ExtJs 入门教程七[登陆窗体Demo:Login] extjs技术交流,欢迎加群(201926085) 在这节我们通过前几节讲的内容做一个登陆页面,把前几节讲的内容贯穿一下. 1.代码如下: ...
- ASP.NET 5系列教程(七)完结篇-解读代码
在本文中,我们将一起查看TodoController 类代码. [Route] 属性定义了Controller的URL 模板: [Route("api/[controller]") ...
- 黄聪:Microsoft Enterprise Library 5.0 系列教程(七) Exception Handling Application Block
原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(七) Exception Handling Application Block 使用企业库异常处理应用程序模块的 ...
- scrapy中的下载器中间件
scrapy中的下载器中间件 下载中间件 下载器中间件是介于Scrapy的request/response处理的钩子框架. 是用于全局修改Scrapy request和response的一个轻量.底层 ...
- webpack4 系列教程(七): SCSS提取和懒加载
教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步>>> (原文)webpack4 系列教程(七): SCSS 提取和懒加载. 个人技术小站: https://god ...
- Miniconda安装scrapy教程
一.背景说明 前两天想重新研究下Scrapy,当时的环境是PyCharm社区版+Python 3.7.使用pip安装一直报错 “distutils.errors.DistutilsPlatformEr ...
- 第三百四十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—通过自定义中间件全局随机更换代理IP
第三百四十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—通过自定义中间件全局随机更换代理IP 设置代理ip只需要,自定义一个中间件,重写process_request方法, request ...
随机推荐
- 6423. 【NOIP2019模拟11.11】画
题目描述 Description Input Output Sample Input 3 2 3 3 6 5 1 2 1 3 Sample Output 15 Data Constraint 题解 迫 ...
- 新建com组件项目步骤
一.菜单栏 新建->项目->ATL->ATL项目->动态链接库 后续默认完成二.菜单栏 项目->添加类->ATL控件->“写入类的命名如:CeshiMai ...
- Redis配置文件全解
基本配置 port 6379 # 监听端口号,默认为 6379,如果你设为 0 ,redis 将不在 socket 上监听任何客户端连接. daemonize no #是否以后台进程启动 datab ...
- BZOJ 3168 Luogu P4100 [HEOI2013]钙铁锌硒维生素 (矩阵求逆、二分图匹配)
线性代数+图论好题. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=3168 (luogu) https://www.lu ...
- Django REST framework 之 认证 权限 限制
认证是确定你是谁 权限是指你有没有访问这个接口的权限 限制主要是指限制你的访问频率 认证 REST framework 提供了一些开箱即用的身份验证方案,并且还允许你实现自定义方案. 接下类我们就自己 ...
- 初识java虚拟机——JVM
1.Java程序运行过程 编写 编译 运行 过程如图所示: 2.JVM的认识 定义:JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚 ...
- List of Mozilla-Based Applications
List of Mozilla-Based Applications The following is a list of all known active applications that are ...
- 将项目发布到neuxs私服
需要在 pom.xml中配置 <distributionManagement> <repository> <id>user-release</id> & ...
- fiddler之入门(安装配置)
在工作中常常需要进行数据的抓包和发包,此时就可以用到fiddler这个工具了. fiddler是一个http协议调试代理工具,通过http代理,让数据从其通过,来坚挺本地计算机与访问网络之间的所有ht ...
- 网易云课堂_C++程序设计入门(下)_第7单元:出入虽同趣,所向各有宜 – 文件输入和输出_第7单元 - 作业2:编程互评
第7单元 - 作业2:编程互评 查看帮助 返回 提交作业(剩余10天) 完成并提交作业 作业批改 互评训练 互评作业 自评作业 成绩公布 查看成绩 由于在线编程不支持 ...