爬虫_08_scrapy&持久化存储&管道操作&手动请求发送

08_scrapy&持久化存储&管道操作&手动请求发送

scrapy框架

简介

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

学习:学习是学好框架中集成好的各种功能、特性

进阶:逐步的探索框架的底层

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

  • 高性能的数据解析、请求发送,持久化存储,全栈数据爬取,中间件,分布式……

环境的安装

  • mac、linum: pip install scrapy

  • windows:

    1. pip3 install wheel

    2. 下载twisted文件,下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

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

      • cp指python版本,win_amd64指windows64位版本

      • Twisted:就是一个异步的架构。被作用在了scrapy中。

      • 安装报错:需要更换另一个版本的twisted文件进行安装即可。

    4. pip3 install pywin32

    5. pip3 install scrapy

测试:cmd中scrapy按下回车,如果没有报错说明安装成功。

scrapy的基本使用

  • 创建一个工程

    • scrapy startproject ProName
    • 目录结构:
      • spiders:爬虫文件夹
        • 必须要存放一个爬虫源文件
      • settings.py:工程的配置文件
  • cd ProName

  • 创建爬虫源文件:

    • scrapy genspider spiderName www.xxx.com
    • 编写对应的代码在爬虫文件中
  • 执行工程

    • scrapy crawl spiderName
    • 执行工程后,默认会输出工程所有的日志信息
    • 指定类型日志的输出:
      • settings.py:加入LOG_LEVEL=‘ERROR’
配置文件settings.py
  1. 指定日志类型:LOG_LEVEL=‘ERROR’
  2. 禁止robots:ROBOTSTXT_OBEY = False
  3. UA伪装 USER_AGENT
爬虫文件spiderName.py内容阐述
import scrapy

class FirstSpider(scrapy.Spider):
    #爬虫文件名称:当前源文件的唯一标识
    name = 'first'
    #允许的域名,一般注释
    #allowed_domains = ['www.baidu.com']
    #起始的url列表:只可以存储url
    #作用:列表中存储的url都会被进行get请求的发送
    start_urls = ['https://www.baidu.com/','https://www.sogou.com']

    #用于数据解析
    #parse方法调用的次数完全取决于请求的次数
    #参数response:表示的就是服务器返回的响应对象
    def parse(self, response):
        pass
  • name:爬虫文件名称,该文件的唯一标识
  • start_urls:起始url列表,存储的都是url,url可以被自动进行get请求的发送
  • parse方法:请求后的数据解析操作

scrapy数据解析

  • 使用:response.xpath(‘xpath表达式’)

  • scrapy封装的xpath和etree中的xpath区别

    • scrapy中的xpath直接将定位到的标签中存储的值或者属性值取出,返回的是Selector对象,且相关的数据值是存储在Selector对象的data属性中,需要调用extract、extract_first()取出字符串数据
  • 三种获得数据的xpath方式

        def parse(self, response):
            #数据解析名称和内容
            li_list = response.xpath('//*[@id="list"]/ul/li')
            for li in li_list:
                #下面解析出来的内容不是字符串数据,说明和etree中的xpath使用方式不同
                #xpath返回的列表中存储的不是字符串而是Selector对象,其实我们想要的字符串数据被存储在了该对象的data属性中
                # title = li.xpath('./div[2]/a/text()')[0]
                # #<Selector xpath='./div[2]/a/text()' data='因为她是你的母亲'>
                # content = li.xpath('./div[1]/text()')[0]
                
                #将Selector对象data属性值取出
                #1. extract()就是将data属性值取出(几乎不用)
                title = li.xpath('./div[2]/a/text()')[0].extract()
                #因为她是你的母亲
                content = li.xpath('./div[1]/text()')[0].extract()
    
                #2. extract_first():将列表中的第一个列表元素表示的Selector对象中的data值取出
                title = li.xpath('./div[2]/a/text()').extract_first()
                #因为她是你的母亲
                content = li.xpath('./div[1]/text()').extract_first()
                
                #3. 直接使用列表调用extract():可以将列表中的每一个列表元素表示的Selector中的data取出
                title = li.xpath('./div[2]/a/text()').extract()
                #['因为她是你的母亲']
                content = li.xpath('./div[1]/text()').extract()
    

持久化存储

