爬虫---10.scrapy框架(壹-初接触)

  • 所谓框架就是一个被集成了很多功能且具有很强通用性的项目模板

  • 学习框架就是学习集成好的各种功能的特性和作用 进阶探索框架的底层

  • scrapy是一个专门用于异步爬虫的框架。

    • 高性能的数据分析、请求发送、持久化存储、全站数据爬取、中间件,分布式...
  • 环境安装

    • mac和linux pip install scrapy
    • window分5个步骤
      • 1.pip3 install wheel
      • 2.下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#teisted
        1,2步是为第3步服务的 twisted就是一个异步的架构
      • 3.安装twisted
        进入下载目录 执行 pip install Twisted-20.3.0-cp38-cp38m-win_amd64.whl
        twisted是异步架构 被作用在scrapy中
        如果安装报错 需要更换另一版本安装即可
      • 4.pip install pywin32
      • 5.pip install scrapy
      • 6.测试 在cmd中scrapy,如果没报错,安装成功
  • scrapy的基本使用

    • 创建一个工程

      • Console 是python控制台 Terminal是终端 命令行模式
      • 执行 scrapy startproject ProName(项目名,在选定的目录下创建)
      • 目录结构
        • spiders 文件夹叫爬虫目录 必须存放一个爬虫源文件
        • settings.py 工程的配置文件
      • cd 到ProName目录 创建一个爬虫源文件
        • scrapy genspider spiderName www.xxx.com(生成一个spiderName.py文件,源文件创造好了)
      • 编写对应的代码在爬虫文件中(后继操作,暂时不用)
    • 执行工程

      • scrapy crawl spiderName 是创建的py文件
        • 执行工程后 默认会输出工程所有的日志信息
      • 指定类型日志的输出:
        • 1.settings.py中加入 LOG_LEVEL = 'ERROR' 打印日志会消失
        • 2.在settings.py中 ROBOTSTXT_OBEY=False 要改成True 不改的话,目前测试https能返回正常response http不能返回
        • 3.可以把User-Agent写在配置文件中
        • 最后 重新执行scrapy crawl spiderName 要在ProName路径下执行
      • 碰到了连接超时问题 暂时没解决
        • windows下 其实是自己创建的网址无法访问报错 拓展知识
          • netstat -ano|findstr 端口号 (查看在用端口) 随后 taskkill /pid PID(进程号) /f 杀掉端口
    • 爬虫文件内容阐述

      • name 爬虫文件名称 该文件的唯一标识
      • start_urls 起始url列表 存储的都是url url可以被自动进行get请求的发送
      • parse方法 请求后的数据解析操作
    • 文件说明

      • scrapy.cfg 项目的配置信息,主要为Scrapy命令行工具提供一个基础的配置信息。(真正爬虫相关的配置信息在settings.py文件中)
      • items.py 设置数据存储模板,用于结构化数据,如:Django的Model
      • pipelines 数据处理行为,如:一般结构化的数据持久化
      • settings.py 配置文件,如:递归的层数、并发数,延迟下载等
        = spiders 爬虫目录,如:创建文件,编写爬虫规则
    • settings.py 需要做的操作

      • 1.禁止robots ROBOTSTXT_OBEY = False
      • 2.指定日志类型 LOG_LEVEL = 'ERROR'
      • 3.UA伪装 USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE'
    • 数据解析

      • 用scrapy自己的xpath做解析 代码写完后 在终端执行scrapy crawl spidername
      • 得到的结果是selector对象 需要的字符串对象被存储在data属性中
      • 取出方式 变量名2 = 变量名1.xpath()[0].extract() extract就是将属性值取出
      • 正式书写是 xpath().extract_first() 意思是将列表中第一个元素表示的selector对象中的data值取出
      • 直接使用列表调用extract() 可以将列表中每个列表元素表示的selector中的data取出 结果为列表
    • 持久化存储

      • 基于终端指令:

        • 局限:该种方式只可以将parse方法的返回值存储到本地指定后缀的文本文件中
        • 命令:scrapy crawl spiderName -o ./qiubai.csv
        • 注意:持久化存储对应的文本文件类型只能为:json jsonlines jl csv xml marshal pickle
        • 优点:简洁便捷高效
        简单代码 点击查看
           class FirstSpider(scrapy.Spider):
           # 爬虫文件名称: 当前源文件的唯一标识
           name = 'duanzi'
        
           # 允许的域名 这里是做域名限定的 所以通常会把allowed_domains注释掉
           # allowed_domains = ['www.baidu.com']
        
           # 起始的url列表 只可以存储url
           # 作用:列表中存储的url都会被进行get请求的发送
           start_urls = ['https://duanzixing.com/微段子/']
        
           # 数据解析 parse方法调用的次数完全取决于请求的次数 response表示的就是服务器返回的响应对象
        
           # 将解析到的数据做持久化存储
        
           def parse(self, response):
               article_list = response.xpath('/html/body/section/div/div/article')
               all_data = []
               for article in article_list:
               # title = article.xpath('.//h2/a/text()')[0].extract()
               title = article.xpath('.//h2/a/text()').extract_first()
        
           dic = {
              'title': title,
           }
           all_data.append(dic)
        
           return all_data
        
           # 终端执行 scrapy crawl duanzi -o duanzi.csv
        
      • 基于管道(重点)(步骤):

        • 1.在爬虫文件中进行数据解析

        • 2.在item.py中定义相关的属性

          • 步骤1中解析出了几个字段的数据 在此就定义几个属性
            Filed()定义好的属性当做是一个万能类型的属性
            name=scrapy.Field()
        • 3.在爬虫文件中将解析的数据封装存储到item类型的对象中

          • 在代码文件中 既scrapy genspider spiderName www.xxx.com创建的文件中
            from 文件夹名称.items import items文件中的类名

          • 随后实例化一个item类型的对象 将解析到的数据存储到该对象中
            在类方法中 item = items中的类名()
            注意 item调用属性不能用. 只能用item[''] = 获取到的数据
            如 item['title'] = title

            操作实例 点击查看
              def parse(self, response):
                  all_data = []
                  # 数据解析名称和内容 
                  article_lst = response.xpath('')
                  for article in article_lst:
                      title = article.xpath().extract_first()
                      # 实例化一个item类型的对象 将解析到的数据存储到该对象中
                      item = duanziproItem()
                      # 不可以通过.的形式调用属性
                      item['title'] = title
            
            
        • 4.将item类型的对象提交给管道进行持久化存储的操作
          在sprid文件的parse方法中 最后录入 yield item 是将item对象提交给管道

        • 5.在管道文件(pipelines.py)中, 接收爬虫文件提交过来的Item类型对象,且对其进行任意形式的持久化存储操作
          工程里只能有一个管道文件, 但是可以有多个管道类

          简单代码 点击查看
              # 文件中有管道类 管道类的的方法是用来接受item对象的 
              # 一次只能接收一个item 说明该方法会被调用多次
              # 参数item就是接受到的tiem对象
          
              # 如果有多个爬虫文件 不能执行一个就打开一次文件 所以要重写父类的2个方法 让文件只打开一次 写入多次
              def open_spider(self, spider):
                  print('我是open_spider(), 我只会在爬虫开始的时候执行一次')
                  self.fp = open('xx.txt', 'w', encoding='utf-8')
                                        
                  # 文件需要关闭 所以需要重写父类的另一个方法
                  def close_spider(self, spider):
                      print("我是close_spider(), 我只会在爬虫结束的时候执行一次!")
                      self.fp.close()
          
                  def process_item(self, item, spider):
                      print(item)          # item其实就是一个字典
                      self.fp.write(item['title']+'
          ')
                      # 将item存储到文件
                      return item
          
        • 6.在配置文件中开启管道机制
          在配置文件中有 ITEM_PIPELINES = {'XXX.pipelines.xxxPipeline': 300,}
          300表示优先级,数字越小 优先级越高

      • 基于管道实现数据的备份

        • 将爬取到的数据分别存储到不同的载体
        • 实现:将数据一份存储到mysql 一份存储到redis
        • 问题:管道文件中的一个管道类表示怎样一组操作?
          • 一个管道类对应一种形式的持久化存储操作 如果将数据存储到不同的载体中就需要使用多个管道类。
        • 定义好了三个管道类 将数据写入到三个载体中进行存储
          • item不会依次提交给三个管道类

          • 爬虫文件中的item只会被提交给优先级最高的那一个管道类

          • 优先级高的管道类需要在process_item中实现return item就传递给下一个即将执行的管道

          • 如果redis报错 需要将redis模块指定为2.10.6即可 pip install -U redis==2.10.6

            简单实例 写入mysql 点击查看
               import mysql
               # 将数据存储到mysql中 在pipelines管道文件中写一个类
               class MysqlPileLine(object):
                   conn = None 
                   # 链接对象和文件句柄一样 只能创建一次                     
                   def open_spider(self, spider):
                       self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='567913', db='lj')
                       print(conn)
                   def process_item(self, item, spider):
                       self.cursor = self.conn.cursor()
                       sql = 'insert into duanziwang values (%s, %s)'
                       try:
                           self.cur.execute(sql, [item['title'], item['content']])
                           self.conn.commit()
                       except Exception as e:
                           print(e)
                           self.conn.rollback()
                   def close_spider(self, spider):
                       self.cursor.close()
                       self.conn.close()
                        
            
            简单实例 写入redis 点击查看
               from redis import Redis
               
               class RedisPileLine(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):
                       sql.conn.lpush('duanziData', item)
            
            
  • scrapy手动请求发送实现的全站数据爬取

    • yield srapy.Request(url, callback) # 这个是GET请求
      • callback指定解析函数 用于解析数据
    • yield scrapy.FormRequest(url, callback, formdata) # 这个是POST请求
      • formdata字典 请求参数
  • 为什么start_urls列表中的url会被自动进行get请求的发送?

    • 因为列表中的url其实是被start_requests这个父类方法实现的get请求发送


      点击查看代码

        ```
            #父类方法:这个是该方法的原始实现
            def start_requests(self):
                for u in self.start_urls:
                    yield scrapy.Request(url=u, callback=self.parse)
        ```
        </details>
      
  • 如何将start_urls中的url默认改成post请求发送?

    • 重写start_requests方法即可


      点击查看代码

      ```
          def start_requests(self):
              for u in self.start_urls:
                  yield scrapy.FormRequst(url=u, callback=self.parse)
      
      ```
      </details>
原文地址:https://www.cnblogs.com/FGdeHB/p/15496834.html