中间件

下载器中间件

写中间件

  1. from scrapy.http import HtmlResponse
  2. from scrapy.http import Request
  3.  
  4. class Md1(object):
  5. @classmethod
  6. def from_crawler(cls, crawler):
  7. # 此方法用于拿到当前的爬虫
  8. s = cls()
  9. return s
  10.  
  11. def process_request(self, request, spider):
  12. print('md1.process_request',request)
  13. return None # 返回如果是 空就会继续往下执行下一个中间件的 process_request 方法,如果一旦有返回值就要考虑情况
  14. """
  15. # 1. 返回 Response
  16. # 返回 Response 之后会往下执行 最后一个中间件的 process_response 方法
  17. # import requests
  18. # result = requests.get(request.url)
  19. # return HtmlResponse(url=request.url, status=200, headers=None, body=result.content)
  20.  
  21. # 2. 返回 Request
  22. # 返回 Request 之后 相当于无视了这次的请求 重新回到 调制器 那边,相当于又产生了新的任务
  23. # return Request('https://dig.chouti.com/r/tec/hot/1')
  24.  
  25. # 3. 抛出异常
  26. # 抛出异常 必须要 有 process_exception 方法进行捕捉异常,不然会报错
  27. # process_exception 方法在进行一系列的操作 在捕捉到异常的时候
  28. # from scrapy.exceptions import IgnoreRequest
  29. # raise IgnoreRequest
  30.  
  31. # 4. 对请求进行加工(*)
  32. # 通常我们都是用于对请求加工,然后再继续下面操作不返回东西
  33. # request.headers['user-agent'] = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
  34. # return None
  35. """
  36.  
  37. def process_response(self, request, response, spider):
  38. # Called with the response returned from the downloader.
  39.  
  40. # Must either;
  41. # - return a Response object # 返回一个 Response 来代替当前的 Response
  42. # - return a Request object # 返回一个 Request 开启新任务
  43. # - or raise IgnoreRequest # 返回一个 IgnoreRequest 进行异常捕捉
  44. print('m1.process_response',request,response)
  45. return response
  46.  
  47. def process_exception(self, request, exception, spider):
  48. # Called when a download handler or a process_request()
  49. # (from other downloader middleware) raises an exception.
  50.  
  51. # Must either:
  52. # - return None: continue processing this exception
  53. # 通常我们都是直接返回 None 就可以了
  54. # - return a Response object: stops process_exception() chain # 只要返回了 Response 当前的 process_exception 就不做操作了
  55. # 返回 Response 表示交给下一个 中间件的 process_exception 继续处理
  56. # - return a Request object: stops process_exception() chain # 只要返回了 Request 当前的 process_exception 就不做操作了
  57. # 返回 Request 放弃本次任务,新建任务
  58. pass

配置文件

  1. DOWNLOADER_MIDDLEWARES = {
  2. #'xdb.middlewares.XdbDownloaderMiddleware': 543,
  3. # 'xdb.proxy.XdbProxyMiddleware':751,
  4. 'xdb.md.Md1':666, # 依旧是 0-1000 越小越优先
  5. 'xdb.md.Md2':667,
  6. }

执行顺序梳理

  1. 调度器 下载器的时候先走 process_request(从第一个中间件往最后一个走) 然后如果根据返回情况进行判断接下来的方向
  2.   返回 None 继续下一个中间件的 process_request
  3.   返回 Response 进入 最后一个下载中间件的 process_response 流程
  4.   返回 Request 返回 调度器开启新任务
  5.   返回 异常 进入当前中间件的 process_exception 进行异常处理

  6. 下载器 还给 爬虫的时候要走 process_response(从最后一个中间件往第一个走)然后如果根据返回情况进行判断接下来的方向
  7.   返回 None 继续上一个中间件的 process_response
  8.   返回 Response 替换当前Response 进入上一个下载中间件的 process_response 流程
  9.   返回 Request 返回 调度器开启新任务 放弃当前的任务
  10.   返回 异常 进入当前中间件的 process_exception 进行异常处理

应用场景 - 随机 User-Agent

开源的组件  导入

  1. from fake_useragent import UserAgent

配置文件中设置选择方式

  1. RANDOM_UA_TYPE = "random"

根据配置文件中的选择方式设置模式

  1. class RandomUserAgentMiddlware(object):
  2. # 随机更换user-agent
  3. def __init__(self, crawler):
  4. super(RandomUserAgentMiddlware, self).__init__()
  5. self.ua = UserAgent()
  6. self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")
  7.  
  8. @classmethod
  9. def from_crawler(cls, crawler):
  10. return cls(crawler)
  11.  
  12. def process_request(self, request, spider):
  13. def get_ua():
  14. return getattr(self.ua, self.ua_type)
  15.  
  16. request.headers.setdefault('User-Agent', get_ua())

