斯坦福在线人工智能课程字幕下载脚本

2011年10月12日 20:42

updated at 2011.11.02: 根据留言问题更新了一下脚本
updated at 2011.12.13: youtube改版, 改一下第一个脚本中某个正则

提供一种思路哈,方便离线看的童鞋~

批量下载youtube视频可以用firefox的插件BYTubeD~

字幕批量下载的步骤(firefox中):

1.选中youtube网页中那一堆视频的缩略图,右键选"查看选中部分源代码",存为in.txt,用下面的脚本正则出视频地址和名字:

# coding: utf-8
import sys,re

urlpattern  =re.compile(r'watch\?v=([^\"]*)\" class=\"ux-thumb')
#titlepattern=re.compile(r'title=\"([^\"]*)\"')
titlepattern=re.compile(r'video-title\">([^\<]*)\<')
timepattern =re.compile(r'video-time\">(\d+:\d+)')

# in.txt is a part of the source file of the youtube page !!!
content=file('in.txt').read().split('\n')
allurl=[]
alltitle=[]
alltime=[]
for line in content:
    allurl+=urlpattern.findall(line)
    alltitle+=titlepattern.findall(line)
    alltime+=timepattern.findall(line)
print "Found:",len(alltime),len(allurl),len(alltitle)
fo=file('urlinfo.txt','w')
sys.stdout=fo
allinfo=zip(allurl,alltitle,alltime)
for info in allinfo:
    for i in info:
        print i
    print ''
fo.close()

2.对于上面的脚本生成的urlinfo.txt文件,用下一个脚本下载所有的xml格式的英文字幕,其中用到了http代理:

import os,urllib2
 
proxy_handler = urllib2.ProxyHandler({"http" : 'http://127.0.0.1:8123'})
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)

#baseurl='http://www.youtube.com/api/timedtext?v=%s&name=English%%20via%%20dotsub&lang=en&hl=en'
baseurl='http://www.youtube.com/api/timedtext?asr_langs=en%%2Cja&caps=asr&v=%s&name=English%%20via%%20dotsub&lang=en'
urlinfo=file('urlinfo.txt','r').read().split('\n')
cnt=len(urlinfo)
print 'Total:',cnt/4

for i in range(0,cnt-1,4):
    try:
        print 'Start',i,urlinfo[i+1]
        if os.path.exists(urlinfo[i+1]+'.xml'):
            continue
        fo=file(urlinfo[i+1]+'.xml','w')
        print 'getting:',baseurl % urlinfo[i]
        fo.write(urllib2.urlopen(baseurl % urlinfo[i]).read())
        fo.close()
        print 'Done ',i
    except Exception,e:
        print e.message
        pass

3.用下面的脚本把xml格式的字幕转换为srt格式的:

# coding: utf-8

import os,re
stamppattern=re.compile(r'start=\"(\d+)\.?\d*\" dur=\"(\d+)\.?\d*\"')
textpattern =re.compile(r'dur=\"\d+\.?\d*\">(.*)$')

def second2time(sec):
    ret=[]
    ret.append(sec/60/60)
    ret.append(sec/60)
    ret.append(sec%60)
    return ret

def xml2srt(data,fo):
    data=data.split('</text>')
    data=data[:-1]
    for i,line in enumerate(data):
        fo.write('%d\n' % (i+1))
        stamps=map(int,stamppattern.findall(line)[0])
        fo.write('%02d:%02d:%02d,000 --> %02d:%02d:%02d,000\n' % tuple(second2time(stamps[0])+second2time(stamps[0]+stamps[1])))
        fo.write(textpattern.findall(line)[0]+'\n\n\n')

if __name__=='__main__':
    fs=filter(lambda s:s.endswith('.xml'),os.listdir('.'))
    for f in fs:
        if os.path.exists(f[:-4]+'.srt'): continue
        print f[:-4],'ON!'
        fo=file(f[:-4]+'.srt','w')
        xml2srt(file(f,'r').read(),fo)
        fo.close()
        print f[:-4],'Done'

