刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • Python基础

  • Python进阶

  • Python并发编程

  • Django

  • Flask

  • 爬虫

    • 认识爬虫& request
    • bs4 和 xpath
    • 代理-cookie-验证码识别-模拟登录
    • 线程池-协程-aiohttp-selenium
    • 移动端数据爬取-scrapy框架
    • scrapy框架-媒体文件爬取-middleware
      • 1.scrapy框架
        • 1.1 安装scrapy
        • 1.2 简单使用
        • 1.3 配置文件
        • 1.4 使用方法
        • 1.5 持久化存储
        • 1.6 发送请求
        • 1.7 scrapy五大核心组件的工作流程
      • 2. scrapy 图片数据的爬取
      • 3.请求传参实现深度爬取
      • 4.下载中间件的应用
        • 4.1 拦截请求中间件
        • 4.2 拦截响应中间件
      • 5.提高scrapy爬取效率配置
    • 基于CrawlSpider全栈数据爬取,分布式爬虫
  • Python
  • 爬虫
bigox
2021-03-22
目录

scrapy框架-媒体文件爬取-middleware

# 1.scrapy框架

  • 框架:- 就是一个集成了各种功能且具有很强通用性(可以被应用在各种不同的需求中)的一个项目模板.
  • scrapy 框架集成了哪些功能:
    • 高性能的数据解析操作,持久化存储操作,高性能的数据下载的操作.

# 1.1 安装scrapy

  1. pip3 install wheel

  2. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

  3. 进入下载目录,执行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl

  4. pip3 install pywin32

  5. pip3 install scrapy

# 1.2 简单使用

scrapy创建需要在终端执行命令

  • scrapy startproject proName 创建项目
  • cd proNme 进入项目文件夹
  • scrapy genspider spiderName www.xxx.com 创建一个爬虫文件

# 1.3 配置文件

  • 不遵从robots协议

    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    
    1
    2
  • 进行UA伪装

    USER_AGENT = '浏览器USER_AGENT'
    
    1
  • 进行日志等级设定:

    LOG_LEVEL = 'ERROR'

# 1.4 使用方法

import scrapy
class FirstSpider(scrapy.Spider):
    # 爬虫文件的名称:爬虫文件的唯一标识(在spiders子目录下是可以创建多个爬虫文件)
    name = 'first'
    # 允许的域名,一般注释掉
    allowed_domains = ['www.baidu.com']
    # 起始的url列表:列表中存放的url会被scrapy自动的进行请求发送
    start_urls = ['https://www.baidu.com/', 'https://www.sogou.com/']

    # 用作于数据解析:将start_urls列表中对应的url请求成功后的响应数据进行解析
    def parse(self, response):
        print(response)
1
2
3
4
5
6
7
8
9
10
11
12
  • 项目启动命令
    • scrapy crawl pro_name

# 1.5 持久化存储

  • 基于终端指令:

    • 特性:只可以将parse方法的返回值存储到本地的磁盘文件中
    • 存储指令: scrapy crawl spiderName -o filePath
    class XiaopapaSpider(scrapy.Spider):
        name = 'qiubai'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://www.qiushibaike.com/text/']
    
        # 基于终端指令的持久化存储操作
        def parse(self, response):
            div_list = response.xpath('//*[@id="content-left"]/div')
            all_data = []
            for div in div_list:
                # scrapy中的xpath返回的列表的列表元素一定是Selector对象,我们最终想要的解析的数据一定是存储在该对象中
                # extract()将Selector对象中data参数的值取出
                # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()  # 取第一个
                author = div.xpath('./div[1]/a[2]/h2/text()').extract_first() # 也是取第一个
                # 列表直接调用extract表示的是将extract作用到每一个列表元素中
                content = div.xpath('./a[1]/div/span//text()').extract()
                content = ''.join(content)
                dic = {'author': author,
                    'content': content }
                all_data.append(dic)
            return all_data
        
    # response.xpath("xpath 路径")  返回的列表的列表元素是Selector对象,数据存在该对象当中
    # extract()将Selector对象中data参数的值取出
    # .extract_first() # 取第一个值
    # 列表直接调用extract表示的是将extract作用到每一个列表元素中,返回的是一个列表
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

