Windows下Scrapy下载图片,指定图片名报错[WinError32]的解决办法

来源:互联网 发布:淘宝退货一般多久到账 编辑:程序博客网 时间:2024/06/18 16:51

问题描述

在学习使用scrapy爬虫框架的时候,我根据一个网上的demo,编写了一个爬取某网站主播头像的spider,并希望爬取的图片以主播的名字来命名。

存储图片的思路是使用scrapy自带的ImagesPipeline类下载图片,通过重写get_media_request()方法和item_completed()方法,来实现下载、保存图片后再重命名的流程。

代码如下:

    class ImagePipeline(ImagesPipeline):  # 自定义一个图片下载类    IMAGES_STORE = get_project_settings().get('IMAGES_STORE')    # 将要下载的图片url创建请求提交给引擎    def get_media_requests(self, item, info):        # 一个item只下载一张图片        yield scrapy.Request(item['image_link'])    def item_completed(self, results, item, info):        """        当一个单独项目中的所有图片请求完成时(要么完成下载,要么因为某种原因下载失败),该方法将被调用。        """        # 获取图片的下载路径,使用了列表生成式        image = [data['path'] for ok, data in results if ok]  # 一个元组就没有括号了        #              ↑ 这个path指的是result中的key        # 拼接原始图片的路径        old_name = self.IMAGES_STORE + os.sep + image[0]        # 拼接新的路径        new_name = self.IMAGES_STORE + os.sep + image[0].split(os.sep)[0] + os.sep + item['nick_name'] + '.jpg'        os.rename(old_name, new_name)        item['image_path'] = new_name        return item

整段代码在Linux中运行无问题,然而在Windows环境下出现了错误:

Traceback (most recent call last):  File "f:\python\python36\lib\site-packages\twisted\internet\defer.py", line 653, in _runCallbacks    current.result = callback(current.result, *args, **kw)  File "D:\MyProjects\pipelines.py", line 53, in item_completed    os.rename(old_name, new_name)PermissionError: [WinError 32] 另一个程序正在使用此文件,进程无法访问。: 'D:\\MyProjects\\img_cach\\full/d8c779ea0c9a7a03a09ab05151796977ac5fc738.jpg' -> 'D:\\MyProjects\\img_cach\\full/d8c779ea0c9a7a03a09ab05151796977ac5fc738.jpg\\熙珍爱吃肉.jpg'

bug原因推测是os.rename时图片仍未close(),Windows不允许两个进程去操作同一个文件。

解决办法

因系统和框架的限制,“先下载好图片在重命名”的思路走不通(或者说屏我的半瓶水走不通了【囧】),那么在保存图片时就设定好图片名即可。
即重写file_path()方法。代码如下

class ImagePipeline(ImagesPipeline):    IMAGES_STORE = get_project_settings().get('IMAGES_STORE')    # 将要下载的图片url创建请求提交给引擎    def get_media_requests(self, item, info):        yield scrapy.Request(item['image_link'], meta={"item": item, 'pic_name': item['nick_name']})    def file_path(self, request, response=None, info=None):        """        重写图片的存储路径。        :param request:        :param response:        :param info:        :return: 字符串形式的文件存储路径,相对于 IMAGES_STORE 的相对路径        """        pic_name = request.meta['pic_name']        pic_path = pic_name + '.jpg'        return pic_path

这次涉及的三个方法,其调用顺序应为:
get_media_request()

file_path()

item_completed()