scrapy 自带和自定义下载图片和文件

scrapy 下载文件和图片

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

为什么选择 scrapy 内置的下载文件方法

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

不用 scrapy自带的图片下载方式下载图片

  • image_baoma.py
import scrapy

from test_spider.items import TestSpiderItem


class ImageBaomaSpider(scrapy.Spider):
    name = 'image_baoma'
    allowed_domains = ['car.autohome.com.cn/pic/series/65.html#pvareaid=3454438']
    start_urls = ['http://car.autohome.com.cn/pic/series/65.html#pvareaid=3454438/']

    def parse(self, response):
        uiboxs = response.xpath('//div[@class="row"]//div[@class="uibox"]')[1:]
        for uibox in uiboxs:
            img_urls = uibox.xpath('.//li/a//img/@src').getall()
            img_names = uibox.xpath('.//li/div/a/text()').getall()
            type_name = uibox.xpath('./div[@class="uibox-title"]/a[1]/text()').get()
            # request.urlretrieve(image_url, image_name)
            items = TestSpiderItem(image_url=img_urls, image_name=img_names, type_name=type_name)
            yield items

  • items.py
import scrapy


class TestSpiderItem(scrapy.Item):

    image_url = scrapy.Field()
    image_name = scrapy.Field()
    type_name = scrapy.Field()
  • pipelines.py
import os

from urllib import request


class TestSpiderPipeline(object):

    def __init__(self):
        # 当前文件所在目录路径
        # os.path.dirname(__file__)
        # 拼接路径
        # os.path.join(path, dirname)
        self.path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'bmw_imgs')
        # 判断 path 路径是否存在,如果不存在,创建该路径
        if not os.path.exists(self.path):
            os.mkdir(self.path)

    def process_item(self, item, spider):
        image_urls = item['image_url']
        image_names = item['image_name']
        type_name = item['type_name']
        type_path = os.path.join(self.path, type_name)
        if not os.path.exists(type_path):
            os.mkdir(type_path)
        for index, image_url in enumerate(image_urls):
            img_url = 'https:' + image_url
            postfix = image_url.split('.')[-1]
            img_name = image_names[index] + str(index) + '.' + postfix
            request.urlretrieve(img_url, os.path.join(type_path, img_name))
        return item

scrapy 自带图片下载方式

下载文件的 files pipeline

  • 当使用 files pipeline 下载文件时,按以下步骤完成:
    • 定义好一个 item,然后在这个 item 中定义两个属性,分别是 file_urls 以及 files,file_urls 是用来存储需要下载的文件的 url 链接,需要给一个列表
    • 当文件下载完成后,会把文件下载的相关信息存储到 item 的 files 属性中,比如下载路径,下载的 url 以及文件的验证码等
    • 在配置文件 settings.py 中配置 FILES_STORE,这个配置是用来设置文件下载下来的路径
    • 启动 pipeline:在 ITEM_PIPELINES 中设置 scrapy.pipelines.files.FilesPipeline: 1

下载图片的 images pipeline

  • 当使用 Images Pipeline 下载文件时,按以下步骤完成:
    • 定义一个 item,然后在这个 item 中定义两个属性,分别是 image_urls 以及 images,image_urls 是用来存储需要下载的图片的 url 链接,需要给一个列表
    • 当文件下载完成后,会把文件下载的相关信息存储到 item 的 images 属性中,比如下载的路径,下载的 url 以及图片的校验码等
    • 在配置文件 settings.py 中配置 IMAGES_STORE,这个配置时用来设置图片下载下来的路径
    • 启动 pipeline:在 ITEM_PIPELINES 中设置 scrapy.pipelines.images.ImagesPipeline: 1

scrapy 自带的下载图片方式

  • image_baoma.py
import scrapy

from test_spider.items import TestSpiderItem


class ImageBaomaSpider(scrapy.Spider):
    name = 'image_baoma'
    allowed_domains = ['car.autohome.com.cn/pic/series/65.html#pvareaid=3454438']
    start_urls = ['http://car.autohome.com.cn/pic/series/65.html#pvareaid=3454438/']

    def parse(self, response):
        uiboxs = response.xpath('//div[@class="row"]//div[@class="uibox"]')[1:]
        for uibox in uiboxs:
            img_urls = uibox.xpath('.//li/a//img/@src').getall()
            # 将 img_urls 补全
            image_urls = list(map(lambda url:response.urljoin(url), img_urls))
            img_names = uibox.xpath('.//li/div/a/text()').getall()
            type_name = uibox.xpath('./div[@class="uibox-title"]/a[1]/text()').get()
            # request.urlretrieve(image_url, image_name)
            items = TestSpiderItem(image_urls=image_urls)
            yield items
  • items.py