应用场景 - IP代理

写个脚本完成对 西刺代理IP的爬虫

并存入数据库

  1. # -*- coding: utf-8 -*-
  2. import requests
  3. from scrapy.selector import Selector
  4. import pymysql
  5.  
  6. conn = pymysql.connect(host="127.0.0.1", user="root", passwd="root", db="article_spider", charset="utf8")
  7. cursor = conn.cursor()
  8.  
  9. def crawl_ips():
  10. # 爬取西刺的免费ip代理
  11. headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0"}
  12. for i in range(1568):
  13. re = requests.get("http://www.xicidaili.com/nn/{0}".format(i), headers=headers)
  14.  
  15. selector = Selector(text=re.text)
  16. all_trs = selector.css("#ip_list tr")
  17.  
  18. ip_list = []
  19. for tr in all_trs[1:]:
  20. speed_str = tr.css(".bar::attr(title)").extract()[0]
  21. if speed_str:
  22. speed = float(speed_str.split("秒")[0])
  23. all_texts = tr.css("td::text").extract()
  24.  
  25. ip = all_texts[0]
  26. port = all_texts[1]
  27. proxy_type = all_texts[5]
  28.  
  29. ip_list.append((ip, port, proxy_type, speed))
  30.  
  31. for ip_info in ip_list:
  32. cursor.execute(
  33. "insert proxy_ip(ip, port, speed, proxy_type) VALUES('{0}', '{1}', {2}, 'HTTP')".format(
  34. ip_info[0], ip_info[1], ip_info[3]
  35. )
  36. )
  37.  
  38. conn.commit()
  39.  
  40. class GetIP(object):
  41. def delete_ip(self, ip):
  42. # 从数据库中删除无效的ip
  43. delete_sql = """
  44. delete from proxy_ip where ip='{0}'
  45. """.format(ip)
  46. cursor.execute(delete_sql)
  47. conn.commit()
  48. return True
  49.  
  50. def judge_ip(self, ip, port):
  51. # 判断ip是否可用
  52. http_url = "http://www.baidu.com"
  53. proxy_url = "http://{0}:{1}".format(ip, port)
  54. try:
  55. proxy_dict = {
  56. "http": proxy_url,
  57. }
  58. response = requests.get(http_url, proxies=proxy_dict)
  59. except Exception as e:
  60. print("invalid ip and port")
  61. self.delete_ip(ip)
  62. return False
  63. else:
  64. code = response.status_code
  65. if code >= 200 and code < 300:
  66. print("effective ip")
  67. return True
  68. else:
  69. print("invalid ip and port")
  70. self.delete_ip(ip)
  71. return False
  72.  
  73. def get_random_ip(self):
  74. # 从数据库中随机获取一个可用的ip
  75. random_sql = """
  76. SELECT ip, port FROM proxy_ip
  77. ORDER BY RAND()
  78. LIMIT 1
  79. """
  80. result = cursor.execute(random_sql)
  81. for ip_info in cursor.fetchall():
  82. ip = ip_info[0]
  83. port = ip_info[1]
  84.  
  85. judge_re = self.judge_ip(ip, port)
  86. if judge_re:
  87. return "http://{0}:{1}".format(ip, port)
  88. else:
  89. return self.get_random_ip()
  90.  
  91. # print (crawl_ips())
  92. if __name__ == "__main__":
  93. get_ip = GetIP()
  94. get_ip.get_random_ip()

设置中间件来调用脚本设置代理 IP

  1. class RandomProxyMiddleware(object):
  2. # 动态设置ip代理
  3. def process_request(self, request, spider):
  4. get_ip = GetIP()
  5. request.meta["proxy"] = get_ip.get_random_ip()

爬虫中间件

写中间件

  1. class Sd1(object):
  2. # Not all methods need to be defined. If a method is not defined,
  3. # scrapy acts as if the spider middleware does not modify the
  4. # passed objects.
  5.  
  6. @classmethod
  7. def from_crawler(cls, crawler):
  8. # This method is used by Scrapy to create your spiders.
  9. s = cls()
  10. return s
  11.  
  12. def process_spider_input(self, response, spider):
  13. # Called for each response that goes through the spider
  14. # middleware and into the spider.
  15.  
  16. # Should return None or raise an exception.
  17. return None
  18.  
  19. def process_spider_output(self, response, result, spider):
  20. # Called with the results returned from the Spider, after
  21. # it has processed the response.
  22.  
  23. # Must return an iterable of Request, dict or Item objects.
  24. for i in result:
  25. yield i
  26.  
  27. def process_spider_exception(self, response, exception, spider):
  28. # Called when a spider or process_spider_input() method
  29. # (from other spider middleware) raises an exception.
  30.  
  31. # Should return either None or an iterable of Response, dict
  32. # or Item objects.
  33. pass
  34.  
  35. # 只在爬虫启动时,执行一次。
  36. def process_start_requests(self, start_requests, spider):
  37. # Called with the start requests of the spider, and works
  38. # similarly to the process_spider_output() method, except
  39. # that it doesn’t have a response associated.
  40.  
  41. # Must return only requests (not items).
  42. for r in start_requests:
  43. yield r

