`

用ruby写了一个搜索下载歌曲的工具

    博客分类:
  • Ruby
阅读更多
前几天用java写了一个GUI的搜索下载工具,主要利用baidu mp3搜索的结果。david同学用perl写了命令行的类似的下载工具,为了练练ruby,我又写了ruby版的。
Fetcher类:
根据url来Fetch到页面,供Parser分析之用
 require "net/http" 

 class Fetcher
  
  def fetch(url)
    host = url.scan(/\/\/(.*?)\//m)[0][0]
    path = url.split(/#{host}\//)[1]
   # print "host: ",host,"\n"
   # print "path: ",path,"\n"
    h = Net::HTTP.new(host,80)
    resp = h.get("/#{path}",nil)
   
    if resp.message == "OK"
     # puts "建立连接成功..." 
      return resp.body     
    end 
    return ""
  end

end


Parser类:
提取出可供下载的链接,并通过ping,来选取速度最快的连接,供Download之用:
class Parser
public
  def initialize()
    @fetcher = Fetcher.new
  end

  def parse_mp3(html)
    urls = html.scan(/<a href="(.*?)"/m)
    download_hosts_urls = {}
    parse_threads = []
    for url in urls do
        if url[0] =~ /.*?\.mp3,,.*?/
           parse_threads << Thread.new(url) do |url|
              song_url = url[0].gsub(" ","%20")
              download_url = parse_download_url(song_url)
              if download_url
              	host =  download_url.scan(/\/\/(.*?)\//m)[0][0] 
              	#We only want to find the best download url,so we needn't care duplicate key
              	download_hosts_urls[host] = download_url
              end 
           end
        end
    end
    parse_threads.each{|t| t.join}
    puts "已经搜索到#{download_hosts_urls.size}个链接可以下载..."
    exit(1) if download_hosts_urls.size == 0
    puts "正在选择速度最快的链接..."
    host = select_best_host(download_hosts_urls.keys)
    download_hosts_urls[host]
  end

private
  def select_best_host(hosts)
    times_hosts = {}
    threads = []
    hosts.each do |host|
      threads << Thread.new(host) do |host|
           response = `ping -c 1 -W 30 #{host}` #use`ping -n 1 -w 30 #{host}` in windows
           r_t = response.scan(/time=(\d+)/m) #only get integer part
           times_hosts[r_t[0][0]] = host unless r_t.empty? #duplicate key no problem 
      end
    end
   
    threads.each{|t| t.join}
   
    times = times_hosts.keys
    min = times.min
    times_hosts[min]
  end

  def parse_download_url(song_url)
     html = @fetcher.fetch(song_url)
     urls = html.scan(/<a href="(.*?)"/m)
     return nil if urls.empty? || urls[0][0] =~ /.*?\.html/
     return urls[0][0]      
  end
end


Download类:
 require "open-uri"
require "parser"
require "fetcher"

class Download
public
  def initialize(song_name)
    @song_name = song_name
    @search_url = "http://mp3.baidu.com/m?f=ms&tn=baidump3&ct=134217728&lf=&rn=&word=#@song_name&lm=0"
    @parser = Parser.new
    @fetcher = Fetcher.new
  end
 
  def download
    puts "正在建立连接..."
    html = @fetcher.fetch(@search_url)
    puts "正在获取搜索结果..."
    url = @parser.parse_mp3(html)
    puts "已经获得最快的下载连接:#{url}.\n开始下载..."
    doDownload(url)    
    puts "下载完毕..."
  end
private
  def doDownload(url)
    open(url) do |fin|
  	size = fin.size
  	download_size = 0
  	puts "大小: #{size / 1024}KB"
  	filename = url[url.rindex('/')+1, url.length-1]
  	puts "歌曲名: #{filename}"
  	open(File.basename("./#{filename}"),"wb") do |fout|
     	    while buf = fin.read(1024) do
       		fout.write buf
       		download_size += buf.size
                print "已经下载: #{download_size * 100 / size}%\r"
                STDOUT.flush 
           end
       end
    end 
    puts
  end
end

download = Download.new(ARGV[0])
download.download

引用