​

  • 基于管道:实现流程

    1.数据解析 2.在item类中定义相关的属性 3.将解析的数据存储或者封装到一个item类型的对象(items文件中对应类的对象) 4.向管道提交item 5.在管道文件的process_item方法中接收item进行持久化存储 6.在配置文件中开启管道

    # 管道中需要注意的细节:
        - 配置文件中开启管道对应的配置是一个字典,字典中的键值表示的就是某一个管道
        - 在管道对应的源文件中其实可以定义多个管道类。一个管道类对应的是一种形式的持久化存储
        - 在process_item方法中的return item表示的是将item提交给下一个即将被执行的管道类
        - 爬虫文件通过yield item只可以将item提交给第一个(优先级最高)被执行的管道
    
    1
    2
    3
    4
    5
  • 将同一份数据持久化到不同的平台中

    • 分析:
      • 1.管道文件中的一个管道类负责数据的一种形式的持久化存储
      • 2.爬虫文件向管道提交的item只会提交给优先级最高的那一个管道类
      • 3.在管道类的process_item中的return item表示的是将当前管道接收的item返回/提交给 下一个即将被执行的管道类
    # 基于管道的持久化存储
    # 爬虫.py文件,
    def parse(self, response):
        div_list = response.xpath('//*[@id="content-left"]/div')
        all_data = []
        for div in div_list:
            #scrapy中的xpath返回的列表的列表元素一定是Selector对象,我们最终想要的解析的
            #数据一定是存储在该对象中
            #extract()将Selector对象中data参数的值取出
            # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
            author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
            #列表直接调用extract表示的是将extract作用到每一个列表元素中
            content = div.xpath('./a[1]/div/span//text()').extract()
            content = ''.join(content)
            #将解析的数据存储到item对象
            item = QiubaiproItem()
            item['author'] = author
            item['content'] = content
            #将item提交给管道
            yield item #item一定是提交给了优先级最高的管道类
            
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    • items.py文件
    # items.py文件
    class QiubaiproItem(scrapy.Item):
        author = scrapy.Field() #Field可以将其理解成是一个万能的数据类型
        content = scrapy.Field()
    
    1
    2
    3
    4
    • pipelines.py文件
      • 存储到不同的数据平台中
    # pipelines.py文件
    #存储到文件中
    class QiubaiproPipeline(object):
        fp = None
        def open_spider(self,spider): # 重写父类方法,只会执行一次,打开文件
            print('开始爬虫!')
            self.fp = open('qiushibaike.txt','w',encoding='utf-8')
    
        #使用来接收爬虫文件提交过来的item,然后将其进行任意形式的持久化存储
        #参数item:就是接收到的item对象
        #该方法每接收一个item就会调用一次
        def process_item(self, item, spider):
            author = item['author']
            content= item['content']
    
            self.fp.write(author+':'+content+'\n')
            return item #item是返回给了下一个即将被执行的管道类
    
        def close_spider(self,spider): # 重写父类方法,只会执行一次,关闭文件
            print('结束爬虫!')
            self.fp.close()
    
    #负责将数据存储到mysql
    class MysqlPL(object):
        conn = None
        cursor = None
        def open_spider(self,spider):
            self.conn = pymysql.Connect(host='127.0.0.1',
                                        port=3306,
                                        user='root',
                                        password='123',
                                        db='spider',
                                        charset='utf8')
            print(self.conn)
        def process_item(self,item,spider):
            author = item['author']
            content = item['content']
            sql = 'insert into qiubai values ("%s","%s")'%(author,content)
            self.cursor = self.conn.cursor()
            try:
                self.cursor.execute(sql)
                self.conn.commit()
            except Exception as e:
                print(e)
                self.conn.rollback()
            return item
        def close_spider(self,spider):
            self.cursor.close()
            self.conn.close()
            
    # 存到redis
    class RedisPL(object):
        conn = None
        def open_spider(self,spider):
            self.conn = Redis(host='127.0.0.1',port=6379)
            print(self.conn)
        def process_item(self,item,spider):
            self.conn.lpush('all_data',item)
            #注意:如果将字典写入redis报错:pip install -U redis==2.10.6
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    • settings文件
      • 注册定义的管道
    ITEM_PIPELINES = {
        'qiubaiPro.pipelines.QiubaiproPipeline': 300,  # 300表示的是优先级,数字越小,优先级就越大
        'qiubaiPro.pipelines.MysqlPL': 301,
        'qiubaiPro.pipelines.RedisPL': 302,
    }
    
    1
    2
    3
    4
    5

# 1.6 发送请求

1.6.1 自动请求发送:

```python

def start_requests(self): for url in self.start_urls: yield scrapy.Request(url,callback=self.parse) ```

