《老友记》是美剧中的经典,其中的对白也早已成为英语爱好者的宝典,我当然也是粉丝之一啊。前几天在普特英语网 上发现有所有10季老友记的对白mp3文件下载 ,大喜!!可惜每一集的连接都在不同的页面上,难道要我保存200多次?! 当然不行,200多次重复机械劳动太丢人啦!而且普通英语网上还有好多不错的英语资料呢,还是写个脚本批量下载的好。 好了,这就开始!
我的ruby 版本是1.8.6 ,这个版本在下载较大文件时会出现问题 ,后面会提到啦。为了吸引眼球,我还是先把结果的截图放到前面来咯
ps:一共是10季嘛
ps:这个是第四季的全部对白
步骤:
1.找到所有包含老友记对白的页面url,
如"http://www.putclub.com/article.php?articleid=8761 "
2.找到这个url中的《老友记》的mp3文件,以及对应的标题,
如上面这个url中的mp3文件是"http://listen.putclub.com/resource/lessons/liurenxing/liurx/12.mp3 ",
标题是” 六人行1-12The One With the Dozen Lasagnas “
3.根据标题确定该对白属于第几季,
如"六人行1-12 The One With the Dozen Lasagnas"是第1季12集
4.开始批量下载《老友记》对白文件,将每一季的对白下载到对应的文件夹下,
如第5季的对白下载到/friends/season-5/ 下
code:
步骤1 ,(这个很简单,因为普特英语网上的文章的url就是http://www.putclub.com
/article.php?articleid=XXXX ,老友记的XXXX就是从8749 到8976 ,哈哈,天助我也!)
def get_urls_in_putclub(url_prefix,start_index, end_index)
page_count = end_index - start_index + 1
urls = []
index = start_index
page_count.times do
url = url_prefix + String(index)
urls << url
index += 1
end
urls
end
调用
all_urls = get_urls_in_putclub("http://www.putclub.com/article.php?articleid=",8749,8976)
就得到了所有urls;
步骤2 ,这个步骤要得到HTML中的对白标题 和mp3文件 连接,用正则表达式来把她们scrape下来
标题的html格式是这样子滴:
.......
<div class="subhead">
<b>六人行1-12The One With the Dozen Lasagnas </b>
</div>
......
对应的匹配方法是这样子滴:
def scrape_resource_title(html)
%r{<div\s*class=[',"]?subhead"\s*><\s*b\s*>\D*(.*?)</b\s*>}mi =~ html
$1
end
返回的标题就是'1-12The One With the Dozen Lasagnas'
mp3文件连接的html格式是这样子滴:
......
<p align="center">
<a href="http://listen.putclub.com/resource/lessons/liurenxing/liurx/12.mp3">下载地址</a>
.....
对应的的匹配方法就是:
def scrape_resource_url(html)
%r{<p\s*align=[',"]?center[',"]?\s*>\s*<a\s*href=[',"](.*?)[',"]\s*>}mi =~ html
$1
end
返回的连接就是:'http://listen.putclub.com/resource/lessons/liurenxing/liurx/12.mp3'
当输入”1-12The One With the Dozen Lasagnas“时,得到”season-1“,还是用正则表达式
def extract_season_index(title)
/(\d+)-/ =~ title
"season-#{$1}"
end
步骤4 ,建立mp3文件连接,将对白下载到对应的文件夹下,用之前得到的标题名来命名文件
比如文件'http://listen.putclub.com/resource/lessons/liurenxing/liurx/12.mp3 '就下载到".../friends/season-1/1-12The One With the Dozen Lasagnas.mp3 "
ps:在windows下,文件名不能包含"\/:*?"<>|"这些字符啊!
下面是下载方法的实现:
def download(resource,root)
url = URI.parse(resource[:url])
unless FileTest.exist?(
"D:\\friends\\#{extract_season_index(resource[:title].chomp)}\\#{resource[:title].chomp}.mp3")
Net::HTTP.start(url.host,url.port) do |http|
start_time = Time.now
puts "Start download #{resource[:title].chomp}.mp3"
data = http.get(url.path).body
size = data.size
if(size > 500)
puts String(size) + "k"
path = root + extract_season_index(resource[:title])
FileUtils.makedirs(path) unless FileTest.exist?(path)
File.open("D:\\friends\\#{extract_season_index(resource[:title].chomp)}\\#{resource[:title].chomp}.mp3",'wb') do |fin|
fin.write data
end
end_time = Time.now
puts "Take #{end_time - start_time} seconds"
else
puts "Download fail"
"download_fail"
end
end
end
end
ruby1.8.6的Net::HTTP中的get方法在连接较大的数据时,几乎都是连接超时 , 有人在rubyforge 上改写了ruby的rbuf_fill方法,原先的timeout参数太小,导致timeout的次数
太多,所以会超时。ruby1.8.7和以后的版本修改了这个bug
被改写的ruby方法在 \ruby\lib\ruby\1.8\net\protocol.rb 里
#def rbuf_fill
# timeout(@read_timeout) {
# @rbuf << @io.sysread(1024)
# }
#end
def rbuf_fill
begin
@rbuf << @io.read_nonblock(4096)
rescue Errno::EWOULDBLOCK
if IO.select([@io], nil, nil, @read_timeout)
@rbuf << @io.read_nonblock(4096)
else
raise Timeout::TimeoutError
end
end
end
主要的步骤和方法都罗列出来啦,完整的ruby文件请下载附件,如果你懒得修改ruby源码的话,可以用附件中的
protocol.rb替换 \ruby\lib\ruby\1.8\net\ 下的protocol.rb. 完整的ruby脚本里还有下载记录的保存,避免了第二次运行脚本时再去连接已下载的mp3对白文件,不过与主题联系不大,我就不多说了。
下一篇是把当年明月的博客 ——《明朝那些事》的博文转为PDF导出,文章嘛还是下到PSP看来得爽嘛!
我们的目标就是——实用的代码!