scrapy框架Images Pipeline下载图片(重写方法来指定下载位置)

使用scrapy框架下载图片

先介绍一下os模块:

import os即可

  • 使用os.path.dirname (__ file__) 可以查看当前文件所在的目录,以如下目录为例:
    目录
    使用os.path.dirname(__ file__)得到的是第二个bmw(即蓝色框下面的那个)目录,假如我们想要在第一个bmw下面创建一个images文件夹,应该怎么做呢?
    所以我们使用os.path.dirname(os.path.dirname(file))语句,如此得到的就是第二个bmw所在的目录了(也就是第一个bmw下)。

  • os.path.join语句可以将目录和想要创建的文件夹组成一个新的目录,如:os.path.join(os.path.dirname(os.path.dirname(file)), ‘images’)
    这句话的作用就是将以后的文件保存在第一个bmw下的images文件夹中。

Images Pipeline

相关配置
要想使用这个东西,先在item中设置两个item,分别是:

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

然后再settings中设置:


ITEM_PIPELINES = {
   # 'bmw.pipelines.BmwPipeline': 300,
   'scrapy.pipelines.images.ImagesPipeline':1
}

注意,此时pipelines中的class BmwPipeline(默认创建的)不会再被执行

存储路径:

IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')

使用 Images Pipeline对汽车之家进行爬取

对原始程序进行上述配置之后,确实可以下载图片,但缺点是无法根据每张图片所属的标签进行分类下载。比如说,某张图片是属于"车厢座椅"标签的,但这个程序没有办法单独创建一个"车厢座椅"文件夹进行下载。

下面解决这个问题:

要想改变存储的路径,就要看看路径是在哪里确定下来的,这个类是ImagesPipeline,在里面发现了file_path这个方法,它确定了图片的路径,因此重写ImagesPipeline(位于scrapy.pipelines.images.ImagesPipeline)也许可以,进去后查看:

  • file_path
    def file_path(self, request, response=None, info=None):
        ## start of deprecation warning block (can be removed in the future)
        def _warn():
            from scrapy.exceptions import ScrapyDeprecationWarning
            import warnings
            warnings.warn('ImagesPipeline.image_key(url) and file_key(url) methods are deprecated, '
                          'please use file_path(request, response=None, info=None) instead',
                          category=ScrapyDeprecationWarning, stacklevel=1)

        # check if called from image_key or file_key with url as first argument
        if not isinstance(request, Request):
            _warn()
            url = request
        else:
            url = request.url

        # detect if file_key() or image_key() methods have been overridden
        if not hasattr(self.file_key, '_base'):
            _warn()
            return self.file_key(url)
        elif not hasattr(self.image_key, '_base'):
            _warn()
            return self.image_key(url)
        ## end of deprecation warning block

        image_guid = hashlib.sha1(to_bytes(url)).hexdigest()  # change to request.url after deprecation
        return 'full/%s.jpg' % (image_guid)

可以看到,最后返回的是’full/%s.jpg’ % (image_guid),即full文件夹下的文件名字。因此我们可以重写这个方法,那就得搞一个新的类,让其继承ImagesPipeline,进而重写一些方法,来让图片下载到对应的文件夹中。

但只有item知道每张图片属于哪个标签,而只重写file_path无法拿到item,因此想办法将item和file_path联系起来。

于是看到下面的方法:

  • get_media_requests
    def get_media_requests(self, item, info):
        return [Request(x) for x in item.get(self.images_urls_field, [])]

我们再重写这个方法,新构建的类如下:

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=None, info=None)
        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

通过重写get_media_requests,将item和每个request联系起来,然后再重写file_path通过requset.item将category提取出来就能达到目的。

从settings中导入IMAGES_STORE,获得images文件夹的位置,然后通过前面说过的os.path.join方法将images文件夹和每个请求的category组成新的目录(如:images/车身外观),然后通过if语句判断是否存在图片所属的文件夹,如果不存在则创建。最后通过replace将之前的full文件夹去掉只剩图片名,再通过os.path.join将图片名和其所属的文件夹组合在一起。

遇见的问题

在重写get_media_requests时,用了一个for循环给每个request执行了 request_obj.item = item,我通过dir(request)查看了request的属性和方法,发现并没有item这个条目,于是产生疑问,为啥没有这个属性还能加上呢?

后来我写了如下代码:

class Father:
    def __init__(self, a, b):
        self.x = a
        self.y = b

    def show(self, q):
        print(dir(self))


f = Father(1, 2)
f.show(5)
f.item = 3
f.show(5)
输出结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'item', 'show', 'x', 'y']

可以看到,虽然类里面没有item这个条目,但可以在类外人为的添加,不知道重写get_media_requests方法时用到的技巧是不是这个道理,验证如下:

    def get_media_requests(self, item, info):
        request_objs = super(BMWImagesPipeline, self).get_media_requests(item, info)
        print(dir(request_objs[1]))
        for request_obj in request_objs:
            request_obj.item = item

        print(dir(request_objs[1]))
        return request_objs

在执行循环之前和之后分别输出request的属性和方法,输出结果如下:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_body', '_encoding', '_get_body', '_get_url', '_meta', '_set_body', '_set_url', '_url', 'body', 'callback', 'cookies', 'copy', 'dont_filter', 'encoding', 'errback', 'flags', 'headers', 'meta', 'method', 'priority', 'replace', 'url']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_body', '_encoding', '_get_body', '_get_url', '_meta', '_set_body', '_set_url', '_url', 'body', 'callback', 'cookies', 'copy', 'dont_filter', 'encoding', 'errback', 'flags', 'headers', 'item', 'meta', 'method', 'priority', 'replace', 'url']

确实如此,看来还是自己对python了解的太浅了,不过以后碰见类似的需求倒是可以应用一下。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值