目的
最近在使用 Scrapy 框架下载视频文件到本地存储,用到 Scrapy 的 Files Pipeline 。有关该 Pipeline 的具体文档 可见官方文档, 在这里主要是将在使用该中间件的一些配置记录下来。
配置
items.py文件
class YouItem(scrapy.Item):
file_urls = scrapy.Field()
files = scrapy.Field()
# other fields
settings.py文件
FILES_STORE = 'File download address'
FILES_URLS_FIELD = 'file_urls' # 这里对应着item.py文件中的字段
FILES_RESULT_FIELD = 'files' # 同样对应item.py文件中的字段
# 120 days of delay for files expiration
FILES_EXPIRES = 120
# 需要在ITEM_PIPELINES中启用该中间件
ITEM_PIPELINES = {
'scrapy.pipelines.files.FilesPipeline': 1,
}
your_spider.py文件
# 该函数的功能是解析视频的链接
def parse_video_url(self, response):
item = YourItem()
file_urls = response.xpath("xpath表达式").extract()
item['file_urls'] = file_urls # 这里的file_urls是一个list
return item
关于自定义文件名
使用 Scrapy 的 FilesPipeline 下载文件时,文件名将下载链接进行 hash,然后将 hash 值作为文件名, 但是如果链接后有查询字符串(http://host.com/media/**.mp4?a=1&b=2),那么文件名的格式就是
<settings.py中定义的下载目录>/download/full/<hash值>.mp4?a=1&b=2
我们可以看一下 FilePipline 的源码
def file_path(self, request, response=None, info=None):
## start of deprecation warning block (can be removed in the future)
def _warn():
from scrapy.exceptions import ScrapyDeprecationWarning
import warnings
warnings.warn('FilesPipeline.file_key(url) method is deprecated, please use '
'file_path(request, response=None, info=None) instead',
category=ScrapyDeprecationWarning, stacklevel=1)
# check if called from file_key with url as first argument
if not isinstance(request, Request):
_warn()
url = request
else:
url = request.url
# detect if file_key() method has been overridden
if not hasattr(self.file_key, '_base'):
_warn()
return self.file_key(url)
## end of deprecation warning block
media_guid = hashlib.sha1(to_bytes(url)).hexdigest() # change to request.url after deprecation
media_ext = os.path.splitext(url)[1] # change to request.url after deprecation
# 在这里media_guid是hash后的值,media_ext就是剩余的包含查询字符串
return 'full/%s%s' % (media_guid, media_ext)
所以我们如果想自定义文件名可以继承FilesPipeline,然后重写部分函数
pipelines.py文件
class CusFilesPipeline(FilesPipeline):
def get_media_requests(self, item, info):
"""
注意:
如果你不想使用url的hash值作为文件名,而是想使用item中的key对应的value作为文件名
需要使用meta将部分参数添加到request中
例如我们item中有video_name字段,我想在自定义视频名时用该字段最为文件名可以这样做
return [Request(x, meta={'video_name': item.get('video_name', None))}) for x in item.get(self.files_urls_field, [])]
"""
return [Request(x}) for x in item.get(self.files_urls_field, [])]
def file_path(self, request, response=None, info=None):
def _warn():
from scrapy.exceptions import ScrapyDeprecationWarning
import warnings
warnings.warn('FilesPipeline.file_key(url) method is deprecated, please use '
'file_path(request, response=None, info=None) instead',
category=ScrapyDeprecationWarning, stacklevel=1)
# check if called from file_key with url as first argument
if not isinstance(request, Request):
_warn()
url = request
else:
url = request.url
# detect if file_key() method has been overridden
if not hasattr(self.file_key, '_base'):
_warn()
return self.file_key(url)
media_guid = hashlib.sha1(to_bytes(url)).hexdigest() # change to request.url after deprecation
media_ext = os.path.splitext(url)[1] # change to request.url after deprecation
# 这里我们使用自定义的文件名,如果meta中没有video_name,就使用url的hash值作为文件名
return 'full/%s.mp4' % (''.join(request.meta.get('video_name', media_guid).split(' ')))
在settings.py文件中修改相相应配置
ITEM_PIPELINES = {
'yourproject.pipelines.CusFilesPipeline': 1, # 继承FilesPipline,自定义文件名
}