fuliang@fuliang-desktop:~/program/ruby/mp3download$ ruby download.rb pretty body
正在建立连接...
正在获取搜索结果...
已经搜索到25个链接可以下载...
正在选择速度最快的链接...
已经获得最快的下载连接:http://www.jxggzp.com/muisc/20051122185348.mp3.
开始下载...
大小: 6570KB
歌曲名: 20051122185348.mp3
已经下载: 100%
下载完毕...

基本上可以使用。现在还存在一些问题,下载链接中有中文,往往会失败,主要是没有进行编码,知道ruby有个Iconv.conv来转换编码,不知道如何直接对中文进行编码:不知道没有像encode("gb2312","大海")之类的方法。另一个是下载问题:进度条有问题,主要open-uri使用open貌似就把文件下载到本地了,造成open很长时间,fin.read,fout.write是本地操作则非常快,结果下载进度从开始出现到下载完成瞬间就完成。希望各位达人可以帮助修正两个问题。
  • mp3.rar (1.8 KB)
  • 描述: windows下的源码 [文中代码为Ubuntu下]
  • 下载次数: 161
分享到:
评论
8 楼 fuliang 2009-03-07  
当时刚学ruby,对ruby的api不熟。学习了。

Hooopo 写道

orange0513 写道
为什么要用正则来分析url中的host、port、path等呢 直接用URI.parse不好么。就是用正则也得加上i这个选项忽略大小写吧。
同意,lz的代码一点没有ruby风格啊。。。比如:


Ruby代码

