Scrapy下载mp4视频文件

Scrapy使用FilesPipeline中间件下载mp4视频

Posted by Jae on May 25, 2018

目的

最近在使用 Scrapy 框架下载视频文件到本地存储,用到 ScrapyFiles 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

关于自定义文件名

使用 ScrapyFilesPipeline 下载文件时,文件名将下载链接进行 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,自定义文件名
}

END