干活干活,区区懒癌已经阻挡不了澎湃的洪荒之力了...... python
运行环境:Windows基于python3.6windows
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------浏览器
抓取视频时遇到M3U8的确挺烦人的,去年年末实习,因为项目需求因此和一个同事主攻蟒蛇爬虫,抓取含有清晰人脸的图片和视频,在爬取一些视频网站和直播网站时就被“它”糊了一脸,做为一只,呃,不对,是两只刚入爬虫坑的菜鸟,在视频加载播放时找不到啥关于的.mp4,.MKV的连接,反而出现很多.ts的连接,而后爬取找度娘,重点以下:服务器
M3U8是苹果公司推出一种视频播放标准,是一种文件检索格式,将视频切割成一小段一小段的TS格式的视频文件,而后存在服务器中(如今为了减小I / O访问次数,通常存在服务器的内存中),经过M3U8解析出来路径,而后去请求,是如今比较流行的一种加载方式,诸如腾讯视频之类大多都是切割成TS流进行加载。ide
但当时仍是晕,理论和实际处理是两回事,那时候关于如何爬取M3U8的博客不是不少并且不多说到重点(给俺下载代码,理论改天再补)样么是其它语言写的更懵,近来却是愈来愈多了(惋惜当时没遇上),最后找到篇关于下载的却发现是python2的代码,由于那时候刚开始使用蟒以前学的是Java中,因此调了好一会仍是运行失败,可是也收获了很多,以后和同事研究了两天试验了几个方案最终肯定采用边下边合的方式进行处理,上个月底从公司离职了,因为公司管理较为严格禁止向外网(公司局域网之外)发送消息或拷贝文件,(嗯,拍照也不行,就似〜这么严)因此这几天晚上下班回来抽空写写改改又另起了炉灶,代码以下,随意写的因此异常处理和模拟浏览器啥的就没加了(懒)!工具
注:仅限窗口下使用,若是要在Linux的上使用须要修改合并命令,嗯,或者等几天我再来篇兼容的网站
# !/user/bin/env python # -*- coding: utf-8 -*- # au: caopeiya # 20180808 import os, shutil import urllib.request, urllib.error, requests # 打开并读取网页内容 def getUrlData(url): try: urlData = urllib.request.urlopen(url, timeout=20) # .read().decode('utf-8', 'ignore') return urlData except Exception as err: print(f'err getUrlData({url})\n', err) return -1 # 下载文件-urllib.request def getDown_urllib(url, file_path): try: urllib.request.urlretrieve(url, filename=file_path) return True except urllib.error.URLError as e: # hasttr(e, 'code'),判断e 是否有.code属性,由于不肯定是否是HTTPError错误,URLError包含HTTPError,可是HTTPError之外的错误是不返回错误码(状态码)的 if hasattr(e, 'code'): print(e.code) # 打印服务器返回的错误码(状态码),如403,404,501之类的 elif hasattr(e, 'reason'): print(e.reason) # 打印错误缘由 def getVideo_urllib(url_m3u8, path, videoName): print('begin run ~~\n') # urlData = getUrlData(url_m3u8).readlines() urlData = getUrlData(url_m3u8) num = 0 tempName_video = os.path.join(path, f'{videoName}.ts') # f'{}' 至关于'{}'.format() 或 '%s'%videoName # print(urlData) for line in urlData: # 解码,因为是直接使用了所抓取的连接内容,因此须要按行解码,若是提早解码则不能使用直接进行for循环,会报错 # 改用上面的readlines()或readline()也能够,但更繁琐些,一样须要按行解码,效率更低 url_ts = line.decode('utf-8') tempName_ts = os.path.join(path, f'{num}.ts') # f'{}' 至关于'{}'.format() if not '.ts' in url_ts: continue else: if not url_ts.startswith('http'): # 判断字符串是否以'http'开头,若是不是则说明url连接不完整,须要拼接 # 拼接ts流视频的url url_ts = url_m3u8.replace(url_m3u8.split('/')[-1], url_ts) print(url_ts) getDown_urllib(url_ts, tempName_ts) # 下载视频流 if num == 0: # 重命名,已存在则自动覆盖 shutil.move(tempName_ts, tempName_video) num += 1 continue cmd = f'copy /b {tempName_video}+{tempName_ts} {tempName_video}' res = os.system(cmd) if res == 0: os.system(f'del {tempName_ts}') if num == 20: # 限制下载的ts流个数,这个视频挺长有四百多个.ts文件,因此限制一下 break num += 1 continue print(f'Wrong, copy {num}.ts-->{videoName}.ts failure') return False os.system(f'del {path}/*.ts') # 调用windows命令行(即cmd)工具,运行命令 filename = os.path.join(path, f'{videoName}.mp4') shutil.move(tempName_video, filename) print(f'{videoName}.mp4 finish down!') if __name__ == '__main__': url_m3u8 = 'http://wscdn.alhls.xiaoka.tv/201886/2f5/75a/HoHdTc1LjUaBjZbJ/index.m3u8' path = r'D:\videos' videoName = url_m3u8.split('/')[-2] getVideo_urllib(url_m3u8, path, videoName)
注:修改文件名时,特地选择shutil模块(能够看做操做系统的高级版)的移动方法,虽然移动主要是用来移动文件的,重命名算是附带的,不过强制覆盖的特色在这里颇有用,避免中断后从新下载时重命名产生异常。url
PS:说来有趣,7月31号,也就是离职的那天上午,灵感突显,利用请求下载文件的写入特色,完全解决了调用命令行致使的不兼容的窗口之外环境的问题,哈哈,因此下一篇就它了。spa