问题描述
在学习使用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()