4.最后一步把字幕里的html标记替换掉:

# coding: utf-8

import os,re
def all_mark():
    markpattern=re.compile(r'&amp;[^;]*;')
    allmarks=set()

    fs=filter(lambda s:s.endswith('.srt'),os.listdir('.'))
    for f in fs:
        marks=markpattern.findall(file(f,'r').read())
        allmarks=allmarks.union(set(marks))
    return allmarks
    # set(['&amp;quot;', '&amp;#39;', '&amp;gt;', '&amp;lt;'])

if __name__=='__main__':
    fs=filter(lambda s:s.endswith('.srt'),os.listdir('.'))
    for f in fs:
        content=file(f,'r').read()
        content=content.replace('&amp;quot;','"')
        content=content.replace('&amp;#39;',"'")
        content=content.replace('&amp;gt;','>')
        content=content.replace('&amp;lt;','<')
        fo=file(f,'w')
        fo.write(content)
        fo.close()
    print 'Unreplace marks:',str(all_mark())

5.调整字幕时间,把上一条字幕的结束时间设为下一条字幕的开始时间

# coding: utf-8

import os,re

if __name__=='__main__':
    fs=filter(lambda s:s.endswith('.srt'),os.listdir('.'))
    for f in fs:
        content=file(f,'r').read().split('\n')
        n=len(content)/5
        for i in range(n-1):
            nextst=content[i*5+6][0:12]
            content[i*5+1]=content[i*5+1][0:17]+nextst
        of=file(f,'w')
        of.write('\n'.join(content))
        of.close()

评论(3) 阅读(3740)

斯坦福在线机器学习课程字幕下载脚本

2011年10月11日 10:40

updated at Oct. 2012: 字幕打包下载 http://dl.vmall.com/c00blsic2a

斯坦福在线机器学习(Machine Learning)课程终于在昨天(10月10号)开课了,网址在 http://www.ml-class.org

和AI课程的视频放在youtube上不同,ML的在线视频提供下载,但是下载的版本没有英文字幕,故此放出自产自用字幕下载脚本,仿照以前的豆瓣电台歌曲信息脚本,也是从firefox获得cookie,然后爬得xml的字幕,再转换为srt格式的,这样就可以离线或者在iPad上看了!^_^

第一个是偷firefox的cookie爬网页,下载xml版的脚本,注意修改其中的firefox的cookie文件的目录,需要安装pysqlite包:

# coding: utf-8

import cookielib,urllib2
from cStringIO import StringIO
from pysqlite2 import dbapi2 as sqlite
import re,os

# a useful function from others
def sqlite2cookie(filename,host):
    con = sqlite.connect(filename)
    con.text_factory = str
    cur = con.cursor()
    cur.execute("select host, path, isSecure, expiry, name, value from moz_cookies where host like ?"
            ,['%%%s%%' % host])
    ftstr = ["FALSE","TRUE"]
    s = StringIO()
    s.write("""\
# Netscape HTTP Cookie File
# http://www.netscape.com/newsref/std/cookie_spec.html
# This is a generated file! Do not edit.
""")
    for item in cur.fetchall():
        s.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (
            item[0], ftstr[item[0].startswith('.')], item[1],
            ftstr[item[2]], item[3], item[4], item[5]))
    s.seek(0)
    #print "cookie:",s.read() ;s.seek(0)
    cookie_jar = cookielib.MozillaCookieJar()
    cookie_jar._really_load(s, '', True, True)
    return cookie_jar

# get cookie
cookiejar = sqlite2cookie(r'C:\Users\lenovo\AppData\Roaming\Mozilla\Firefox\Profiles\osfuexqh.default\cookies.sqlite','ml-class.org')
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
urllib2.install_opener(opener)

# post cookie
print 'Getting page, if it takes too long time, kill me!'
content=urllib2.urlopen('http://www.ml-class.org/course/video/list?mode=view').read()
#print content
content=''.join(content.split('\n'))
print "Page got!"

