Python爬虫5.9 — scrapy框架下载文件和图片

综述

本系列文档用于对Python爬虫技术的学习进行简单的教程讲解,巩固自己技术知识的同时,万一一不小心又正好对你有用那就更好了。
Python 版本是3.7.4

本篇文章主要pipeline模块下载文件和图片的使用。

下载文件和图片

Scrapy为下载item中包含的文件(比如再爬取到产品时,同时也想保存到对应图片)提供了一个可重用的item pipeline。这些pipeline有些共同的方法和结构(我们称之为media Pipeline),一般来说我们会使用到File PipelineImages Pipeline

使用Scrapy框架内置方法的好处

我们为什么要选择用Scrapy内置的下载文件的防范:

  1. 避免重复下载最近已经下载过的数据。
  2. 可以方便的指定文件存储的路径。
  3. 可以将下载的图片转存成通用的格式,比如png或jpg
  4. 可以方便的生成缩略图。
  5. 可以方便的检测图片的宽和高,确保他们满足最小限制。
  6. 异步下载,效率非常高。

下载文件的File Pipeline

当使用File Pipeline下载文件的时候,按照以下步骤来完成:

  1. 定义好一个Item,然后再这个Item中定义两个属性,分别为file_urls以及filesfile_urls是用来存储需要下载的文件的url链接,需要给一个列表。
  2. 当文件下载完成后,会把文件下载相关的信息存储到itemfiles属性中。比如下载路径、下载的url和文件的校验码等。
  3. 在配置文件setting.py中配置FILES_STORE,这个配置时用来设置文件下载下来的路径。
  4. 启动pipeline:在ITEM_PIPELINES中设置scrapy.pipelines.files.FilesPipeline:1

下载图片的Images Pipeline

当使用Images Pipeline下载文件的时候,按照以下步骤来完成:

  1. 定义好一个Item,然后再这个Item中定义两个属性,分别为image_urls以及imagesimage_urls是用来存储需要下载的图片的url链接,需要给一个列表。
  2. 当文件下载完成后,会把图片下载相关的信息存储到itemimages属性中。比如下载路径、下载的url和图片的校验码等。
  3. 在配置文件setting.py中配置IMAGES_STORE,这个配置时用来设置文件下载下来的路径。
  4. 启动pipeline:在ITEM_PIPELINES中设置scrapy.pipelines.images.ImagesPipeline:1

实例说明

我们就以爬取汽车之家中的图片为例。

传统下载方式

传统的下载方式在这里就简单说下步骤:

  1. 使用scrapy命令创建项目;
  2. 使用命令创建爬虫;
  3. 更改setting.py文件配置信息;
  4. 编写items.py代码;
  5. 编写spider模块下代码(爬取网页中需要下载的图片链接);
  6. 编写pipelines.py代码(进行数据处理,图片的保存等)。

pipelines.py代码如下:

import os
import urllib.request


class BmwPipeline(object):

    def __init__(self):
        # 创建文件保存文件夹
        self.image_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
        if not os.path.exists(self.image_path):
            os.mkdir(self.image_path)

    def process_item(self, item, spider):
        category = item['category']
        img_urls = item['img_urls']
        # 创建图片类别文件夹
        category_path = os.path.join(self.image_path, category)
        if not os.path.exists(category_path):
            os.mkdir(category_path)
        
        # 根据图片url进行下载保存图片
        for url in img_urls:
            img_name = url.split('_')[-1]
            urllib.request.urlretrieve(url=url, filename=os.path.join(category_path, img_name))
            print(img_name)

        return item

通过上述代码我们就可以将爬取的图片url进行处理保存图片到本地。但是这种处理方式一个很大的缺点就是图片只能一个一个下载不能进行异步下载。下面我们使用Scrapy框架中自带的图片下载进行下载图片。

使用Scrapy框架自带ImagesPipeline进行下载图片

按照上述的步骤进行编写相应的代码:

  1. 定义好一个Item,然后再这个Item中定义两个属性,分别为image_urls以及images。编写items.py代码如下:
    import scrapy
    
    
    class BmwItem(scrapy.Item):
        category = scrapy.Field()
        image_urls = scrapy.Field()
        images = scrapy.Field()
    
  2. 在配置文件setting.py中配置IMAGES_STORE。增加代码如下:
    # 图片下载路径,供image pipeline使用
    IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
    
  3. 启动pipeline:在ITEM_PIPELINES中设置scrapy.pipelines.images.ImagesPipeline:1。修改代码如下:
    ITEM_PIPELINES = {
        # 'bmw.pipelines.BmwPipeline': 300,
        'scrapy.pipelines.images.ImagesPipeline': 1
    }
    
  4. 编写spider模块代码如下:
    import scrapy
    from bmw.items import BmwItem
    
    class Bmw5Spider(scrapy.Spider):
        name = 'bmw5'
        allowed_domains = ['car.autohome.com.cn']
        start_urls = ['https://car.autohome.com.cn/pic/series/65.html']
    
        def parse(self, response):
            uiboxs = response.xpath('//div[@class="uibox"]')[1:]
    
            for uibox in uiboxs:
                category = uibox.xpath('.//div[@class="uibox-title"]/a/text()').get()
                img_urls_tmp = uibox.xpath('.//ul/li/a/img/@src').getall()
                img_urls = list(map(lambda url:response.urljoin(url),img_urls_tmp))
                item = BmwItem(category=category,image_urls=img_urls)
                yield item
    

运行代码你会发现其能很快的将图片下载完成。下载完成后你会在你定义的图片保存文件夹images下多了一个full文件夹,这个里面就是所下载下来的所有图片。有的想问如果我还想要根据分类继续保存图片需要怎么办呢?这就需要我们去重写ImagesPipeline类中的一些方法了。通过读ImagesPipeline类的代码我们可以知道,类中有两个方法分别是get_media_requestsfile_path

  • get_media_requests : 这个方法是在发送下载请求之前调用,其实这个方法本身就是去发送下载请求的。
  • file_path : 这个方式是在图片将要被存储时候调用,来获取这个图片的存储路径。

优化方案如下:

  1. pipelines.py中重写声明定义一个类,继承ImagesPipeline;
  2. 在此子类中重新这两个方法代码如下:
    import os
    import urllib.request
    
    from scrapy.pipelines.images import ImagesPipeline
    from bmw import settings
    
    
    class BmwImagesPipeline(ImagesPipeline):
    
        # 重写get_media_requests方法
        def get_media_requests(self, item, info):
            # 这个方法是在发送下载请求之前调用
            # 其实这个方法本身就是去发送下载请求的
            request_objs = super(BmwImagesPipeline, self).get_media_requests(item, info)
            # 将item数据加入到请求中
            for requests_obj in request_objs:
                requests_obj.item = item
            return request_objs
    
        # 重写file_path方法
        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
    
  3. 重新修改setting.py中的ITEM_PIPELINES配置如下:
    ITEM_PIPELINES = {
        # 'bmw.pipelines.BmwPipeline': 300,
        # 'scrapy.pipelines.images.ImagesPipeline': 1
        'bmw.pipelines.BmwImagesPipeline': 300,
    }
    

之后运行代码即可。

其他博文链接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章