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

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

 

上一篇文章已经写了有10个月之久了,那之后出差半年,都一直没时间继续。最近不太忙,终于有时间继续做下去,花了1周半的时间,把后续的下载功能实现了。这篇文章开始主要就介绍一下具体的实现。

 

 

 


 

 

一 程序结构

前一篇文章只是一个开头,介绍了搜索部分的大概结构,现在主要看一下整个程序的结构。程序主要的功能目前是搜索和下载。他们都已DLL的形式存在,供主程序调用。

 

以上是整个工程的结构。主要功能有两大块,搜索音乐和下载音乐。

ISearh:定义了一套接口,指定了搜索时的编码方式,搜索歌词的方式和搜索音乐文件的方式。如果工程引入并实现了这些接口,那么编译出来的DLL就能被我们的主程序直接使用了。这样可以方便的开发搜索插件。

 

MusicPiugin:这个目录下的工程就是搜索插件。我们搜索来源于百度网页,soso网页以及百度提供的快搜接口(只返回5首歌曲);而歌词是来源于千千静听提供的接口,所以我们一共有4个插件,他们都实现了ISearh下定义的接口。

 

MusicCrawler:这个项目中主要是封装了搜索时的网络请求操作,并且对ISearh下定义的接口进行编程,提供了搜索音乐和歌词方法。

 

MusicRunner:这个项目的主要作用就是负责加载搜索插件并提供搜索方法。其中引入了MusicCrawler工程的DLL,在加载了搜索插件DLL后,调用他的方法来实现具体的应约搜索。

 

MusicDownload:这个项目主要功能是负责文件的下载,其中对下载的网络请求进行封装,并且提供了基于事件驱动的多任务的调度和管理,并且包含了文件管理功能。

 

MusicCommon:这个项目定义了一些公用的方法,实体类,常量,枚举等类型。

 

Test :是一个测试DEMO,他只引用了MusicRunner、MusicDownload、MusicCommon这三个工程,而其他工程对他都是不可见的。并且在其中提供了简单的播放功能。

 

 

.

 

二 搜索功能的实现

上一篇文章介绍了搜索的思路,这里不详细介绍了。关于搜索我采用的是最原始的方法,请求页面然后分析,其中百度有提供API,但是只能搜索5首歌曲。下面就具体看看MusicCrawler是怎么工作的。

 

1.MusicCrawler

 

这个工程中只包含两个类,NetworkRequest是用来进行http请求的。其中主要的RequestPage方法就是通过输入请求的URL地址和编码方式,然后获得页面的字符串存放到pageHtml。其中还提供了一个重载方法,用来过滤一些不需要的HTML元素。

 

代码比较简单,就是使用了HttpWebRequestHttpWebResponse对象进行网络请求,其中加入了代理的使用。

 

Crawler类提供了3个方法,是搜索歌曲列表,歌词列表,以及歌词类容。传入的是搜索信息的实体类,以及搜索歌词和歌曲的接口。然后返回搜索结果列表。主要看一下如何搜索歌曲的。

 

上面并不是全部代码,省略的只是请求失败时重试的操作。内部实际是调用NetworkRequest中的RequestPage方法,但这里传入的方法是使用接口IMusicSearch来调用的。这样在实际运行时,会根据加载的搜索插件来决定具体的搜索行为。当得到请求的值之后,调用了接口的PageAnalysis方法,来对页面进行分析,得到音乐文件地址列表。

 

所以对于MusicCrawler来说,他提供了一个搜索框架,而具体的搜索功能是在插件内部实现的。对于调用者,不需要知道插件的存在;而对于插件,只需要满足接口就能被主程序调用。

.

2.ISearch接口

对于我们程序来说,制作一个搜索插件都是针对一个网站。所以我们要能从这个网站获得数据,必须知道要请求的页面地址、页面的编码方式、以及如何分析页面得到想要的数据。 所以我们制定接口时就是从这3点出发的。

 

 IEncoding指定网页的编码格式,而ILRCSearchIMusicSearch也都继承了此接口。因为不同页面编码方式可能不同,所以必须指定编码方式。而在ILRCSearchIMusicSearch接口中都定义了返回请求页面的地址的方法,并且都有一个PageAnalysis方法用来分析页面得到数据。歌词因为需要请求列表和歌词内容,所以返回2个地址。 

 

在MusicCrawler中我们看到,都是针对这2个接口进行编程,所以,我们的插件实现了这些接口方法就能被使用了。

.

3 搜索插件的实现

我们先看看音乐搜索的插件,针对的是soso的页面。百度的页面类似,而使用百度API搜索时,返回的是XML。过程是一样,只是分析的方法不同。

 

