zoukankan      html  css  js  c++  java
  • 爬虫2.4-scrapy框架-图片分类下载

    scrapy框架-图片下载

    python小知识:

    map函数:将一个可迭代对象的每个值,依次交给一个函数处理,返回一个生成器。

    urls = uibox.xpath(".//ul/li/a/img/@src").getall()
    urls = list(map(lambda url: 'https:'+url, urls))
    

    urls本身是一个列表,使用map函数,将urls中的每一项传递给url ,并让url执行‘https:’+url的操作。 lambda则是一个无名函数,方便一行内写完函数。最后用list函数将map返回的生成器转成列表

    1 传统下载方法:

    pipelines.py
    def process_item(self, item, spider):
        category = item['category']  # 一个字符串
        urls = item['urls']  # 一个列表
        local_path = os.path.abspath('.') # 获取绝对路经
        os.mkdir(r'{}images{}'.format(local_path, category)) #分类创建images/category文件夹
        for url in urls:
            filename = url.split('_')[-1]  # 获取图片名
            request.urlretrieve(url, '{}images{}{}'.format(local_path, category, filename))  # 将图片下载到/images/category/filename
    

    缺点是非异步,图片是一张一张按顺序下载的,数量较大时效率低

    2 scrapy框架的下载方法

    scrapy.pipelines中提供了两个下载文件的子类

    一个是scrapy.pipelines.files 中的FilesPipeline,另一个是scrapy.pipelines.images 中的ImagesPipeline

    其中ImagesPipeline专门用于下载图片,是继承自FilesPipeline

    ImagesPipeline使用方法

    1)在items.py中定义好image_urls和images,image_urls是用来存储需要下载的图片的url,需要给一个列表,images中需要保存图片的属性,如下载路径、url、图片的校验码等

    2)当图片下载完成后,会把文件下载的相关信息存储到item中的images属性中。

    3)在配置文件settings.py中配置IMAGES_STROE,这个配置是用来设置图片下载路径的

    4)启动pipeline,在ITEM_PIPELINES中注释原来的pepeline设置'scrapy.pipelines.images.ImagesPipeline':1

    FilesPipeline使用方法类似

    定义好file_urls和file,设置好FILES_STORE,以及ITEM_PIPELINES中的scrapy.pipelines.files.FilesPipeline:1

    2.1 将图片全部下载到full文件夹下

    本来高高兴兴来到实验室,2分钟就把需要整的步骤整完了,如何折腾了一个小时都没想通为什么爬虫无法执行parse()函数,各种百度,最后对比之前写的爬虫发现,我睡了午觉起来迷迷糊糊将start_urls 改成了start_image_urls 这样的话scrapy框架一检查,没有开始点啊,那直接就结束了。。。。

    1)修改setttings.py 注释掉原来的ITEM_PIPELINES中的内容,添加'scrapy.pipelines.images.ImagesPipeline': 1

    并添加图片存储路径IMAGES_STORE = os.path.join(os.path.abspath('.'), 'images')

    ~ settings.py
    ITEM_PIPELINES = {
       # 'bmw.pipelines.BmwPipeline': 300,
     'scrapy.pipelines.images.ImagesPipeline': 1
    }
    IMAGES_STORE = os.path.join(os.path.abspath('.'), 'images')  # 爬虫项目根目录/images
    

    2)修改items.py 添加或修改 image_urls和images

    image_urls = scrapy.Field()
    images = scrapy.Field()
    

    3)修改[spider.py] yield image_urls

    item = BmwItem(category=category, image_urls=image_urls)  # 重点在于image_urls
    yield item
    

    执行,得到结果:./images/full 目录下下载了一堆没有分类的图片

    这个时候就想怎么讲图片分类下载好,比如车身图片(实验代码以汽车之家宝马5x图片进行下载)就在/images/车身,内部图片在/images/内部,这样强迫症才舒服

    2.2 将图片分类到不同目录

    这里就需要重写scrapy.pipelines.images.ImagesPipeline里面的一些方法了。

    这里需要注意:parse()函数中yield 抛出的数据格式为 category: ‘xxx’ image_urls: ['url', 'url'....] images: ''

    所以在之后操作时,每次抛出的image_urls 都对应着同一个category

    1)继承ImagesPipeline,并改写一些返回值

    from bmw import settings  # bmw是项目名
    import os  # 2)中需要用到
    class BMWImagesPipeline(ImagesPipeline):  # 新定义一个子类,继承ImagesPipeline
        def get_media_requests(self, item, info):
            request_objs = super(BMWImagesPipeline, self).get_media_requests(item, info)
            for request_obj in request_objs:
                request_obj.item = item  # 为每个request_obj对象添加item属性,里面包含了category
            return request_objs  # 父类中必须返回这个列表,给其他函数使用
    

    get_media_requests函数原本返回的是[Request(x) for x in item.get(self.images_urls_field, [])],即返回了一个Request(url)的列表,这里改造一下,使用super重复父类中的方法,得到一个返回值,并添加了item属性,方便下一步使用。

    2)重写file_path()方法

    def file_path(self, request, response=None, info=None):
        # path获取父类中file_path方法返回的值即‘full/hash.jpg' hash是图片的哈希值
        path = super(BMWImagesPipeline, self).file_path(request, response, info)
        #request即前一个函数返回的列表中的每一项,所以有item属性
        category = request.item.get('category')
        images_store = settings.IMAGES_STORE  # IMAGES_STORE是需要下载到的路径,例如c:/images
        category_path = os.path.join(images_store, category) # 创建每个种类的路径并判断是否已存在
        if not os.path.exists(category_path):
            os.mkdir(category_path)
        image_name = path.replace('full/', '')  # 去掉原本的'full/' 只留下文件名
        image_path = os.path.join(category_path, image_name) 
        # 图片完整的路径和文件名c:/images/aaa/js451v88225F45sd42f4y4.jpg
        return image_path
    

    3)修改settings.py中的配置

    ITEM_PIPELINES = {
       # 'bmw.pipelines.BmwPipeline': 300, 注释掉原有的pipeline
        'bmw.pipelines.BMWImagesPipeline': 1
    }
    IMAGES_STORE = os.path.join(os.path.abspath('.'), 'images')  # 添加图片保存地址
    

    完工,重写之后每个图片的路径就包含了自己的种类,下载完成后目录格式如下:

    images
       车头
           41312r322r2234.jpg
           1425flj5l2.jpg
       车身
       。。
    

    3 分类下载完整代码

    项目名:bmw 爬虫名:bmw5x

    bmw5x.py

    import scrapy
    from bmw.items import BmwItem
    
    
    class Bmw5xSpider(scrapy.Spider):
        name = 'bmw5x'
        allowed_domains = ['car.autohome.com.cn']
        start_urls = ['https://car.autohome.com.cn/pic/series/65.html#pvareaid=2042209']
    
        def parse(self, response):
            uiboxs = response.xpath("//div[@class='uibox']")[1:]
            for uibox in uiboxs:
                category = uibox.xpath("./div/a/text()").get()
                image_urls = uibox.xpath(".//ul/li/a/img/@src").getall()
                image_urls = list(map(lambda url: 'https:'+url, image_urls))
                item = BmwItem(category=category, image_urls=image_urls)
                yield item
    

    items.py

    import scrapy
    class BmwItem(scrapy.Item):
        category = scrapy.Field()
        image_urls = scrapy.Field()
        images = scrapy.Field()
    

    pipelines.py

    import os
    from scrapy.pipelines.images import ImagesPipeline
    from bmw import settings
    
    
    class BMWImagesPipeline(ImagesPipeline):
        def get_media_requests(self, item, info):
            request_objs = super(BMWImagesPipeline, self).get_media_requests(item, info)
            for request_obj in request_objs:
                request_obj.item = item
            return request_objs
    
        def file_path(self, request, response=None, info=None):
            path = super(BMWImagesPipeline, self).file_path(request, response, info)
            category = request.item.get('category')
            images_store = settings.IMAGES_STORE
            category_path = os.path.join(images_store, category)
            if not os.path.exists(category_path):
                os.mkdir(category_path)
            image_name = path.replace('full/', '')
            image_path = os.path.join(category_path, image_name)
            return image_path
    

    settings.py

    import os
    ROBOTSTXT_OBEY = False
    DEFAULT_REQUEST_HEADERS = {
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
      'Accept-Language': 'en',
      'User-Agent': 'xxx',
      'Referer': 'xxx',
      'Cookie': 'xxx'
    }
    ITEM_PIPELINES = {
        'bmw.pipelines.BMWImagesPipeline': 1
    }
    IMAGES_STORE = os.path.join(os.path.abspath('.'), 'images')
    

    作者:bitterz
    本文版权归作者和博客园所有,欢迎转载,转载请标明出处。
    如果您觉得本篇博文对您有所收获,请点击右下角的 [推荐],谢谢!
  • 相关阅读:
    实验 7:OpenDaylight 实验——Python 中的 REST API 调用
    实验 6:OpenDaylight 实验——OpenDaylight 及 Postman实现流表下发
    实验 5:OpenFlow 协议分析和 OpenDaylight 安装
    实验 4:Open vSwitch 实验——Mininet 中使用 OVS 命令
    实验 3:Mininet 实验——测量路径的损耗率
    软件工程第一次作业——自我介绍
    实验 2:Mininet 实验——拓扑的命令脚本生成
    实验1、Mininet 源码安装和可视化拓扑工具
    第01组 Beta版本演示
    第01组 Beta冲刺(4/4)
  • 原文地址:https://www.cnblogs.com/bitterz/p/10202197.html
Copyright © 2011-2022 走看看