require&nbsp;"net/http"&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;
class&nbsp;Fetcher &nbsp;&nbsp;
&nbsp; &nbsp;&nbsp;
&nbsp;def&nbsp;fetch(url) &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;host&nbsp;=&nbsp;url.scan(/\/\/(.*?)\//m)[0][0] &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;path&nbsp;=&nbsp;url.split(/#{host}\//)[1] &nbsp;&nbsp;
&nbsp;&nbsp;#&nbsp;print&nbsp;"host:&nbsp;",host,"\n" &nbsp;&nbsp;
&nbsp;&nbsp;#&nbsp;print&nbsp;"path:&nbsp;",path,"\n" &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;h&nbsp;=&nbsp;Net::HTTP.new(host,80) &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;resp&nbsp;=&nbsp;h.get("/#{path}",nil) &nbsp;&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;if&nbsp;resp.message&nbsp;==&nbsp;"OK"&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;puts&nbsp;"建立连接成功..."&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;resp.body&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;end&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;return&nbsp;""&nbsp;&nbsp;
&nbsp;end&nbsp;&nbsp;
&nbsp;&nbsp;
nd&nbsp;&nbsp; require "net/http"

class Fetcher
 
  def fetch(url)
    host = url.scan(/\/\/(.*?)\//m)[0][0]
    path = url.split(/#{host}\//)[1]
   # print "host: ",host,"\n"
   # print "path: ",path,"\n"
    h = Net::HTTP.new(host,80)
    resp = h.get("/#{path}",nil)
  
    if resp.message == "OK"
     # puts "建立连接成功..."
      return resp.body    
    end
    return ""
  end

end



&nbsp;可以这样:



Ruby代码

require&nbsp;"net/http"&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;
class&nbsp;Fetcher &nbsp;&nbsp;
&nbsp; &nbsp;&nbsp;
&nbsp;def&nbsp;fetch(url) &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;url=URI.parse&nbsp;url &nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;h&nbsp;=&nbsp;Net::HTTP.new(url.host,url.port) &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;resp&nbsp;=&nbsp;h.get(url.request_uri) &nbsp;&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;if&nbsp;resp.code&nbsp;==&nbsp;"200"&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;puts&nbsp;"建立连接成功..."&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resp.body&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;end&nbsp; &nbsp;&nbsp;
&nbsp;end&nbsp;&nbsp;
nd&nbsp;&nbsp; require "net/http"

class Fetcher
 
  def fetch(url)
    url=URI.parse url

    h = Net::HTTP.new(url.host,url.port)
    resp = h.get(url.request_uri)
  
    if resp.code == "200"
     # puts "建立连接成功..."
      resp.body    
    end
  end
end



&nbsp;还有那个for循环,完全可以这样:



Ruby代码

html.scan(/&lt;a&nbsp;href="(.*?)"/im)&nbsp;.flatten.each&nbsp;do&nbsp;|url| &nbsp;&nbsp;
&nbsp;&nbsp;#do&nbsp;someting&nbsp;here &nbsp;&nbsp;
end&nbsp;&nbsp;html.scan(/&lt;a href="(.*?)"/im) .flatten.each do |url|
  #do someting here
end

&nbsp;还有:



Ruby代码

song_url&nbsp;=&nbsp;url[0].gsub("&nbsp;","%20")&nbsp;&nbsp;song_url = url[0].gsub(" ","%20")

&nbsp;可以这样:



Ruby代码

requrie'cgi'&nbsp;&nbsp;
song_url=CGI::escape&nbsp;url[0]&nbsp;&nbsp;requrie'cgi'
song_url=CGI::escape url[0]

最后/&lt;a&nbsp;href="(.*?)"/m最好这样写:



Ruby代码

/&lt;a\s+href="(.*?)"/im&nbsp;&nbsp;/&lt;a\s+href="(.*?)"/im&nbsp;

&nbsp;

7 楼 ywencn 2009-03-07  
MD,牛人真多~
6 楼 Hooopo 2009-03-06  
<div class="quote_title">orange0513 写道</div>
<div class="quote_div">为什么要用正则来分析url中的host、port、path等呢 直接用URI.parse不好么。就是用正则也得加上i这个选项忽略大小写吧。</div>
<p><br />同意,lz的代码一点没有ruby风格啊。。。<br />比如:<br />
<pre name="code" class="ruby"> require "net/http"

class Fetcher
 
  def fetch(url)
    host = url.scan(/\/\/(.*?)\//m)[0][0]
    path = url.split(/#{host}\//)[1]
   # print "host: ",host,"\n"
   # print "path: ",path,"\n"
    h = Net::HTTP.new(host,80)
    resp = h.get("/#{path}",nil)
  
    if resp.message == "OK"
     # puts "建立连接成功..."
      return resp.body    
    end
    return ""
  end

end

</pre>
</p>
<p>&nbsp;可以这样:</p>
<p>
<pre name="code" class="ruby"> require "net/http"

class Fetcher
 
  def fetch(url)
    url=URI.parse url

    h = Net::HTTP.new(url.host,url.port)
    resp = h.get(url.request_uri)
  
    if resp.code == "200"
     # puts "建立连接成功..."
      resp.body    
    end
  end
end

</pre>
</p>
<p>&nbsp;还有那个for循环,完全可以这样:</p>
<p>
<pre name="code" class="ruby">html.scan(/&lt;a href="(.*?)"/im) .flatten.each do |url|
  #do someting here
end</pre>
</p>
<p>&nbsp;还有:</p>
<p>
<pre name="code" class="ruby">song_url = url[0].gsub(" ","%20")</pre>
</p>
<p>&nbsp;可以这样:</p>
<p>
<pre name="code" class="ruby">requrie'cgi'
song_url=CGI::escape url[0]</pre>
</p>
<p>最后<span style="font-family: Consolas; line-height: 18px; -webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px;">/&lt;a&nbsp;href=<span class="string" style="color: blue;">"(.*?)"</span><span style="color: black;">/m最好这样写:</span></span></p>
<p>
<pre name="code" class="ruby">/&lt;a\s+href="(.*?)"/im</pre>
&nbsp;</p>
<p>&nbsp;</p>
5 楼 orange0513 2009-03-04  
为什么要用正则来分析url中的host、port、path等呢 直接用URI.parse不好么。就是用正则也得加上i这个选项忽略大小写吧。
4 楼 likunkun 2009-03-04  
我觉得这文章不错呀,为什么没人顶呀
3 楼 flyinglife 2008-07-31  
博主好强悍
2 楼 david19842003 2008-04-15  
读ruby代码就像自然语言一样
1 楼 frozentree 2008-04-15  
Iconv.new("gbk","utf-8").iconv("做人不能太CNN")

相关推荐

    2012届软件工程论文答辩记录

    G music是一个音乐下载软件,用户可以使用命令行方式下载和搜索歌曲。该软件旨在提供一个命令行音乐下载平台,旨在满足音乐爱好者的需求。 微型社交网站是一个社交网站,用户可以在线社交和分享信息。该网站使用...

    barstool_beats_app

    "Barstool Beats App" 是一个基于 Ruby 开发的应用程序,根据提供的信息,我们可以推测这可能是一个音乐或娱乐相关的项目。Ruby 是一种面向对象的、动态类型的编程语言,以其简洁和可读性强的代码而闻名,常用于Web...

    时髦hypemachine.com

    用户可以搜索和播放歌曲,也可以通过“Hype Chart”了解当前最受欢迎的音乐。该平台的关闭或转型可能是由于音乐行业的变化,如版权问题、竞争压力以及音乐消费习惯的转变。 4. gh-pages分支:在文件名...

    MusicStore

    【MusicStore】是一个针对初学者友好的音乐应用项目,它提供了良好的用户界面,便于用户浏览、搜索和享受音乐。从项目名称和描述中我们可以推测,这可能是一个模拟在线音乐商店或者音乐播放平台的示例应用,旨在帮助...

    bbcr1-spotify:使用BBC R1歌曲在Spotify上动态创建播放列表

    这个项目将这些电台播放的歌曲与Spotify的API接口相结合,使得用户能够即时创建包含这些歌曲的播放列表,无需手动搜索和添加。 核心知识点包括: 1. **Spotify API**:Spotify Web API允许开发者访问Spotify平台的...

    i_was_there:一个 Rails 应用程序,允许用户使用 Last.FM API 搜索艺术家和事件

    "i_was_there" 是一个基于 Ruby on Rails 的应用程序,其主要功能是让用户能够通过 Last.FM API 来搜索音乐艺术家和音乐活动。这个应用旨在为用户提供一个平台,让他们能够发现新的音乐,同时还能查看和分享对特定...

    music_information:该项目的目标是实时查找与歌曲或专辑相关的相关信息

    该"music_information"项目旨在实现实时搜索和获取与歌曲或专辑相关的详细信息。这可能包括艺术家信息、发行日期、流派、歌词、评论等。通过这样的系统,用户可以方便地探索音乐世界,获取他们感兴趣的内容。 技术...

    fsp:我的全栈克隆的回购

    【fsp:我的全栈克隆的回购】 "fsp"是一个项目名称,暗示着开发者尝试创建一个与Soundcloud类似...通过阅读和理解这些代码,我们可以更深入地学习如何使用Ruby on Rails构建一个全栈应用,并从中汲取灵感和实践技巧。

    dmti:数字音乐转录接口

    在Ruby编程语言中,DMTI可能表现为一个库或者框架,允许开发者通过Ruby代码与音乐数据进行互动。这通常涉及到解析MIDI(Musical Instrument Digital Interface)文件,这是一种广泛用于存储和交换音乐信息的标准格式...

    crystal_mpd:用Crystal编写的并发MPD客户端

    总结,Crystal_mpd结合了Crystal的高效性和MPD的灵活性,为音乐爱好者提供了一个强大的工具,用于控制和享受他们的音乐体验。通过理解Crystal语言的特性和MPD的工作原理,我们可以更好地理解和利用这个并发客户端,...

    Rpotify:Spotify CLI 远程控制

    Rpotify 是一种无需 GUI 即可远程管理 Spotify 的工具,只能通过 CLI。 ##特征 目前的特点: 播放/暂停/停止 下一个/上一个 随机/重复 音量增大/减小 正在播放 信息轨道 搜索歌曲/专辑/艺术家 未来功能: 订单...

    music-library-app

    2. **音乐资源管理**:应用可能包含一个模型来表示音乐资源,如歌曲、专辑或艺术家。这些数据可能存储在数据库中,如SQLite、MySQL或PostgreSQL。Rails的ActiveRecord允许开发者方便地与数据库进行交互。 3. **搜索...

    Spotify-SDK-Swift-Tutorial-Part-One:iOS SDK Swift 3.0+教程第一部分的源代码-one source code

    这通常通过CocoaPods完成,它是iOS开发中的一个依赖管理工具。确保你的项目中已经配置了CocoaPods,然后在`Podfile`中添加Spotify SDK的相关依赖: ```ruby pod 'SpotifyiOS' ``` 接着,运行`pod install`命令来...

    spotify.cr:Spotify Web API的Crystal包装器

    **Spotify Web API** 是一个官方提供的RESTful API,允许开发者构建与Spotify服务交互的应用程序,例如播放音乐、搜索内容、获取用户信息等。它提供了广泛的资源和操作,覆盖了从播放控制到数据获取的各个方面。 **...

Global site tag (gtag.js) - Google Analytics