# get file name
namefinder=re.compile(r"file: ([^,]*),")
found=namefinder.findall(content)
print 'Number of files:',len(found)
found=map(lambda s: s[1:-1],found)
#print found

# down xml
baseurl='http://s3.amazonaws.com/stanford_videos/cs229/subtitles/%s-subtitles.xml'
for i,fn in enumerate(found):
    fn=fn.replace(r'\'','\'')
    outfile=fn+'.xml'
    if os.path.exists(outfile):
        continue
    print 'Getting:',fn
    fo=file(outfile,'w')
    fo.write(urllib2.urlopen(baseurl % fn).read())
    fo.close()
    print 'Done:',i,outfile

第二个是批量把xml脚本转化为srt字幕文件的脚本,各种字符串处理比较挫,见笑了:

# coding: utf-8
import os,sys,re,codecs

limit=[60,60,60,1000]
def xml2srt(fi,fo):
    data=''.join((fi.read().split('\n')[9:-4])).strip().split('</p>')
    for i in range(0,len(data)-1):
        #print i,data[i]
        if data[i]:
            st_st=data[i].index('"')
            st_ed=data[i].index('"',st_st+1)
            if i+1<len(data)-1:
                nx_st=data[i+1].index('"')
                nx_ed=data[i+1].index('"',nx_st+1)
            fo.write(str(i+1)+' \n')
            stamps=[data[i][st_st+1:st_ed],
                    data[i+1][nx_st+1:nx_ed] if i+1<len(data)-1 else "99:59:59.999"]
            word=data[i][data[i].index('>')+1:].replace('\n',' ')+' \n\n\n'
            for i,stamp in enumerate(stamps):
                stamp=stamp.split('.')
                stamps[i]=map(int,stamp[0].split(':'))
                stamps[i].append(int(stamp[1]))
            stamps=map(lambda s:"%02d:%02d:%02d,%03d" % tuple(s),stamps)
            fo.write("%s --> %s \n" % tuple(stamps))
            fo.write(word)
    #print 'OK!'

if __name__=='__main__':
    for fn in os.listdir('.'):
        if fn[-4:]!='.xml':
            continue
        print 'Converting:',fn[:-4]
        fi=file(fn,'r')
        if os.path.exists(fn[:-4]+'.srt'):
            continue
        fo=file(fn[:-4]+'.srt','w')
        xml2srt(fi,fo)
        fo.close()
        print 'Done'

感谢wr大牛(@isnowfy)的支持,附上wr大牛的单个字幕下载的简化版本,还有exe可执行文件哦!

评论(4) 阅读(9130)

python生成epub电子书

2011年9月10日 15:17

epub格式其实就是个zip压缩包,用python也不过是一些简单的文件操作
一个简单的解压后的epub目录树:

tmp
   │─ META-INF
   │   └─ container.xml (固定内容文件)
   │─ mimetype (固定内容文件)
   └─ OPS
        │─ 1.txt.xhtml (html文件)
        │─ 2.txt.xhtml (html文件)
        │─ content.ncx (目录文件!)
        │─ content.opf (信息及内容文件!)
        └─ cover.jpg   (封面,可选,位置啊名字啊都不太重要)

一个示例: gist

参考:
epub格式电子书剖析之一:文档构成
Simple EPUB ebooks with Py­thon

评论(1) 阅读(7762)

带上传的HTTP文件服务器脚本(Bottle)

2011年9月08日 16:23

轻量级的框架当然要用来做轻量级的事了!

尝试用 Bottle 做一个类似 Python 自带的 SimpleHTTPServer.py 的东西,记得有人给 SimpleHTTPServer.py 加过上传功能,看了 Bottle 后觉得可以用很少量的代码实现,而且文档里都有上传的例子了(囧)。

单文件的 Bottle 再加一个 50 行的脚本这样的组合也不比 SimpleHTTPServer.py 差多少,而且还可以随用随改,自用蛮方便的。

updated at 2013.5.8: 在 win 下,os 的函数的输入输出都是 gbk 编码的,所以有一些恶心的 hack ……