配置文件

  1. SPIDER_MIDDLEWARES = {
  2. # 'xdb.middlewares.XdbSpiderMiddleware': 543,
  3. 'xdb.sd.Sd1': 666, # 同爬虫中间件一样的判断机制
  4. 'xdb.sd.Sd2': 667,
  5. }

执行流程

1. 第一次启动爬虫文件封装好 request 之后 走 process_start_requests 上传给引擎

2. 引擎将封装好的 request 给调度器

3. 调度器 继续执行 给下载器

4. 下载器 下载了内容之后给 引擎

5. 引擎再给 爬虫文件的时候要走 process_spider_input

6. 爬虫文件处理完之后如果有 yield 就要在走 process_spider_output 给引擎

应用

- 深度

- 优先级

信号

使用框架预留的位置,帮助你自定义一些功能

使用实例

  1. from scrapy import signals
  2.  
  3. class MyExtend(object):
  4. def __init__(self):
  5. pass
  6.  
  7. @classmethod
  8. def from_crawler(cls, crawler):
  9. self = cls()
  10.  
  11. crawler.signals.connect(self.x1, signal=signals.spider_opened) # 绑定信号发生时允许的函数
  12. crawler.signals.connect(self.x2, signal=signals.spider_closed)
  13.  
  14. return self
  15.  
  16. def x1(self, spider):
  17. print('open')
  18.  
  19. def x2(self, spider):
  20. print('close')
  1. # 信号可选类型 from scrapy import signals 中可以看到
  2. engine_started = object()
  3. engine_stopped = object()
  4.  
  5. spider_opened = object()
  6. spider_idle = object()
  7. spider_closed = object()
  8. spider_error = object()
  9.  
  10. request_scheduled = object()
  11. request_dropped = object()
  12. response_received = object()
  13. response_downloaded = object()
  14.  
  15. item_scraped = object()
  16. item_dropped = object()
  1. # settings.py
  2.  
  3. EXTENSIONS = {
  4. 'xdb.ext.MyExtend':666,
  5. }

定制命令

单爬虫运行

  1. import sys
  2. from scrapy.cmdline import execute
  3.  
  4. if __name__ == '__main__':
  5. execute(["scrapy","crawl","chouti","--nolog"])

所有爬虫

  1. - spiders同级创建任意目录,如:commands
  2. - 在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)
  3. - settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称'
  4. - 在项目目录执行命令:scrapy crawlall
  1. # crawlall.py
  2.  
  3. from scrapy.commands import ScrapyCommand
  4. from scrapy.utils.project import get_project_settings
  5.  
  6. class Command(ScrapyCommand):
  7.  
  8. requires_project = True
  9.  
  10. def syntax(self):
  11. return '[options]'
  12.  
  13. def short_desc(self):
  14. return 'Runs all of the spiders'
  15.  
  16. def run(self, args, opts):
  17. spider_list = self.crawler_process.spiders.list()
  18. for name in spider_list:
  19. self.crawler_process.crawl(name, **opts.__dict__)
  20. self.crawler_process.start()
  1. # settings.py
  2.  
  3. COMMANDS_MODULE = "xdb.commands"

爬虫暂停重启

原理

爬虫的暂停重启是需要文件支持

在启动的命令里选择一个路径

不同的爬虫不能共用,

相同的爬虫如果公用同一个就会给予这个文件的上一次状态继续爬取

该命令的中断命令是基于 windows  Ctrl+c / 杀进程 或者 Linux 里面的  kill -f -9 main.py

因此在 pycharm 中的中断是做不到的, 只能在命令行中处理

  1. scrapy crawl lagou -s JOBDIR=job_info/001

配置文件方式

指定 文件路径可以在 settings.py 中设置

这样就是全局设置了

  1. JOBDIR="job_info/001"

或者在单爬虫类中设置

  1. cutom_settings = {
  2. "JOBDIR": "job_info/001"
  3. }

总结

但是还是和上面的说法一样.....pycharm 里面没办法中断, 因此还是没有啥意义,

还是只能使用命令行方式

