python小练习:网络视频下载

Published on Dec 02, 2012

python小练习:网络视频下载

互联网上有很多视频网站,提供大量视频。可是大多都要求你使用它提供的专有软件才能下载,或者根本没提供下载的地方。在linux下怎么办?总是有办法的。浏览器扩展,you-get,直接从硕鼠解析出地址自己下载……

这不是重点,重点是:自己动手玩一玩。 - 顺便学习下几个python标准库:=urllib=、=urllib2=、=re=。 - 初步学习http报头知识 Extra bonus: - flv文件结构

都是随便看看。

下载视频

简单起见,直接从硕鼠抓取地址。

首先我们随便找一个视频地址,比如来自sina的憨豆特工。我们把地址用硕鼠解析出来看看:

original_jaVP_5b4d00008fb1125d.jpg
Figure 1: 硕鼠

哇哦,一个电影被分成15个文件了。

我们接下来要做的就是抓取这十五个视频的地址并把他们下载下来。

也许,用正则是个不错的办法。查看该页面源码,可以找到这样的内容:

<input type="hidden" name="inf" value="<R>憨豆特工
<T>0
<F>http://video.sina.com.cn/v/b/56925622-2257301331.html
<QX>normal
<$>
<N>憨豆特工-0001
<P>新浪网
<U>http://edge.v.iask.com/56926940.hlv?KID=sina,viask&Expires=1354550400&ssig=0G8Acouy32
<X>0001
<C>aHR0cDovL3YuaWFzay5jb20vdl9wbGF5LnBocD92aWQ9NTY5MjU2MjItMjI1NzMwMTMzMSZyPXZpZGVvLnNpbmEuY29tLmNu
<EXPLODEID>1
<&>
<$>
<N>憨豆特工-0002
<P>新浪网
<U>http://edge.v.iask.com/56921791.hlv?KID=sina,viask&Expires=1354550400&ssig=aDBPWf%2B2YH
<X>0001
<C>aHR0cDovL3YuaWFzay5jb20vdl9wbGF5LnBocD92aWQ9NTY5MjU2MjItMjI1NzMwMTMzMSZyPXZpZGVvLnNpbmEuY29tLmNu
<EXPLODEID>2
<&>
<$>
<N>憨豆特工-0003
<P>新浪网
......

显然,=<N>=后面是片名而=<U>=后面是地址。

另外,我们观察一下硕鼠当前页面的url地址。

http://www.flvcd.com/parse.php?kw=http%3A%2F%2Fvideo.sina.com.cn%2Fv%2Fb%2F56925622-2257301331.html

显然,kw参数后面是之前sina视频页面地址,只不过已经经过转码处理。

至此,足够我们用python来完成这一切了。

打开ipython:

In [1]: import urllib

In [2]: import urllib2

In [3]: videourl = 'http://video.sina.com.cn/v/b/56925622-2257301331.html' 

In [4]: url = 'http://www.flvcd.com/parse.php?kw=' + urllib.quote(videourl)

In [5]: req = urllib2.Request(url);

In [6]: req.add_header('host', 'www.flvcd.com');

In [7]: res = urllib2.urlopen(req)

In [8]: html = res.read()

In [9]: print unicode(html,'gbk') # 注意硕鼠的页面编码是charset=GB2312

至此,我们完成了从硕鼠读取整个网页的操作。并把读取的内容保存在html中。

接下来开始抓地址,根据之前的观察,很容易通过正则表达式完成:

In [13]: import re

In [14]: pattern = re.compile('<input\s+type="hidden"\s+name="inf"\s+value="([^"]+)')

In [15]: match = pattern.search(html)

In [16]: urls = match.group(1)

In [17]: urls = unicode(urls, 'gbk')

In [18]: urlpattern = re.compile('<[NU]>(.+)')

In [19]: result = urlpattern.findall(urls)

至此,=<N>=和=<U>=后面的文件名和地址都被以列表的形式保存在result中了。我们可以遍历它来完成下载:

先简单处理下,以文件名-地址成对保存:

In [28]: data = [result[i:i+2] for i in range(0, len(result), 2)]

然后遍历下载:

In [32]: for k, v in enumerate(data):                   
    print '  >downloading Block %.2d ...' % (k+1,) 
    urllib.urlretrieve(v[1], v[0] + '.flv') 
    print '  downloaded Block.%.2d completely<' % (k+1,)

然后?等着……目前的下载器相当简陋,木有进度条,木有断点……

之后就是合并flv的问题了。

合并flv文件

简单起见,直接用这里join\flv.py来完成。

python2 join_flv.py -o out.flv flv1.flv flv2.flv ...

如果你想深入了解flv文件结构,参考further reading部分和flv文件规范。

original_yzzH_5943000093b01191.jpg
Figure 2: flash-data

Practice

抓取这里的视频。并按名称保存他们:

  • https://class.coursera.org/ml/lecture/preview
  • http://v.163.com/special/opencourse/machinelearning.html

作为正则和urllib、urllib2的练习。

Further Reading

  • 在线视频下载(Using Python / Bash / C / Reguar Expressions)
  • flv文件详解
  • FLV文件格式解析
  • you-get