# coding: utf-8
from bottle import *
import os
import sys
from os.path import abspath, basename, join, isfile, isdir

basepath = abspath(sys.argv[1] if len(sys.argv) > 1 else '.')

struct = '''
<!doctype html>
<html>
  <head>
    <meta charset=utf-8>
    <title>Directory listing for {path}</title>
  </head>
  <body>
    <h2>Directory listing for {path}</h2>
    <hr><ul>
{lis}
    </ul><hr>
    <form action="" method="post" enctype="multipart/form-data">
      Upload file: <input type="file" name="file"/>
      <input type="submit" value="submit">
    </form>
  </body>
</html>
'''
li = '<li><a href="{name}">{name}</a>'


@get('/<path:re:.*>')
def main(path):
    if sys.platform.startswith('win'): # f**k gbk
        path = path.decode('utf8').encode('gbk')
    curpath = join(basepath, path)
    if isfile(curpath):
        return static_file(path, root=basepath, download=path)

    childs = [join(curpath, c) for c in os.listdir(curpath)]
    dirs = filter(isdir, childs)
    if path:
        dirs = ['..'] + dirs
    files = filter(isfile, childs)
    lis = [li.format(name=basename(d)+'/') for d in dirs]
    lis += [li.format(name=basename(f)) for f in files]
    if sys.platform.startswith('win'): # f**k gbk
        path = path.decode('gbk').encode('utf8')
        lis = [l.decode('gbk').encode('utf8') for l in lis]
    return struct.format(path=path if path else '/', lis='\n'.join(lis))

@post('/<path:re:.*>')
def upload(path):
    upfile = request.files.get('file')
    filename = join(basepath, path, upfile.filename)
    if sys.platform.startswith('win'): # f**k gbk
        filename = filename.decode('utf8').encode('gbk')
    outfile = open(filename, 'wb')
    try:
        buf = upfile.file.read(upfile.bufsize)
        while buf:
            outfile.write(buf)
            buf = upfile.file.read(upfile.bufsize)
        outfile.close()
        return 'Uploaded %s !' % upfile.filename
    except Exception, e:
        print e.message
        return 'Failed in uploading %s !' % upfile.filename

if __name__ == '__main__':
    debug(True)
    run(reloader=True, host='0.0.0.0', port=8080)

评论(1) 阅读(4407)

打印目录结构

2011年8月22日 13:45

# coding: utf-8
import os,sys,codecs

lc=sys.getfilesystemencoding()
sys.stdout=codecs.lookup(lc)[-1](sys.stdout)

def walkdir(d,prefix=u''):
    l=os.listdir(d)
    last=len(l)-1
    prefix+=u'   │'
    for i,f in enumerate(l):
        if i!=last:
            print  prefix+u'─',
        else:
            print  prefix[:-1]+u'└─',
            prefix=prefix[:-1]+u'  '
        print f.decode(lc)
        
        p=os.path.join(d,f)
        if os.path.isdir(p):
            walkdir(p,prefix)

 
if __name__ == '__main__':
    d=os.path.abspath('.')
    print os.path.basename(d).decode(lc)
    walkdir(d)

output:

ComicShelf
   │─ dirtree.py
   │─ zipMyComic.py
   │─ 死神
   │   │─ 死神_第460话
   │   │   │─ 001.jpg
   │   │   │─ 002.jpg
   │   │   │─ 003.jpg
   │   │   └─ 018.jpg
   │   └─ 死神_第460话.zip
   │─ 海贼王
   │   │─ 海贼王[635]悍破云霄
   │   │   │─ 001.jpg
   │   │   │─ 002.jpg
   │   │   │─ 003.jpg
   │   │   └─ 017.jpg
   │   └─ 海贼王[635]悍破云霄.zip
   └─ 火影忍者
        │─ 火影忍者[551]阻止长门
        │   │─ 001.jpg
        │   │─ 002.jpg
        │   │─ 003.jpg
        │   └─ 015.jpg
        └─ 火影忍者[551]阻止长门.zip

评论(1) 阅读(1705)