前言
我们在使用Scrapy爬取资料时,除了获取页面看得到的文本信息外,经常会遇到需要下载图片或文件的情况,这个时候就可以使用Scrapy自带的下载起来下载,下面我们来学习一下Scrapy里面图片和文件下载器的使用。
应用案例
简书上有对FilesPipeline和ImagesPipeline简单明了的介绍,如下引用:
FilesPipeline的工作流如下:
- 在
spider
中爬取要下载的文件链接,将其放置于item中的file_urls
;spider
将其返回并传送至pipeline
链;- 当
FilesPipeline
处理时,它会检测是否有file_urls
字段,如果有的话,会将url传送给scarpy调度器和下载器;- 下载完成之后,会将结果写入item的另一字段
files
,files
包含了文件现在的本地路径(相对于配置FILE_STORE
的路径)、文件校验和checksum
、文件的url
。从上面的过程可以看出使用
FilesPipeline
的几个必须项:
Item
要包含file_urls
和files
两个字段;打开
FilesPipeline
配置;配置文件下载目录
FILE_STORE
。……
IMagesPipeline的过程与FilePipeline差不多,参数名称和配置不一样;如下:
FilesPipelin ImagesPipeline Package scrapy.pipelines.files.FilesPipeline scrapy.pipelines.images.ImagesPipeline Item file_urls ,files image_urls ,images 存储路径配置参数 FILES_STROE IMAGES_STORE 除此之外,ImagesPipeline还支持以下特别功能:
生成缩略图,通过配置
IMAGES_THUMBS = {'size_name': (width_size,heigh_size),}
过滤过小图片,通过配置
IMAGES_MIN_HEIGHT
和IMAGES_MIN_WIDTH
来过滤过小的图片。
本项目既有文件下载,也有图片下载,先配置item和settings:
item中必须包含files_urls、files或image_urls、images:
#下载文件的item
class GetAisItem_Sound(scrapy.Item):
# define the fields for your item here like:
sd_name = scrapy.Field()
sd_xq_url = scrapy.Field()
sd_type = scrapy.Field()
sd_pf = scrapy.Field()
sd_createdate = scrapy.Field()
#文件下载地址
file_urls = scrapy.Field()
files = scrapy.Field()
#下载图片的item
class GetAisItem_Img(scrapy.Item):
# define the fields for your item here like:
img_name = scrapy.Field()
img_xq_url = scrapy.Field()
img_type = scrapy.Field()
img_pf = scrapy.Field()
img_createdate = scrapy.Field()
#文件下载地址
image_urls = scrapy.Field()
images = scrapy.Field()
settings中启用下载器,并配置文件或图片存储目录,下载器优先级排在前面:
ITEM_PIPELINES = {
#'scrapy.pipelines.files.FilesPipeline':1,
'get_ais.pipelines.DownFilesPipeline':2,
'get_ais.pipelines.DownImagesPipeline':3,
'get_ais.pipelines.GetAisPipeline': 300,
}
#文件下载路径
FILES_STORE = basedir +'/filedata/sound'
#避免下载90天内下载过的文件
FILES_EXPIRES = 90
IMAGES_STORE = basedir +'/filedata/img'
#避免下载90天内下载过的图片
IMAGES_EXPIRES = 90
#单文件大小限制kb
DOWNLOAD_WARNSIZE=3355443200
可以看到启用的下载器并不是FilesPipeline和ImagesPipeline,是因为下载器为了避免重名文件覆盖会按照一个加密方式重新生成文件名来保存文件,而重新生成的文件名非常不直观,因此我们集成FilesPipeline和ImagesPipeline写了新的下载器类,用来重命名文件,具体如下:
#在Pipelines.py里面继承并重写下载文件的类
class DownFilesPipeline(FilesPipeline):
#本函数返回文件保存路径和名字,修改此方法可以修改文件名
def file_path(self, request, response=None, info=None):
#request是下载连接的请求,request.url就是file_urls
try:
#从下载连接中提取域名后的文件名
file_path = "".join(re.findall("https://mmssxs1.com/([\w\W]+)",request.url))
#由于中文作为url时会被quote成url的格式,因此需要unquote变回中文
file_path_name = unquote(file_path,encoding='utf-8')
#替换掉不能作为文件名的字符
filename = re.sub(r'[\/:*?"<>|]', '_',file_path_name)
logging.info('文件名%s'%filename)
return filename
except Exception as e:
logging.info('错误信息%s'%e)
有的时候,下载链接的文件名也不太直观,我们想用spider爬取的到其他信息作为文件名,这个时候需要重写get_media_requests,将item传递给file_path,如下案例:
#在Pipelines.py里面继承并重写下载图片的类
class DownImagesPipeline(ImagesPipeline):
# 这个方法是在发送下载请求之前调用的,其实这个方法就是去发送下载请求的
def get_media_requests(self, item, info):
#获取请求对象和item
request_objs = super(DownImagesPipeline, self).get_media_requests(item, info)
for request_obj in request_objs:
#在每个下载url请求前,往请求中添加item
request_obj.item = item
return request_objs
def file_path(self, request, response=None, info=None):
try:
#从请求中获取item里面spider爬取的文件主题
img_name = request.item.get('img_name')
num = unquote("".join(re.findall(".com/([\w\W]+)",request.url)))
#用文件主题加上url的文件名称作为下载后的文件名称
file_path_name=img_name[0]+'_'+num
filename = re.sub(r'[\/:*?"<>|]', '_',file_path_name)
logging.info('文件名%s'%filename)
return filename
except Exception as e:
logging.info('错误信息%s'%e)