import scrapy


class TestSpiderItem(scrapy.Item):
    image_urls = scrapy.Field()
    images = scrapy.Field()
  • settings.py
...

ITEM_PIPELINES = {
    # 300 表示优先级,值越小,优先级越高
   # 'test_spider.pipelines.TestSpiderPipeline': 300,
    # scrapy 自带的下载图片的中间件
   'scrapy.pipelines.images.ImagesPipeline': 1
}

...

# 图片的下载路径,供 images pipeline 使用
IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')

基于 scrapy 自带的图片下载方式去自定义

在这里插入图片描述

  • get(key, default=None),这里表示如果该 key 不存在,返回 [],如果存在,返回 Request(value)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 由此看出前面的 x 为 image_urls 中的每一项,所以原来的 get_media_requests() 得到的是请求每一张图片的 url 返回的结果

  • pipelines.py

import json
import os

from test_spider import settings
from urllib import request
from scrapy.pipelines.images import ImagesPipeline


class BmwImagesPipeline(ImagesPipeline):
    """
    ImagesPipeline 的 get_media_requests() 得到的是请求每一张图片的 url 返回的结果
    """

    def get_media_requests(self, item, info):
        """
        在发送下载请求之前调用,其实这个方法就是用来发送下载请求的,只不过之前的只是用到 item 的 image_url,该项目 item 还包括图片种类类 type_name,我们想要将下载的图片分类存在各自类名的文件夹下,所以将 item 赋值给每个 请求 image_url 返回的结果,然后再统一返回给下一级
        """
        # 由上面图可以看出,这里的 request_objs 是一个 urls 请求返回结果的集合
        request_objs = super().get_media_requests(item, info)
        for request_obj in request_objs:
        # 通过遍历,将每一个 item 赋值给每一个请求返回的结果
            request_obj.item = item
        return request_objs

    def file_path(self, request, response=None, info=None):
        """
        在请求之后调用,在图片将要被存储时调用,来获取图片存储的路径
        """
        path = super().file_path(request, response, info)
        type_name = request.item.get('type_names')
        images_store = settings.IMAGES_STORE
        type_name_path = os.path.join(images_store, type_name)
        if not type_name_path:
            os.mkdir(type_name_path)
        image_name = path.replace('full/', '')
        image_path = os.path.join(type_name_path, image_name)
        return image_path
  • settings.py
ITEM_PIPELINES = {
    'test_spider.pipelines.BmwImagesPipeline': 1
}

下载高清图

  • image_baoma.py
import scrapy
import re

from test_spider.items import TestSpiderItem
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class ImageBaomaSpider(CrawlSpider):
    name = 'image_baoma'
    allowed_domains = ['car.autohome.com.cn']
    start_urls = ['https://car.autohome.com.cn/pic/series/65.html']
    rules = (
        Rule(
            LinkExtractor(allow=r"https://car.autohome.com.cn/pic/series/65.+html"),
            callback='parse_page',  #需要解析页面,所以要 callback
            follow=True #有多个页,需要跟进
        ),
    )

    def parse_page(self, response):
        uibox = response.xpath('//div[@class="row"]//div[@class="uibox"]')
        type_name = uibox.xpath('./div[@class="uibox-title"]/text()').get().strip()
        srcs = uibox.xpath('.//li//img/@src').getall()
        # hrefs = uibox.xpath('.//li//a/@href').getall()
        slt_urls = list(map(lambda url: response.urljoin(url), srcs))
        # gqt_urls = list(map(lambda url: response.urljoin(url), hrefs))
        image_urls = []
        for url in slt_urls:
            if not 'upload' in url:
            # 因为 改装 下面的图片和前面不一样,没有高清图,只有缩略图,所以判断
                image_url = re.sub(r'240.+autohome', 'autohome', url)
                image_urls.append(image_url)
            else:
                image_urls.append(url)

        items = TestSpiderItem(image_urls=image_urls, type_names=type_name)
        yield items
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值