使用scrapy下载图片

本案例要抓取的图片地址是汽车之家,使用scrapy下载图片主要思路是,在Spider中把图片的url地址交给pipelines来处理(前面也介绍了使用pipelines来处理Spider传递过来的数据)

一、创建一个基本爬虫

  • 1、创建项目及创建一只爬虫
  • 2、在items.py中定义汽车的item

    class CarItem(scrapy.Item):
        category = scrapy.Field()
        # 存储每次请求的url地址
        urls = scrapy.Field()
    
  • 3、Spider爬虫的书写

    import scrapy
    from urllib import parse
    
    from jobbloe.items import CarItem
    
    class CarSpider(scrapy.Spider):
        name = 'car'
        allowed_domains = ['car.autohome.com.cn']
        start_urls = ['https://car.autohome.com.cn/pic/series/65.html#pvareaid=3454438']
    
        def parse(self, response):
            uibox_list = response.xpath('//div[@class="uibox"]')[1:]
            for uibox in uibox_list:
                category = uibox.xpath('./div[@class="uibox-title"]/a/text()').extract_first()
                urls = uibox.xpath('.//img/@src').getall()
                # 这里也可以使用传统的for遍历后把数据存储到一个列表中
                urls = list(map(lambda url: parse.urljoin(response.url, url), urls))
                # 遍历一次发送一次数据到pipelines中
                yield CarItem(category=category, urls=urls)
    

二、不继承scrapy类自带文件的类中书写下载图片的方式有

  • 1、方式一:直接使用urllib库中的request请求图片的url地址

    import os
    from urllib import request
    
    class CarPipeline(object):
        """
        下载图片的pipeline
        """
    
        def __init__(self):
            # 生成最外面的文件夹
            self.path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
            if not os.path.exists(self.path):
                os.makedirs(self.path)
    
        def process_item(self, item, spider):
            if spider.name == 'car':
                category = item['category']
                urls = item['urls']
    
                # 生成分类的文件夹
                category_path = os.path.join(self.path, category)
                if not os.path.exists(category_path):
                    os.makedirs(category_path)
    
                # 遍历全部的url地址写入
                for url in urls:
                    imgage_name = url.split('_')[-1]
                    request.urlretrieve(url, os.path.join(category_path, imgage_name))
                return item
    
  • 2、方式二:借用requests库请求Spider过来的url地址,请求,手动写入到本地

    import os
    import requests
    
    class CarPipeline(object):
        """
        手动下载图片
        """
    
        def __init__(self):
            # 生成最外面的文件夹
            self.path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
            if not os.path.exists(self.path):
                os.makedirs(self.path)
    
        def process_item(self, item, spider):
            if spider.name == 'car':
                category = item['category']
                urls = item['urls']
    
                # 生成分类的文件夹
                category_path = os.path.join(self.path, category)
                if not os.path.exists(category_path):
                    os.makedirs(category_path)
                # 遍历全部的url地址写入
                for url in urls:
                    imgage_name = url.split('_')[-1]
                    response = requests.get(url)
                    with open(os.path.join(category_path, imgage_name), 'wb') as f:
                        f.write(response.content)
                return item
    

三、继承scrapy中自带文件写入、图片写入的类来实现图片的认识

  • 1、下载文件的File pipeline:

    当使用File pipeline下载文件的时候,需要根据下面步骤来完成

    • 定义好一个Item,然后再这个Item中定义两个属性,分别为file_urls以及filesfile_urls是用来存储需要下载的文件的url链接,需要给一个列表;
    • 当文件下载完成后,会把文件下载的相关信息存储到Itemfiles属性中。比如下载路径、下载的url和文件的校验码等;
    • 在配置文件settings.py中配置FILES_STORE,这个配置是用来设置文件下载来的路径(存储到本地的目录文件);
    • settings.py中的ITEM_PIPELINES中注册pipeline

      scrapy.pipelines.files.FilesPipeline: 1
      
  • 2、下载图片的Images pipeline:

    当使用Images pipeline下载文件的时候,需要根据下面步骤来完成

    • 定义好Item,然后再这个Item中定义两个属性,分别为image_urls以及images,image_urls是用来存储需要下载的图片的url连接,需要给一个列表;
    • 当文件下载完成后,会把文件下载的相关信息存储到itemimages属性中,比如下载路径、下载的url和图片相关的校验码等;
    • 在配置文件settings.py中配置IMAGES_STORE,这个配置是用来设置文件下载来的路径(存储到本地的目录文件);
    • settings.py中的ITEM_PIPELINES中注册pipeline

      scrapy.pipelines.images.ImagesPipeline: 1
      

四、使用自带的pipeline下载改写上面的代码

  • 1、定义items.py文件

    class CarItem(scrapy.Item):
        category = scrapy.Field()
        # 下面两个字段必须要有,别的字段就看业务需要
        image_urls = scrapy.Field()
        images = scrapy.Field()
    
  • 2、修改spider爬虫文件

    import scrapy
    from urllib import parse
    
    from jobbloe.items import CarItem
    
    class CarSpider(scrapy.Spider):
        name = 'car'
        allowed_domains = ['car.autohome.com.cn']
        start_urls = ['https://car.autohome.com.cn/pic/series/65.html#pvareaid=3454438']
    
        def parse(self, response):
            uibox_list = response.xpath('//div[@class="uibox"]')[1:]
            for uibox in uibox_list:
                category = uibox.xpath('./div[@class="uibox-title"]/a/text()').extract_first()
                urls = uibox.xpath('.//img/@src').getall()
                urls = list(map(lambda url: parse.urljoin(response.url, url), urls))
                # image_urls必须是一个列表
                yield CarItem(category=category, image_urls=urls)
    
  • 3、在settings.py配置下载路径

    # 配置图片下载路径
    import os
    
    IMAGES_STORE = os.path.join(os.path.dirname(__file__), 'cat_images')
    
  • 4、激活pipeline

    ITEM_PIPELINES = {
        'scrapy.pipelines.images.ImagesPipeline': 1,
    }
    
  • 5、可能会缺少PIL模块,需要安装pillow图片包

    pip3 install pillow
    

五、如果有特殊的要求就要重写pipeline(类似重命名或者分目录)

  • 1、导包

    from scrapy import Request
    from scrapy.exceptions import DropItem
    from scrapy.pipelines.images import ImagesPipeline
    
  • 2、书写pipeline代码

    class CarImagePipeline(ImagesPipeline):
        """
        定义一个下载图片的pipeline
        """
    
        def file_path(self, request, response=None, info=None):
            """
            用来存储图片的路径
            :param request:
            :param response:
            :param info:
            :return:
            """
            url = request.url
            file_name = url.split('/')[-1]
            return file_name
    
        def item_completed(self, results, item, info):
            image_paths = [x['path'] for ok, x in results if ok]
            if not image_paths:
                raise DropItem('Image Downloaded Failed')
            return item
    
        def get_media_requests(self, item, info):
            """
            这个函数是先获取item中过来的image_urls重新发送请求
            :param item:
            :param info:
            :return:
            """
            for url in item['image_urls']:
                yield Request(url)
    
  • 3、激活pipeline

  • 4、更多可以参考

results matching ""

    No results matching ""