爬虫爬取图片需要从scrapy.pipelines.images模块中调用ImagesPipeline来进行图片的下载和存取。
在爬取王者荣耀各英雄皮肤时,我将一个英雄所有皮肤图片的url存在列表中,想要把同一个英雄的皮肤爬取下来放在一个文件夹中。但是每次提交下载请求的返回值不能是列表值,也就意味着一次调用WzryImgPipeline只能下载一次图片。由于图片下载后,还需要进行更名操作,需要获取皮肤图片的英雄名和皮肤名信息。那该怎么办呢?
def get_media_requests(self, item, info):
return [Request(x) for x in item.get(self.images_urls_field, [])]
get_media_requests用来发送下载请求,images_urls_field储存的是图片的url,需要在items.py里设置。
def item_completed(self, results, item, info):
if isinstance(item, dict) or self.images_result_field in item.fields:
item[self.images_result_field] = [x for ok, x in results if ok]
return item
通过scrapy爬取框架爬下来的图片名是SHA1值,若要更改文件名,可通过重写item_completed实现。
item_completed函数的第二个参数results的格式是list [(DownLoad_success_or_failure,dict)],dict中含有三个键:1、'url':图片路径 2、'path':图片下载后的保存路径 3、'checksum':校验码。
下面是应用于爬取链家二手房图片的修改图片名代码:
class LianjiaImgPipeline(ImagesPipeline):
def get_media_requests(self,item,info):
for image_url in item['image_urls']:
yield scrapy.Request(image_url)
def item_completed(self,results,item,info):
#取results图片信息里,图片路径的值
print ('******the results is********:',results)
image_path = [x['path'] for ok,x in results if ok]
if not image_path:
raise DropItem("Item contains no images")
os.rename(images_store+image_path[0],images_store+item['title']+'.jpg')
#os.rename(IMGS + '/' + results[0][1]['path'], IMGS + '/' + item['img_name'])
return item
def file_path(self, request, response=None, info=None):
image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()
return 'full/%s.jpg' % (image_guid)
这个方法是在图片将要被储存时调用,用来获取图片存储的全部本地路径。观察代码可发现图片的存储形式是在full文件夹下,以"%s.jpg"命名的,%s为图片的sha1值。因此,修改file_path函数也可以对图片重命名。但是这个方法相对于重写item_completed来说,对程序的破坏较大,不是必须用时还是用item_completed比较好。因为在爬取王者荣耀英雄皮肤图片时,需要把不同英雄的皮肤保存到不同的文件夹,所以下面选用了重写file_path。
调用父类方法,将所有列表里的元素全部提取出来,变为一个个request对象,再给对象传入hero,name参数,便于重写file_path方法改名。
class WzryImgPipeline(ImagesPipeline):
#此方法是在发送下载请求之前调用,其实此方法本身就是去发送下载请求
def get_media_requests(self,item,info):
#调用原父类方法,发送下载请求并返回的结果(request的列表)
request_objs=super().get_media_requests(item,info)
#给每个request对象带上meta属性传入hero、name参数,并返回
for request_obj,num in zip(request_objs,range(0,len(item['skins']))):
request_obj.meta['hero']=item['hero']
request_obj.meta['skin']=item['skins'][num]
return request_objs
#此方法是在图片将要被存储时调用,用来获取这个图片存储的全部路径
def file_path(self,request,response=None,info=None):
#获取request的meta属性的hero作为文件夹名称
hero=request.meta.get('hero')
#获取request的meta属性的skin并拼接作为文件名称
image=request.meta.get('skin')+'.jpg'
#获取IMAGES_STORE图片的默认地址并拼接!!!!不执行那一步
hero_path=os.path.join(images_store,hero)
#判断地址是否存在,不存在则创建
if not os.path.exists(hero_path):
os.makedirs(hero_path)
#拼接文件夹地址与图片名存储的全部路径并返回!!!!原方法
image_path=os.path.join(hero_path,image)
return image_path
参考链接:https://blog.csdn.net/weixin_45335208/article/details/103351806