基于终端指令的持久化存储
  • 要求:该种方式只可以将parse方法的返回值存储到本地指定后缀的文本文件中

  • 执行指令:scrapy crawl spiderName -o filePath

  • 例如:

        #将解析到的数据进行持久化存储:基于终端指令的持久化存储
        def parse(self, response):
            all_data = []
            # 数据解析名称和内容
            li_list = response.xpath('//*[@id="list"]/ul/li')
            for li in li_list:
                # extract_first():将列表中的第一个列表元素表示的Selector对象中的data值取出
                title = li.xpath('./div[2]/a/text()').extract_first()
                content = li.xpath('./div[1]/text()').extract_first()
                dic = {
                    'title':title,
                    'content':content
                }
                all_data.append(dic)
    
            return all_data
    

    随后命令行输入:scrapy crawl duanzi -o duanzi.csv

  • 局限性

    指定后缀文件:‘json’, ‘jsonlines’, ‘jl’, ‘csv’, ‘xml’, ‘marshal’, ‘pickle’

基于管道的持久化存储(重点)
  1. 在爬虫文件中进行数据解析

    获得每个title和content

  2. items.py中定义相关属性

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

    例如:WangziproItem

    from wangziPro.items import WangziproItem
        def parse(self, response):
            ...
                #实例化一个item类型的对象,将解析到的数据存储到该对象中
                item = WangziproItem()
                #不可以通过。的形式调用属性
                item['title'] = title
                item['content'] = content
    
  4. 将Item类型的对象提交给管道

    上接yield item

  5. 在管道文件(pipelines.py)中,接受爬虫文件提交过来了Item类型对象,且对其进行任意形式的持久化存储操作

    class WangziproPipeline:
        fp = None
        #重写父类的两个方法
        def open_spider(self,spider):
            print('我是open_spider(),我只会在爬虫开始的时候执行一次!')
            self.fp = open('duanzi.txt','w',encoding='utf-8')
            
        def close_spider(self,spider):
            print('我是close_spider(),我只会在爬虫结束的时候执行一次!')
            self.fp.close()
    
        #该方法是用来接受item对象。一次只能接收一个item,说明该方法会被调用多次
        #参数item:就是接收到的item对象
        def process_item(self, item, spider):
            # print(item) #item其实就是一个字典
            self.fp.write(item['title']+":"+item['content']+'\n')
            #将item存储到文本文件
            return item
    
  6. 在配置文件settings.py中开启管道机制

    ITEM_PIPELINES = {
        #300表示管道类的优先级,数值越小优先级越高
       'wangziPro.pipelines.WangziproPipeline': 300,
    }
    
基于管道实现数据的备份
  • 将爬取到的数据分别存储到不同的载体

  • 实现:将数据一份存储到mysql,一份存储到redis

  • 问题:管道文件中的一个管道类表示怎样的一组操作呢?

    • 一个管道类对应一种形式的持久化存储操作。如果将数据存储到不同的载体中就需要使用多个管道类
  • 已经定义好了三个管道类,将数据写入到三个载体中进行存储:

    • item会不会依次提交给三个管道类?
      • 不会,爬虫文件中的item只会被提交给优先级最高的那一个管道类
      • 优先级高的管道类需要在process_item中实现return item,就item传递给下一个即将被执行的管道类

注意点:

#duanzi.py
class DuanziSpider(scrapy.Spider):
    def parse(self, response):
        #...
        yield item
        
#pipelines.py,3种保存方式:文件、mysql、redis
import pymysql
from redis import Redis
class WangziproPipeline:
    fp = None
    #重写父类的两个方法
    def open_spider(self,spider):
        print('我是open_spider(),我只会在爬虫开始的时候执行一次!')
        self.fp = open('duanzi.txt','w',encoding='utf-8')
    def close_spider(self,spider):
        print('我是close_spider(),我只会在爬虫结束的时候执行一次!')
        self.fp.close()

    #该方法是用来接受item对象。一次只能接收一个item,说明该方法会被调用多次
    #参数item:就是接收到的item对象
    def process_item(self, item, spider):
        # print(item) #item其实就是一个字典
        self.fp.write(item['title']+":"+item['content']+'\n')
        #将item存储到文本文件
        return item

#将数据存储到mysql中
class MysqlPipLine(object):
    conn = None
    cursor = None
    def open_spider(self,spider):
        self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='1234',db='spider',charset='utf8')
        print(self.conn)

    def process_item(self,item,spider):
        self.cursor = self.conn.cursor()
        sql = 'insert into duanziwang values ("%s","%s")'%(item['title'],item['content'])

        #事务处理
        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 RedisPipeLine(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):
        #item是字典,导入到duanziData的列表中
        #如果报错:将redis模块的版本指定成2.10.6即可。pip install -U redis==2.10.6
        self.conn.lpush('duanziData',item)
        
