zoukankan      html  css  js  c++  java
  • Scrapy管道

    管道简介

    作用是处理抓取的数据,包括

    • 清洗数据
    • 检查抓取的数据是否有效
    • 去重
    • 保存数据
      一个项目包含多条管道,爬虫收集到的Item会根据指定顺序传递给管道进行处理。
      官方的项目管道的典型用途有
    清理HTML数据
    验证抓取的数据(检查项目是否包含某些字段)
    检查重复项(并删除它们)
    将爬取的项目存储在数据库中
    

    编写自定义管道

    每个item pipeline组件都是一个python类,必须实现以下方法:
    process_item(self, item, spider)¶
    对每个项管道组件调用此方法。
    item 是一个 item object 见 支持所有项目类型 .
    process_item() 必须:返回 item object 返回A Deferred 或提高 DropItem 例外。
    丢弃的项目不再由其他管道组件处理。
    参数
    item (item object) -- 刮掉的东西
    spider (Spider object) -- 爬取项目的蜘蛛
    此外,它们还可以实现以下方法:
    open_spider(self, spider)¶
    当spider打开时调用此方法。
    参数
    spider (Spider object) -- 打开的蜘蛛
    close_spider(self, spider)¶
    当spider关闭时调用此方法。
    参数
    spider (Spider object) -- 关闭的蜘蛛
    from_crawler(cls, crawler)¶
    如果存在,则调用此ClassMethod从 Crawler . 它必须返回管道的新实例。爬虫对象提供对所有零碎核心组件(如设置和信号)的访问;它是管道访问它们并将其功能连接到零碎的一种方式。
    参数
    crawler (Crawler object) -- 使用此管道的爬虫程序
    

    项目实例

    需要知晓的知识点,dump和dumps是实现json编码功能,dump把dict编码成json字符串并保存在文件中,dumps把dict编码成json字符串;JSON格式的键一定要用双引号扩起;而且在保存中文字典时,记得加ensure_ascii=false,因为JSON在处理中文时,默认使用的是ASCII编码。
    load和loads用于JSON的解码,区别是load是需要从文件中解码,而loads加载字符串进行解码。

    编写items.py
    import scrapy
    class JobboleArticleItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        # 文章标题
        title = scrapy.Field()
        # 内容摘要
        summary = scrapy.Field()
        # 发表日期
        publish_date = scrapy.Field()
        # 标签
        tag = scrapy.Field()
    

    编写pipelines.py

    import json
    
    
    class JobboleArticlePipeline(object):
        # 当启动爬虫时,打开items.json文件,准备写入数据
        def open_spider(self, spider):
            self.file = open('items.json','w')
    
        
        # 当爬虫执行结束时,关闭打开的文件
        def close_spider(self, spider):
            self.file.close()
        # 将抓取到的数据做json序列化存储
        def process_item(self, item, spider):
            line = json.dumps(dict(item), ensure_ascii=False) + "\n" # json的每条数据加上换行符
            self.file.write(line)
            return item #这返回的数据到哪去了呢
    

    编写完管道之后记得在settings.py中开启管道;只需要将管道的路径加到settings.py中

    ITEM_PIPELINES = {
       'jobbole_article.pipelines.JobboleArticlePipeline': 300,
    }
    

    最后编写爬虫文件

    # -*- coding: utf-8 -*-
    import scrapy
    from jobbole_article.items import JobboleArticleItem
    
    
    class ArticleSpider(scrapy.Spider):
        name = 'article'
        allowed_domains = ['jobbole.com']
        start_urls = ['http://blog.jobbole.com/all-posts/']
    
        def parse(self, response):
            all_post = response.css(".post")
            for post in all_post:
                item = JobboleArticleItem()
                item['title'] = post.css('.archive-title::text').extract_first() # 这里返回的数据其实都是字符串格式
                item['summary'] = post.css('.excerpt p::text').extract_first()
                # 根据正则表达式提取发表日期
                item['publish_date'] = post.css('.post-meta p::text').re_first(r'\d{4}/\d{2}/\d{2}')
                # Tag 标签可能有多个,因此不需要获取第一个值,保存列表即可
                item['tag'] = post.xpath(".//a[2]/text()").extract()
                yield item
    
            # 检查是否有下一页url,如果有下一页则调用parse进行处理
            next_page = response.css('.next::attr(href)').extract_first()
            if next_page:
                yield scrapy.Request(next_page,callback=self.parse)
    

    使用命令运行爬虫,在该项目文件夹下即可

    scrapy crawl article
    

    下载文件和图片

    通常使用FilePipeline和ImagesPipeline,分别用于下载文件和图片。
    这两种管道都包含以下特性

    • 避免重复下载最近下载过的数据
    • 指定存储的位置,可使用本地文件系统或者云端存储
      同时,ImagePipeline还有些额外的的特性
    • 将下载的图片转换成通用的JPG格式和RGB模式
    • 为下载的图片生成缩率图
    • 检查图片的宽/高,确保能够满足最新要求。
      管道会为计划中下载的文件URL保存在一个内部队列,与保存同样文件的Response相关联,从而避免重复下载几个Item共用的图片。

    编写item

    import scrapy
    
    
    class DownloadfileItem(scrapy.Item):
        # define the fields for your item here like:
        # 文件名
        file_name = scrapy.Field()
        # 发布时间
        release_date = scrapy.Field()
        # 文件url
        file_urls = scrapy.Field()
        # 文件结果信息
        files = scrapy.Field()
    
    

    编写settings.py

    DownloadfilePipeline其实还是我们编写的类;但他继承了FilesPipeline类

    ITEM_PIPELINES = {
       # 'scrapy.pipelines.files.FilesPipeline': 1
       'downloadfile.pipelines.DownloadfilePipeline': 300,
    }
    # 保存文件设置
    FILES_STORE = 'D:\\Scrapy\\downloadfiles'
    FILES_URLS_FIELD = 'file_urls'
    FILES_RESULT_FIELD = 'files'
    
    

    编写爬虫脚本文件

    import scrapy
    from downloadfile.items import DownloadfileItem
    
    
    class GetfileSpider(scrapy.Spider):
        name = 'getfile'
        allowed_domains = ['szhrss.gov.cn']
        start_urls = ['http://hrss.sz.gov.cn/wsbs/xzzx/rcyj/']
    
        def parse(self, response):
            files_list = response.css('.conRight_text_ul1 li')
            for file in files_list:
                item = DownloadfileItem()
                item['file_name'] = file.css('a::text').extract_first()
                item['release_date'] = file.css('span::text').extract_first()
                # 由于获取到的url类似"./201501/P020170328745500534334.doc"
                # 所以需要手动调整为完成的url格式
                url = file.css('a::attr(href)').extract_first()
                # file_urls必须是list形式
                item['file_urls'] = [response.url + url[1:]]
                yield item
    

    获取的文件名是根据URL自动生成的SHA1哈希值
    这时候我们为了知晓文件名,就需要修改pipelines.py文件,添加自定义管道

    from scrapy.pipelines.files import FilesPipeline
    from scrapy import Request
    
    class DownloadfilePipeline(FilesPipeline): # 继承FilesPipeline
        # 修改file_path方法,使用提取的文件名保存文件
        def file_path(self, request, response=None, info=None):
            # 获取到Request中的item
            item = request.meta['item']
            # 文件URL路径的最后部分是文件格式
            file_type = request.url.split('.')[-1]
            # 修改使用item中保存的文件名作为下载文件的文件名,文件格式使用提取到的格式
            file_name = u'full/{0}.{1}'.format(item['file_name'],file_type)
            return file_name
    
        def get_media_requests(self, item, info):
            for file_url in item['file_urls']: # item中的字段url是字典的值file_urls
                # 为request带上meta参数,把item传递过去
                yield Request(file_url,meta={'item':item})
    

    图片管道

    编写itmes.py

    
    import scrapy
    
    
    class DownloadimageItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        # 小说名称
        title = scrapy.Field()
        # 小说作者
        author = scrapy.Field()
        # 小说类型
        type = scrapy.Field()
        # 图片URL
        image_urls = scrapy.Field()
        # 图片结果信息
        images = scrapy.Field()
    

    编写settings.py

    ITEM_PIPELINES = {
        'downloadimage.pipelines.DownloadimagePipeline': 300,
        'scrapy.pipelines.images.ImagesPipeline':1 # 这里添加ImagesPipeline
    }
    IMAGES_STORE = 'D:\\03'
    IMAGES_URLS_FIELD = 'image_urls' # 图片URL对应的Item字段
    IMAGES_RESULT_FIELD = 'images' # 图片结果信息对应的Item字段
    IMAGES_THUMBS = {
        'small' : (80,80),
        'big' : (300,300)
        }
    
    

    编写自定义管道pipelines.py

    # 并没有实现按下载图片的名字为原本图片名,或许原本图片名就是这个
    import json
    from scrapy import Request
    from scrapy.pipelines.images import ImagesPipeline
    class DownloadimagePipeline(ImagesPipeline):
        # 将小说信息保存为json文件
        def open_spider(self,spider): # 这样还就把他保存为jso格式了,只要在pipelines.py中重写下函数
            self.file = open('qidian2021.json','w')
    
        def close_spider(self,spider):
            self.file.close()
        def file_path(self, request, response=None, info=None):
            # 获取到Request中的item
            item = request.meta['item']
            # 文件URL路径的最后部分是文件格式
            file_type = request.url.split('.')[-1]
            # 修改使用item中保存的文件名作为下载文件的文件名,文件格式使用提取到的格式
            file_name = u'full/{0}.{1}'.format(item['title'],file_type)
            return file_name
    
        def process_item(self, item, spider): # 这个函数才是真正写入json格式的函数,上面那个open_spider和close_spider都是定义
            # 写入文件
            line = json.dumps(dict(item), ensure_ascii=False) + "\n"
            self.file.write(line)
            return item
        # 这个应该没用,是从上面那个pipelines.py中copy过来的
        def get_media_requests(self, item, info):
            for file_url in item['file_urls']: # item中的字段url是字典的值file_urls
                # 为request带上meta参数,把item传递过去
                yield Request(file_url,meta={'item':item})
    

    编写爬虫脚本

    import scrapy
    from downloadimage.items import DownloadimageItem
    
    
    class GetimageSpider(scrapy.Spider):
        name = 'getimage'
        allowed_domains = ['qidian.com']
        start_urls = ['https://www.qidian.com/finish']
    
        def parse(self, response):
            for novel in response.css(".all-img-list > li"):
                item = DownloadimageItem()
                item['title'] = novel.xpath('.//h4/a/text()').extract_first()
                item['author'] = novel.css('.name::text').extract_first()
                item['type'] = novel.css('em + a::text').extract_first() # em就是分割线|
                item['image_urls'] = ['https:' + novel.xpath('.//img/@src').extract_first()]
                yield item
    

    运行该爬虫之后,会在settings.py设置的路径中生成两个文件夹:full和thumbs.thumbs文件夹中有big及small文件夹

  • 相关阅读:
    DS博客作业05--查找
    DS博客作业04--图
    DS博客作业03--树
    DS博客作业02--栈和队列
    DS01-线性表
    C博客作业06-结构体&文件
    C博客作业05--指针
    C博客作业04--数组
    C博客作业03--函数
    C博客作业02--循环结构
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15683744.html
Copyright © 2011-2022 走看看