1.6.2 手动发送请求:

  • 在scrapy中如何进行手动请求发送(GET)
    • 使用场景:爬取多个页码对应的页面源码数据
    • yield scrapy.Request(url,callback)
  • 在scrapy中如何进行手动请求发送(POST) data = { #post请求的请求参数 'kw':'aaa' } yield scrapy.FormRequest(url,formdata=data,callback)
#将多个页码对应的页面数据进行爬取和解析的操作

url = 'https://www.qiushibaike.com/text/page/%d/'#通用的url模板
pageNum = 1
#parse第一次调用表示的是用来解析第一页对应页面中的段子内容和作者
def parse(self, response):
    div_list = response.xpath('//*[@id="content-left"]/div')
    all_data = []
    for div in div_list:
        author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
        # 列表直接调用extract表示的是将extract作用到每一个列表元素中
        content = div.xpath('./a[1]/div/span//text()').extract()
        content = ''.join(content)

        # 将解析的数据存储到item对象
        item = QiubaiproItem()
        item['author'] = author
        item['content'] = content

        # 将item提交给管道
        yield item  # item一定是提交给了优先级最高的管道类
        
   if self.pageNum <= 5:
       self.pageNum += 1
       new_url = format(self.url%self.pageNum)
       #手动请求(get)的发送
       yield scrapy.Request(new_url,callback=self.parse)  # 递归调用parse方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 1.7 scrapy五大核心组件的工作流程

  • 引擎(Scrapy)
    • 用来处理整个系统的数据流处理, 触发事务(框架核心)
  • 调度器(Scheduler)
  • 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
  • 下载器(Downloader)
    • 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
  • 爬虫(Spiders)
    • 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
  • 项目管道(Pipeline)
    • 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

​ ![1567386219292](C:\Users\big cattle\AppData\Roaming\Typora\typora-user-images\1567386219292.png)

# 2. scrapy 图片数据的爬取

  • 基于scrapy进行图片数据的爬取:

    • 在爬虫文件中只需要解析提取出图片地址,然后将地址提交给管道
    • 配置文件中写入文件存储位置:IMAGES_STORE = './imgsLib'
    • 在管道文件中进行管道类的制定:
      • 1.from scrapy.pipelines.images import ImagesPipeline
      • 2.将管道类的父类修改成ImagesPipeline
      • 3.重写父类的三个方法
  • 校花网爬取示例

    • spider.py文件

      import scrapy
      from imgspider.items import ImgspiderItem
      
      
      class ImgSpiderSpider(scrapy.Spider):
          name = 'img_spider'
          # allowed_domains = ['www.xxx.com']
          start_urls = ['http://www.521609.com/daxuemeinv/']
          url = 'http://www.521609.com/daxuemeinv/list8%d.html'
          pageNum = 1
      
          def parse(self, response):
              li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
              # 拼接图片url
              for li in li_list:
                  print(self.pageNum)
                  img_src = 'http://www.521609.com' + li.xpath('./a[1]/img/@src').extract_first()
                  item = ImgspiderItem()
                  item['src'] = img_src
                  yield item
      
                  if self.pageNum < 3:
                      self.pageNum += 1
                      new_url = format(self.url % self.pageNum)
                      yield scrapy.Request(new_url, callback=self.parse)
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
    • pipelines.py文件

      import scrapy
      from imgspider.items import ImgspiderItem
      
      
      class ImgSpiderSpider(scrapy.Spider):
          name = 'img_spider'
          # allowed_domains = ['www.xxx.com']
          start_urls = ['http://www.521609.com/daxuemeinv/']
          url = 'http://www.521609.com/daxuemeinv/list8%d.html'
          pageNum = 1
      
          def parse(self, response):
              li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
              # 拼接图片url
              for li in li_list:
                  print(self.pageNum)
                  img_src = 'http://www.521609.com' + li.xpath('./a[1]/img/@src').extract_first()
                  item = ImgspiderItem()
                  item['src'] = img_src
                  yield item
      
                  if self.pageNum < 3:
                      self.pageNum += 1
                      new_url = format(self.url % self.pageNum)
                      yield scrapy.Request(new_url, callback=self.parse)
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25

# 3.请求传参实现深度爬取

  • 请求传参:

    • 实现深度爬取:爬取多个层级对应的页面数据

    • 使用场景:爬取的数据没有在同一张页面中

    • 在手动请求的时候传递item:yield scrapy.Request(url,callback,meta={'item':item})

      • 将meta这个字典传递给callback
      • 在callback中接收meta:item = response.meta['item']
        def parse(self, response):
            li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
            for li in li_list:
                title = li.xpath('./div[1]/a/@title').extract_first()
                detail_url = 'https://www.4567tv.tv' + li.xpath('./div[1]/a/@href').extract_first()
                item = MoviespiderItem()
                item['title'] = title
                # meta参数是一个字典,该字典就可以传递给callback指定的回调函数
                yield scrapy.Request(detail_url, callback=self.parse_detail, meta={"item": item})
    
        def parse_detail(self, response):
            # 接收meta:response.meta
            item = response.meta['item']
            desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
            item["desc"] = desc
            yield item
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# 4.下载中间件的应用

  • scrapy中间件有:-爬虫中间件,下载中间件
  • 下载中间件应用较多
  • 下载中间件:
    • 作用:批量拦截请求和响应
    • 拦截请求:
    • UA伪装:将所有的请求尽可能多的设定成不同的请求载体身份标识
      • request.headers['User-Agent'] = 'xxx'
    • 代理操作:request.meta['proxy'] = 'http://ip:port'
    • 拦截响应:篡改响应数据或者直接替换响应对象

# 4.1 拦截请求中间件

  • 作用:

    • UA伪装:将所有的请求尽可能多的设定成不同的请求载体身份标识
      • request.headers['User-Agent'] = 'xxx'
    • 代理操作:request.meta['proxy'] = 'http://ip:port'
  • 爬取4567视频网示例:

    • spider.py文件
    import scrapy
    from moviespider.items import MoviespiderItem
    
    class MovieSpiderSpider(scrapy.Spider):
        name = 'movie_spider'
        # allowed_domains = ['https://www.4567tv.tv/index.php/vod/show/class/动作/id/1.html']
        start_urls = ['https://www.4567tv.tv/index.php/vod/show/class/动作/id/1.html']
        url = 'https://www.4567tv.tv/index.php/vod/show/class/动作/id/1/page/%d.html'
        pageNum = 1
    
        def parse(self, response):
            li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
            for li in li_list:
                title = li.xpath('./div[1]/a/@title').extract_first()
                detail_url = 'https://www.4567tv.tv' + li.xpath('./div[1]/a/@href').extract_first()
                item = MoviespiderItem()
                item['title'] = title
                # meta参数是一个字典,该字典就可以传递给callback指定的回调函数
                yield scrapy.Request(detail_url, callback=self.parse_detail, meta={"item": item})
    
        def parse_detail(self, response):
            # 接收meta:response.meta
            item = response.meta['item']
            desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
            item["desc"] = desc
            yield item
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    • items.py

      • 创建title和desc的属性
    • pipelines.py文件

      • 存储
    • middleware.py

      • downloadmiddleware文件
      
      from scrapy import signals
      import random
      
      user_agent_list = [
          "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
          "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
      ]  # 常见浏览器user_agent
      
      PROXY_http = [
          '153.180.102.104:80',
      ]
      PROXY_https = [
          '120.83.49.90:9000',
      ]
      
      
      class MoviespiderDownloaderMiddleware(object):
      
          # 拦截正常的请求,参数request就是拦截到的请求对象
          def process_request(self, request, spider):
              print('i am process_request()')
              # 实现:将拦截到的请求尽可能多的设定成不同的请求载体身份标识
              request.headers['User-Agent'] = random.choice(user_agent_list)
              # 代理操作
              if request.url.split(':')[0] == 'http':
                  request.meta['proxy'] = 'http://' + random.choice(PROXY_http)  # http://ip:port
              else:
                  request.meta['proxy'] = 'https://' + random.choice(PROXY_https)  # http://ip:port
      
              return
      
          # 拦截响应:参数response就是拦截到的响应
          def process_response(self, request, response, spider):
              print('i am process_response()')
              return response
      
          def process_exception(self, request, exception, spider):
              print('i am process_exception()')
              # 拦截到异常的请求然后对其进行修正,然后重新进行请求发送
              # 代理操作
              if request.url.split(':')[0] == 'http':
                  request.meta['proxy'] = 'http://' + random.choice(PROXY_http)  # http://ip:port
              else:
                  request.meta['proxy'] = 'https://' + random.choice(PROXY_https)  # http://ip:port
      
              return request  # 将修正之后的请求进行重新发送
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
  • 常用浏览器user_agent

    user_agent_list = [
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
        "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
        "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
        "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
        "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
        "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
        "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
        "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
        "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
        "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
        "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
    ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

# 4.2 拦截响应中间件

  • 作用:拦截响应可以篡改响应数据或者直接替换响应对象

  • selenium在scrapy中的应用:

    • 实例化浏览器对象:写在爬虫类的构造方法中
    • 关闭浏览器:爬虫类中的closed(self,spider)关闭浏览器
    • 在中间件中执行浏览器自动化的操作
  • 示例:

    • 需求; 爬取网易新闻的国内,国际,军事,航空,无人机这五个板块下对应的新闻标题和内容(数据是动态加载的)

    • spider.py文件

      # -*- coding: utf-8 -*-
      import scrapy
      from selenium import webdriver
      from wangyinews.item import WangyinewsItem
      
      class NewsSpider(scrapy.Spider):
          name = 'news'
          # allowed_domains = ['www.wangyi.com']
          start_urls = ['https://news.163.com']
          five_model_urls = []
          bro = webdriver.Chrome(executable_path=r'D:\教学视频\python 爬虫\tools\chromedriver.exe')
      
          # 用来解析五个板块对应的url,然后对其进行手动请求发送
          def parse(self, response):
              model_index = [3, 4, 6, 7, 8]
              li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
              for index in model_index:
                  li = li_list[index]
                  # 获取了五个板块对应的url
                  model_url = li.xpath('./a/@href').extract_first()
                  self.five_model_urls.append(model_url)
                  # 对每一个板块的url进行手动i请求发送
                  yield scrapy.Request(model_url, callback=self.parse_model)
      
          # 解析每一个板块页面中的新闻标题和新闻详情页的url
          # 问题:response(不满足需求的response)中并没有包含每一个板块中动态加载出的新闻数据
          def parse_model(self, response):
              div_list = response.xpath('/html/body/div[1]/div[3]/div[4]/div[1]/div/div/ul/li/div/div')
              for div in div_list:
                  title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
                  detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
                  item = WangyinewsItem()
                  item['title'] = title
                  # 对详情页发起请求解析出新闻内容
                  yield scrapy.Request(detail_url, callback=self.parse_new_content, meta={'item': item})
      
          # 获取内容详情
          def parse_new_content(self, response):
              item = response.meta["item"]
              content = response.xpath('//*[@id="endText"]//text()').extract()
              item["content"] = content
              yield item
      
          # 最后执行,关闭bro
          def close(self, spider):
              self.bro.quit()
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
    • items.py

      • 创建title和desc的属性
    • pipelines.py文件

      • 存储
    • middleware.py文件:

      # -*- coding: utf-8 -*-
      
      # Define here the models for your spider middleware
      #
      # See documentation in:
      # https://docs.scrapy.org/en/latest/topics/spider-middleware.html
      from time import sleep
      
      from scrapy import signals
      from scrapy.http import HtmlResponse
      
      
      class WangyinewsDownloaderMiddleware(object):
      
          def process_request(self, request, spider):
              return None
      
          def process_response(self, request, response, spider):
              # spider就是爬虫文件中爬虫类实例化的对象
              # 进行所有响应对象的拦截
              # 1.将所有的响应中那五个不满足需求的响应对象找出
              # 1.每一个响应对象对应唯一一个请求对象
              # 2.如果我们可以定位到五个响应对应的请求对象后,就可以通过该i请求对象定位到指定的响应对象
              # 3.可以通过五个板块的url定位请求对象
              # 总结: url==》request==》response
              # 2.将找出的五个不满足需求的响应对象进行修正(替换)
              # spider.five_model_urls:五个板块对应的url
              bro = spider.bro
              if request.url in spider.five_model_urls:
                  bro.get(request.url)
                  sleep(1)
                  page_text = bro.page_source  # 包含了动态加载的新闻数据
                  # 如果if条件程利则该response就是五个板块对应的响应对象
                  new_responde = HtmlResponse(url=request.url, body=page_text, encoding="utf-8", request=request)
                  return new_responde
              return response
      
          def process_exception(self, request, exception, spider):
              pass
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40

# 5.提高scrapy爬取效率配置

#增加并发:
    默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。

#降低日志级别:
    在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’。

#禁止cookie:
    如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False。

#禁止重试:
    对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False。

#减少下载超时:
    如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#Python#
上次更新: 2023/04/16, 18:35:33
移动端数据爬取-scrapy框架
基于CrawlSpider全栈数据爬取,分布式爬虫

← 移动端数据爬取-scrapy框架 基于CrawlSpider全栈数据爬取,分布式爬虫→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式