#settings.py
ITEM_PIPELINES = {
    #300表示管道类的优先级,数值越小优先级越高
    #优先级高:优先级高的管道类先被执行
   'wangziPro.pipelines.WangziproPipeline': 300,
   'wangziPro.pipelines.MysqlPipLine': 301,
   'wangziPro.pipelines.RedisPipeLine': 302
}

scrapy手动请求发送实现全站数据爬取

  • yield scrapy.Request(url,callback):GET请求

    • callback指定解析函数,用于解析数据
  • yield scrapy.FormRequest(url,callback,formdata):POST请求

    • formdata:字典,请求参数
  • 实现多页爬取

    class DuanziSpider(scrapy.Spider):
    	name = 'duanzi'
        start_urls = ['http://www.haoduanzi.com/category/?1-1.html']
        #通用的url模板
        url = 'http://www.haoduanzi.com/category/?1-%d.html'
        page_num = 2
        
        def parse(self, response):
            #xxx
            if self.page_num < 5: #结束递归的条件
                new_url = format(self.url%self.page_num) #其他页码对应的完整url
                self.page_num += 1
                #对新的页码对应的url进行请求发送(手动请求GET发送)
                yield scrapy.Request(url=new_url,callback=self.parse)
    
  • 为什么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)
    
  • 如何将start_urls中的url默认进行post请求的发送?

    重写start_requests方法即可

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

剩余内容

  • scrapy的五大核心组件介绍
  • 请求传参实现的深度爬取
  • 中间件机制
  • CrawlSpider
  • 分布式
  • 增量式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Scrapy是一个基于Python的爬虫框架,它可以帮助我们快速高效地抓取网站数据。在这里,我将介绍Scrapy的基本用法,让您能够快速入门。 安装Scrapy ----------------------- 在安装Scrapy之前,我们需要先安装Python。然后,我们可以通过以下命令来安装Scrapy: ``` pip install scrapy ``` 创建Scrapy项目 ----------------------- 创建Scrapy项目的命令是: ``` scrapy startproject project_name ``` 这个命令将会在当前目录下创建一个名为project_name的文件夹,其中包含了Scrapy项目的基本结构。 编写Spider ----------------------- 在Scrapy中,Spider是用来定义爬取网站的规则的。我们可以通过以下命令来创建一个Spider: ``` scrapy genspider spider_name domain_name ``` 其中,spider_name是我们自己定义的Spider名称,domain_name是我们要抓取的网站域名。 接下来,我们需要在Spider中定义如何爬取网站。这里我们以爬取&ldquo;http://quotes.toscrape.com/&rdquo;网站上的名言警句为例。我们可以在Spider中定义如下规则: ```python import scrapy class QuotesSpider(scrapy.Spider): name = &quot;quotes&quot; start_urls = [ &#39;http://quotes.toscrape.com/page/1/&#39;, &#39;http://quotes.toscrape.com/page/2/&#39;, ] def parse(self, response): for quote in response.css(&#39;div.quote&#39;): yield { &#39;text&#39;: quote.css(&#39;span.text::text&#39;).get(), &#39;author&#39;: quote.css(&#39;span small::text&#39;).get(), &#39;tags&#39;: quote.css(&#39;div.tags a.tag::text&#39;).getall(), } next_page = response.css(&#39;li.next a::attr(href)&#39;).get() if next_page is not None: yield response.follow(next_page, self.parse) ``` 在上述代码中,我们首先定义了Spider的名称,接着定义了我们要爬取的起始URL,最后定义了如何解析网页的函数parse()。在parse()函数中,我们使用了Scrapy的选择器来提取网页中的名言警句,并将其保存到字典中。接着,我们使用response.follow()函数来获取下一页的URL,并继续解析。 运行Spider ----------------------- 要运行我们刚才创建的Spider,我们可以使用以下命令: ``` scrapy crawl spider_name ``` 其中,spider_name是我们之前创建的Spider名称。 Scrapy会自动去抓取我们定义的起始URL,并根据我们定义的规则来解析网页。解析完成后,Scrapy会将结果保存到我们指定的位置。 总结 ----------------------- Scrapy是一个非常强大的Python爬虫框架,它可以帮助我们快速高效地抓取网站数据。在本教程中,我们介绍了Scrapy项目的创建、Spider的定义以及如何运行Spider。如果您想更深入地学习Scrapy,可以参考官方文档:https://docs.scrapy.org/en/latest/。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值