下载图片文件
1, scrapy 框架下载文件(包括图片)有自己的一套解决方案,比我们使用 urlretrieve 更加优势
2. 避免重新下载最近已经下载的文件,指定过期时间,比如一天,一天以内就不会再重复下载了
3. 可以方便的指定文件储存的路径
4. 可以将下载的图片转换成通用的格式,比如PNG JPEG
5. 可以方便的检测图片的宽与高,确保他们满足最小的限制。
6. 可以方便的生成缩略图
7. 异步下载,效率max
8. 更多参考官方文档吧脑部链接
案例:站酷图片下载
- 定义item 上面有两个字段,一个是image_urls,一个是images.其中image_urls 是用来储存图片的链接,有开发者吧数据爬下来后添加的。
- 使用scrapy.pipelines.images.ImagesPipeline 来作为数据保存pipline
- 再 settings.py文件中设置 IMAGES_SOTRE来定义图片下载的路径
- 如果想要有更复杂的图片保存路径需要,可以重写ImagePipeline 的 file_path方法,这个方法用于返回每个图片的保存路径。
然后创建项目开写,,,
- item
import scrapy
class ZcoolItem(scrapy.Item):
image_urls = scrapy.Field() # 这两个是必须的,第一个用来保存这个item上的图片的链接
image = scrapy.Field() # 在图片下载完后,形成image对象在保存到这个上面
title = scrapy.Field()
- spider
import scrapy
from scrapy.spiders.crawl import CrawlSpider,Rule # 如果忘记了路径可以按两下shift再整个项目里搜索
from scrapy.linkextractors import LinkExtractor # 这里还是把普通的改成crawlspider好,,
from ..items import ZcoolItem
class ZcoolSpiderSpider(CrawlSpider):
name = 'zcool_spider'
allowed_domains = ['zcool.com.cn']
start_urls = ['http://zcool.com.cn/']
rules={
Rule(LinkExtractor(allow=r'https://www.zcool.com.cn/?p=\d+#tab_anchor'),follow=True), # 翻页的url
Rule(LinkExtractor(allow=r'https://www.zcool.com.cn/work/.+html'),follow=False,callback='parse_detail')
}
# 如果使用的是CrawlSpider 这个类就不能重写parse方法,因为外再底层会自动调用这个方法,如果重写了就会报错。
def parse_detail(self, response):
image_urls = response.xpath(r'//div[@class="work-show-box js-work-content"]//img/@src').getall()
title = response.xpath(r'//div[@class="details-contitle-box"]/h2/text()').get().strip()
item = ZcoolItem(title=title,image_urls=image_urls) # image 不用我们管,scrapy自己帮我设置
yield item
- settings
除了请求头,不遵守协议,这里由于是要保存图片,直接用’scrapy.pipelines.images.ImagesPipeline’这个已经给我们写好的管道就行(也就是说我们不用再自己写pipeline了。。。。)
import os
ITEM_PIPELINES = {
'zcool.pipelines.ZcoolPipeline': 300,
'scrapy.pipelines.images.ImagesPipeline': 1 # 其实这只有一个队列,优先级多少都一样
}
IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)),'image')
# __file__代表了这个settings文件,找到他爷爷那层目录。。在拼接成我们想保存的路径。其实'D:\scrapy 框架\zcool\image' 也一样
虽然不用写pipeline感觉不错,但是这个方法的问题就是所有的图片都保存再了full 这个文件里,,但是我们想分类的。所有我们就需要重写ImagePipeline 的 file_path方法
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这个文件夹里
首先来修改下settings
import os
ITEM_PIPELINES = {
'zcool.pipelines.ZcoolPipeline': 300, # 因为我们要重写pipeline,所以使用我们自己的就行
}
IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)),'image')
# 这个参数是必须的,因为ImagesPipeline 还是要从settings 里提取这个
pipeline
from scrapy.pipelines.images import ImagesPipeline
import os,re
class ZcoolPipeline(ImagesPipeline):
# 我们要分类,就需要title,title就从item来,但是file_path传不进去item,,就把item和request绑定一起
# def get_media_requests(self, item, info): # 源码,再请求发送之前调用
# return [Request(x) for x in item.get(self.images_urls_field, [])]
def get_media_requests(self, item, info): # 把item与request绑定,
media_requests = super(ZcoolPipeline, self).get_media_requests(item,info)
for media_request in media_requests: # 这是把每个图片的请求都绑定上item
media_request.item = item
return media_requests
def file_path(self, request, response=None, info=None): # 再图片下载之后调用
origin_path = super().file_path(request,response,info) # 父类的路径
title = request.item['title']
title = re.sub(r'[\\/:\*\?<>\|"]','',title) # 文件名格式嘛,,不能有\/:*?<>|
save_path = os.path.join('D:\scrapy 框架\zcool\image','image'),title)
if not os.path.exists(save_path):
os.mkdir(save_path)
image_name = origin_path.replace('full/','') # 需要图片的文件名,,
return os.path.join(save_path,image_name)