实现自己的音乐搜索软件(一)

在公司上班,都用QQ音乐听歌,然后一天同事给了个网页听歌的。当然不是百度那些MP3搜索的。页面是一个播放器,边上一个搜索框,很方便。反正最经也不忙,想自己也来做个搜索歌曲的,输入歌名返回地址。毕竟去百度搜索还要多点几下,现在一下就搞定了。

 

一 实现思路

 

 

因为没有自己的音乐库,所以只能到网上去搜索。于是选择了百度和soso两个网站,利用WEB请求,对返回的结果进行分析,得到MP3地址。这就是最基本的实现思路。其实很简单,就是用到了.NET中HttpWebRequest类来操作,然后使用正则表达式分析返回的结果。有了基本思路,就要调查如何实现。如何去网上搜索MP3。我们平时去百度搜索,在地址栏中可以看到URL中带有参数,我们可以通过构造一个URL,提交请求,获得页面的HTML。然后从中得到MP3以及歌词。

以下就是整理的搜索URL的格式。具体结果可以在IE中看到。其中百度和soso是返回和页面HTML,也就是我们在IE中看到的页面;而百度快搜是返回一个XML文件,最多只返回5首歌曲信息;最后是利用千千静听的歌词服务器,获得歌词信息,然后通过歌词信息中的内容,构建URL去获取歌词文件,返回的是LRC文件。

从上面可以看到,尽管搜索页面和类容不同,但是都是:构建URL–请求–分析页面–取得地址 。所以我们可以吧相同的部分提取出来作为一个基类。下图就是最开始的结构:

实现1search

建立了一个BaseSearch的抽象基类,其中有一个PageRequest 的方法,他是对HttpWebRequest操作的包装,输入一个URL和编码方式,返回一个PageContext的流。然后是一个PageAnalysis 方法,这个方法是一个抽象方法,用来分析返回的页面流。不同的子类有不同的分析方法,所以具体分析算法,由子类自己重写。有点策略模式的感觉。

GetMusicList、GetMusicLrcList、GetMusicLyrics 三个方法是获得歌曲列表、歌词列表、歌词内容;他们都调用了PageRequestPageAnalysis 方法,并且实现了失败重试,超时等。向外提供查询的接口。

然后又建立了一个SearchInfo类,她的作用是创建不同的请求URL(图中的Create方法),然后提供3个子类的搜索方法,最后一个Search方法,接受搜索信息,返回MP3和歌词信息。最终用户能看到的就只有这个类中的Search方法。

当然还新建了其他一些辅助类,比如RegerHelper来包装对正则表达式的操作,CommonOperator来进行通用的操作,比如URL和HTML的编码解码;建立了歌曲信息,歌词信息,搜索信息对应的实体类,以及一些枚举对象,比如音乐格式,搜索类型。

 

二 实现的改进

 

 

上面的代码实现之后,倒是没有什么问题。不过既然是自己练习,就想做的扩展性更强,因为我们是对网页进行分析,网页是会变化的,而且可以搜索的网页也不仅仅是百度和搜搜。如果要增加一个新的搜索方式,必须继承BaseSearch,需要重新编译。所以想到把每个搜索方法编译成一个DLL,然后搜索时加载这些DLL。这样,当增加了新的搜索方法时,只需要新增一个DLL。

为了实现类似与插件的方式,我们就需要定义接口。而我们搜索方法,是对接口进行操作。

改进结构

上图就是改进后的结构。最右边定义了3个接口。IEncoding 是指定页面的编码方式;IMusicSearch 是进行音乐搜索时要实现的接口;ILRCSearch 是进行歌词搜索时要实现的接口。然后看看中间的4个类,他们就是实现了这些接口,实现了自己的搜索。

NetworkRequest 是对HttpWebRequest的包装。和之前的功能是一样的。只不过获取页面的编码是通过IEncoding接口获得的。

Crawler 是搜索的核心部分。GetMusicList、GetMusicLrcList、GetMusicLyrics 三个方法的作用没有变化,他们也是获取URL-请求-分析–返回结果。但是他们是对接口进行编程,例如搜索歌曲GetMusicList方法 的实现如下:

  1. 调用IMusicSearch 接口的CreateMusicUrl 方法获取URL
  2. 调用NetworkRequestPageRquest 方法请求URL,而编码方式是通过IEncoding 接口的PageEncode 取得。
  3. 调用IMusicSearch 接口的PageAnalysis 方法分析返回的页面流
  4. 返回歌曲信息。

MSLRCRunner 的作用是对客户端提供搜索接口。在前面我们把实现搜索接口的DLL放到了指定的Plugin目录下, Initialize方法就是来加载这些插件。然后对加载的插件调用Crawler 中的方法,得到结果。并向客户端提供SearchM和SearchL方法。这样对于新增的DLL,只需要放到Plugin就可以直接使用。

 

三 实现中的一些问题

 

因为没有自己的数据库,所以搜索都是通过网络,这也是的搜索速度是一个需要考虑的问题。目前从3个地方那个搜索歌曲信息。对于百度快搜返回5首歌曲,而其他2个搜索,都只是搜多第一页的内容。所以总体上速度还是很快的。考虑对每个DLL搜索时采用多线程。这样可以加快速度。

在每个具体搜索方法中,也是对网页中<TR></TR>块中信息进行分析。百度一页30条记录,搜搜一页20条记录。但是因为每条记录分析速度很快。所以并没有采用多线程,因为线程切换反而可能导致速度更慢。当然后来做成插件的形式,那就是自己决定了。

经过分析大部分的搜索时间花在了网络请求上,但是每次分析必须等请求完成后进行。所以考虑使用异步请求来提高速度。但是因为在请求和分析之间不需要什么操作。所以采用异步也没有真正的提升速度,只是释放了线程。在单机和小并发的情况下,还是采用简单的同步方式实现。如果作为服务器短,提供多人同时搜索,大并发下可以使用异步方式,及时释放线程,提高吞吐量。

如果作为server提供服务,也可以把搜索的结果保存到数据库中,这样就能加快搜索速度,并建立自己的搜索库。

最后有一个没有解决的问题:因为我是分析页面中的信息,从页面中获取地址等信息。而很多MP3搜索网站,页面上没有直接提供地址,而是通过一段JS来获得歌曲地址。不知道这个有没有办法实现。

 

四 Demo效果

 

demo

这就是TEST的DEMO,后面打算使用WPF来做界面,并增加播放器。不过下周开始新的项目事情很多,估计没什么时间了。而且要出差,所以不知道什么时候能搞定。这次就不放代码了,同事还在修改,下周吧。

 


如果本文对您有帮助,可以扫描下方二维码打赏!您的支持是我的动力!
微信打赏 支付宝打赏

3 评论

  1. 我一直想怎么才能批量的下载歌词(歌曲文件的ID3信息是完整的),今天算是遇到高人了,如果你的工作有新的进展请告诉我,寄邮件到thlbl#163.com,#改为@

  2. 大哥,我最近也在做这个,不过是用API的,麻烦啊!有源代码么?给一份小弟啊!

匿名用户进行回复 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注