Scrapy 框架 中间件,信号,定制命令的更多相关文章

  1. scrapy框架中间件配置代理

    scrapy框架中间件配置代理import random#代理池PROXY_http = [ '106.240.254.138:80', '211.24.102.168:80',]PROXY_http ...

  2. Scrapy 框架 中间件 代理IP 提高效率

    中间件 拦截请求跟响应 进行ua(User-Agent ) 伪装 代理 IP 中间件位置: 引擎 和下载器 中间 的中间件 ( 下载中间件) 引擎 跟 spider 中间 的中间件 ( 爬虫中间件)( ...

  3. Scrapy框架-中间件

    一.中间件中主要有3个函数方法 process_request:处理请求,默认返回值是None process_response:处理响应,默认返回值是response对象 process_exce ...

  4. 爬虫 之 scrapy框架

    浏览目录 介绍 安装 项目结构及爬虫应用简介 常用命令行工具 Spiders爬虫 Selectors选择器 Item Pipeline 项目管道 Downloader Middleware下载中间件 ...

  5. Scrapy框架——介绍、安装、命令行创建,启动、项目目录结构介绍、Spiders文件夹详解(包括去重规则)、Selectors解析页面、Items、pipelines(自定义pipeline)、下载中间件(Downloader Middleware)、爬虫中间件、信号

    一 介绍 Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前Scrapy的用途十分广泛,可 ...

  6. python 全栈开发,Day138(scrapy框架的下载中间件,settings配置)

    昨日内容拾遗 打开昨天写的DianShang项目,查看items.py class AmazonItem(scrapy.Item): name = scrapy.Field() # 商品名 price ...

  7. scrapy框架之下载中间件

    介绍 中间件是Scrapy里面的一个核心概念.使用中间件可以在爬虫的请求发起之前或者请求返回之后对数据进行定制化修改,从而开发出适应不同情况的爬虫. “中间件”这个中文名字和前面章节讲到的“中间人”只 ...

  8. Scrapy框架的命令行详解【转】

    Scrapy框架的命令行详解 请给作者点赞 --> 原文链接 这篇文章主要是对的scrapy命令行使用的一个介绍 创建爬虫项目 scrapy startproject 项目名例子如下: loca ...

  9. scrapy框架的命令行解释

    scrapy框架的命令解释 创建爬虫项目 scrapy startproject 项目名例子如下: scrapy startproject test1 这个时候爬虫的目录结构就已经创建完成了,目录结构 ...

随机推荐

  1. JS时间的获取及格式

    最近在做一个web聊天室,一个时间的问题挡住了进程,只好全网大搜索,将实用的方法记录下来,以备后查 <script src="/static/bootstrap/js/jquery.m ...

  2. VS根据数据库生成实体类

    一.在类库项目上添加新项 二. 三.依次填入数据库连接 选择数据库 就可以生成数据库实体

  3. macOS 安装 Java (Homebrew)

    macOS 安装多个 Java 版本 Homebrew 是 macOS 下的一个非常好用的包管理工具, caskroom 则是基于 Homebrew 构建的一个强大的应用程序管理器. Homebrew ...

  4. jsp 简单下载

    <%@ page language="java" import="java.util.*" contentType="text/html;cha ...

  5. 持续代码质量管理-SonarQube Scanner部署

    1. SonarQube Scanner地址 上一篇文章我们安装了SonarQube-7.3,让我们可以在页面查看代码质量.但是具体的扫描工作则需要SonarQube Scanner完成. 下载页面 ...

  6. 5分钟了解TypeScript

    1.安装TypeScript 有两种方式安装TypeScript: Via npm 通过安装VS插件,更多可参见这里. 对于npm用户,可以直接使用下面的命令行安装: nmp install -g T ...

  7. 网络编程_tcp与dup协议简单应用

    老师的博客:http://www.cnblogs.com/Eva-J/articles/8066842.html 计算机网络基础 :http://www.cnblogs.com/Eva-J/artic ...

  8. KAPTCHA验证码使用步骤

    使用kaptcha可以方便的配置: · 验证码的字体 · 验证码字体的大小 · 验证码字体的字体颜色 · 验证码内容的范围(数字,字母,中文汉字!) · 验证码图片的大小,边框,边框粗细,边框颜色 · ...

  9. GIL:全局解释器锁 VS 用户程序锁

    既然有了GIL锁,CPython还要多线程干什么? ''' GIL:全局解释器锁的来历 四核:同一时刻真正有四个任务在运行,多核的意义在于此 单核:看上去是并发的,因为进行了上下文切换,单核永远是串行 ...

  10. Javascrip 入门第三节课

    一.location对象 location.href 获取当前网页的URLlocation.search() 获取?之后的请求信息 location.href="URL" // 跳 ...