我们除了爬取文本信息, 有的时候还需要爬媒体信息, 比如视频图片音乐等. 就拿 B 站来说, 我的收藏夹内的视频可能随时会失效, 所以把它们下载到本地是非常保险的一件事.
对于这种大量列表型的数据, 可以猜测 B 站收藏夹的请求中, 详细的收藏详细可能会是异步加载的, 因为这部分数据可能比较庞大.
我们来分析一下网络请求.
可以看到对收藏夹的请求是指定 URL 加收藏夹的 id 号, 我们爬取的前提是这个收藏夹是公共收藏夹, 不然是无法访问的.
再来看他的返回, 明显没有收藏视频的信息, 所以可以判断收藏视频的信息是通过 API 接口异步加载的.
详细查看一下其他的请求, 你会发现这样一条.
可以看到这里返回了 JSON 数据, 内容就是我们收藏夹中的视频, 但是这里这有 20 个, 再来看请求的 URL.
media_id 是收藏夹的 id 号, pn 是代表哪一分页, ps 是当前分页中视频数量.
那么我们就可以调用这个 API 来拿到所有收藏的视频了.
我们的视频分页当然不可能只有一页, 所以我们可以遍历 pn 递增.
- i = 1
- while 1 :
- url = 'https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=88854277&pn='+ str(i) +'&ps=20&keyword=&order=mtime&type=0&tid=0&jsonp=jsonp'
- html = requests.get(url)
- i = i + 1
- print(HTML.text)
这样就能拿到一个收藏夹下所有视频了, 当 i 超过收藏夹页数时, 直接异常退出即可.
接下来我们需要解析出每一个视频的 id.
根据之前说的 JSON 解析, 我们很容易就能用
res['data']['medias']
来获得所有的视频, 然后再根据下标解析出每一个视频.
- res = JSON.loads(HTML.text)
- len_video = len(res['data']['medias'])
- for id in range(0,len_video):
- create_thread(res['data']['medias'][id])
这样我们就可以获取当前页视频数量, 然后创建线程进行下载了, 因为下载是一个非常占 IO 的事情, 如果你单线程执行, 下载一个视频再下载另一个, 这样会很慢, 我们可以给每一个视频创建一个线程来提高速度.
- def create_thread(res):
- thread = myThread(res['id'],res['title'],res['id'])
- thread.start()
创建线程的线程号是视频的 id 号, 线程名是视频名.
- class myThread(threading.Thread): # 继承父类 threading.Thread
- def __init__(self, threadID, name, counter):
- threading.Thread.__init__(self)
- self.threadID = threadID
- self.name = name
- self.counter = counter
- def run(self):
- download_video(self.threadID)
线程类如上, 里面有两个函数,__init__是默认的线程初始化函数, 里面就是我们创建线程时传入的 id 和 name. 第二个函数是线程执行时的 run 方法, 也就是我们定义线程的具体要做的事, 里面只有一个 download_video 方法.
- # 下载视频
- def download_video(av_id):
- os.system('you-get -o d:/vedio/ https://www.bilibili.com/video/av'+str(av_id))
在下载函数中, 我们可以调用 you-get 来帮助我们解析下载视频(不要问为什么调 you-get, 自己解析太麻烦了).
这样我们就完成了.
下载完成后:
源码地址: https://github.com/CasterWx/VideoDown
来源: http://www.bubuko.com/infodetail-3216618.html