在插件中,我们实现了这三个接口。因为我们已知道要请求的页面,所以制定编码方式不是很么难事;而指定请求地址时,需要根据搜索条件进行一定的拼接,关于请求的地址可在前一篇文章中有介绍。而PageAnalysis方法中,则是使用了正则表达式进行分析。这个需要结合页面的HTML接口,从中获得MP3的信息。这里主要的任务是写正则表达式,我们可以在分析页面之前,把一些不需要的标签过滤掉,以简化我们的正则表达式。

 

因为分析的内容和要获取的数据都很多,所以我们的正则表达式可能会很长,这个时候执行的速度可能会很慢,一个有效的办法就是分2次进行匹配。在分析百度页面时我就遇到过,分析一次要900多毫秒。而拆分成2个表达式进行2次分析一共只需要几毫秒。

 

而在搜索歌词时,我们使用的是千千静听提供的接口,他和百度搜索API一样,返回的是XML,所以我们可以以XML进行操作,效率要比正则高很多。只是搜索歌词时,首先返回的是歌词列表。我们需要从歌词列表中获得歌词的id,并且需要进行编码之后才能请求到真正的歌词,但是千千静听并没有公开编码方式,不过网上已经有了,项目的TTEncode中包含了编码方法。

.

4.存在的问题

因为一开始想法比较简单,就是做一搜索软件。所以考虑的并不周全,而当基本实现后发现了问题,又比较难在去全局感动了。

 

问题1:一个页面需要多次请求

 

MusicCrawler的好处是帮我们封装了网络请求操作,所以我们实现插件时,只用去关注如何分析得到数据,而不需要管如何请求数据。因为一开始并没有坐成插件形式,而且只有百度和搜搜2个站点获取数据,所以并没有考虑多次请求的问题。后来就发现了问题。soso是把所有MP3地址都放在了一个DIV中,这样只需要获取一次;而百度的MP3却是需要点击搜索列表的歌曲名,进入下一个页面,在获得MP3的地址。于是这就有了问题,我们的MusicCrawler只能提供一次搜索。

 

那么让插件自己去做第2次请求,那么是使用我们的NetworkRequest,还是自己实现呢?这是个挺麻烦的问题。Crawler并不可能知道你要请求几次,并不知道那一个返回的htmlString是用于分析的。但是插件自己去做请求,那么一些设置就无法使用,比如代理。

 

 

问题2:无法执行页面的JS

 

同样是百度页面的问题,百度页面的中的MP3地址是通过JS获得的,我们直接http请求是得不到的。我们可以在程序中使用webBrowser来执行JS,这也是遗留下来的一个问题。这个问题解决的话,讲有更多的网站可以作为数据源。因为很多网站MP3地址都是通过JS获得的。

 

 

问题3:执行速度

 

执行速度来说,在网络良好的情况下,速度还是可以;即便不是用百度的页面搜索,目前搜索出来的歌曲也足够,soso搜索到的歌曲基本都是有效的。但是在网络不好的情况下,速度就不行。程序受网络影响比较大。而类似QQ音乐,也有搜索其他站点的数据,但是他应该是有自己的数据库来存放这些数据。所以我们搜索,实际是对数据库的请求;而我们软件是完全来自于网络的。当然也考虑过是用数据库进行存储,但是对于单机是用,意义不大。

.

 

三 加载插件进行搜索

 前面已经介绍了搜索的接口,搜索插件的实现方法和存在的问题,最后就来看看是如何是用这些插件,来进行搜索的。MusicRunner工程主要负责这些操作。

 

 

在Initialize方法中,我们去Plugin目录下加载了所有的插件。因为搜索音乐和歌词实现的接口不同,我们容易区分出这两种插件。然后把加载的插件的实例对象,存放到一个内部列表中。

而在Runner中,我们提供了3个对外的方法,用来搜索歌曲和歌词。我们看到,在内部,我们实际是调用的Crawler对象中的方法。我们遍历了歌词和歌曲搜索插件的对象,并把他们传递给了Crawler的搜索方法。也就是告诉了Crawler,我们使用这些插件去搜索。因为这些插件都实现了ISearch中定义的接口,所以在Crawler内部,他们调用了接口方法,通过多态,实现了各自的搜索功能。最终返回结果数据。

 

 

而在Test中,调用就非常简单,创建搜索对象的实体类,然后使用MSLRCRunner的对象Finder调用SearchM,就能获得搜索结果了。

 

.

四 总结

有关搜索就介绍到这里,其实搜索的思想很简单,类似一个简单的采集程序。但是目前最大的问题就是没有实现执行JS。这个有机会要解决掉。呵呵。下一篇就开始介绍下载部分了。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值