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

上一篇文章已经写了有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

public class Crawler { /// <summary> /// 获取音乐信息列表 /// </summary> /// <param name="info">歌曲信息的实体类</param> /// <param name="objSearch">外部对象</param> /// <returns>音乐列表</returns> public List<MusicInfo> GetMusicList(SearchMusicInfo info,IMusicSearch objSearch) /// <summary> /// 获取音乐歌词列表 /// </summary> /// <param name="info">歌词信息的实体类</param> /// <param name="objSearch">外部对象</param> /// <returns>返回的歌词列表</returns> public List<MusicLrcInfo> GetMusicLrcList(SearchMusicInfo info, ILRCSearch objSearch) /// <summary> /// 获取音乐歌词内容 /// </summary> /// <param name="info">歌词信息的实体类</param> /// <param name="objSearch">外部对象</param> /// <returns>歌词字符串</returns> public string GetMusicLyric(MusicLrcInfo info, ILRCSearch objSearch) } public class NetworkRequest { /// <summary> /// 对指定URL页面进行请求,获取页面流 /// </summary> /// <param name="pageHtml">返回的页面HTML字符串</param> /// <param name="reqUrl">请求的页面URL</param> /// <param name="encode">请求的页面编码</param> /// <param name="filterReg">页面要过滤的正则表达式</param> /// <returns>请求结果</returns> public PageRequestResults RequestPage(out string pageHtml, string reqUrl, Encoding encode, string filterReg) /// <summary> /// 对指定URL页面进行请求,获取页面流,去除文字修饰的标签 /// </summary> /// <param name="pageHtml">返回的页面HTML字符串</param> /// <param name="reqUrl">请求的页面URL</param> /// <param name="encode">请求的页面编码</param> /// <returns>请求结果</returns> public PageRequestResults RequestPage(out string pageHtml, string reqUrl, Encoding encode) /// <summary> /// 去除网页HTML中指定的标签 /// </summary> /// <param name="pageContext">要处理的HTML字符串</param> /// <param name="filterReg">要去除内容的正则表达式</param> /// <returns>消除后的字符串</returns> private string ReplaceHtml(string pageContext, string filterReg)

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

public PageRequestResults RequestPage(out string pageHtml, string reqUrl, Encoding encode, string filterReg) { pageHtml = string.Empty; if (!string.IsNullOrEmpty(reqUrl)) { // 设置请求信息,使用GET方式获得数据 HttpWebRequest musicPageReq = (HttpWebRequest)WebRequest.Create(reqUrl); musicPageReq.AllowAutoRedirect = false; musicPageReq.Method = "GET"; //设置超时时间 musicPageReq.Timeout = SearchConfig.TIME_OUT; //获取代理 IWebProxy proxy = SearchConfig.GetConfiguredWebProxy(); //判断代理是否有效 if (proxy != null) { //代理有效时设置代理 musicPageReq.Proxy = proxy; } try { // 获取页面响应 using (HttpWebResponse musicPageRes = (HttpWebResponse)musicPageReq.GetResponse()) { // 如果HTTP为200,并且是同一页面,获取相应的页面流 if (musicPageRes.StatusCode == HttpStatusCode.OK) { // 获取响应的页面流 using (Stream pageStrem = musicPageRes.GetResponseStream()) { // 读取页面流,获取页面HTML字符串,去除指定的标签 using (StreamReader reader = new StreamReader(pageStrem, encode)) { pageHtml = ReplaceHtml(reader.ReadToEnd(), filterReg); return PageRequestResults.Success; } } } else { return PageRequestResults.UnknowException; } } } }

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

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

public List<MusicInfo> GetMusicList(SearchMusicInfo info,IMusicSearch objSearch) { List<MusicInfo> _musicList = new List<MusicInfo>(); string _pageHTML; // 请求页面 NetworkRequest nwr = new NetworkRequest (); PageRequestResults result = nwr.RequestPage(out _pageHTML, objSearch.CreateMusicUrl(info), objSearch.PageEncode()); if (result == PageRequestResults.Success) { // 如果请求成功,分析页面 _musicList = objSearch.PageAnalysis(_pageHTML); break; } }

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

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

.

2.ISearch接口

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

/// <summary> /// 编码接口 /// </summary> public interface IEncoding { /// <summary> /// 指定页面的编码方式 /// </summary> /// <returns>页面编码方式</returns> Encoding PageEncode(); } /// <summary> /// 获取歌词信息时要实现的接口 /// </summary> public interface ILRCSearch : IEncoding { /// <summary> /// 分析要采集的HTML页面内容,并返回采集到的歌词信息 /// </summary> /// <param name="PageContent">HTML页面内容</param> /// <returns>搜索到的歌词列表</returns> List<MusicLrcInfo> PageAnalysis(string PageContent); /// <summary> /// 创建采集音乐歌词列表时请求的地址 /// </summary> /// <param name="info">音乐搜索信息</param> /// <returns>获取所有歌词信息时请求的URL</returns> string CreateAllLrcUrl(SearchMusicInfo info); /// <summary> /// 创建采集指定歌词内容时请求的地址 /// </summary> /// <param name="info">歌词信息</param> /// <returns>获取指定歌词内容时请求的URL</returns> string CreateLrcUrl(MusicLrcInfo info); } /// <summary> /// 获取歌曲信息要实现的接口 /// </summary> public interface IMusicSearch : IEncoding { /// <summary> /// 分析要采集的HTML页面内容,并返回采集到的歌曲信息 /// </summary> /// <param name="PageContent">HTML页面内容</param> /// <returns>搜索到的歌曲列表</returns> List<MusicInfo> PageAnalysis(string PageContent); /// <summary> /// 创建采集音乐信息时请求的URL /// </summary> /// <param name="info">音乐搜索信息</param> /// <returns>请求的URL</returns> string CreateMusicUrl(SearchMusicInfo info); }

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

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

.

3 搜索插件的实现

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

/// <summary> /// 分析搜搜音乐搜索 /// </summary> public List<MusicInfo> PageAnalysis(string PageContent) { List<MusicInfo> lstMusic = new List<MusicInfo>(); try { // 获取所有歌曲的DIV块信息,此块包含了MusicInfo信息 List<string> musicDIV = RegexHelper.GetRegexStringList(PageContent, SOSO_TR_PATTREN, RegexOptions.IgnoreCase | RegexOptions.Singleline); if (musicDIV != null) { foreach (string div in musicDIV) { // 把每个DIV块中取得的歌曲信息存入歌曲列表 lstMusic.AddRange(MusicInfoBuild(div)); } } } catch { } return lstMusic; } /// <summary> /// 创建音乐获取URL /// </summary> /// <param name="info">音乐搜索信息</param> /// <returns>URL</returns> public string CreateMusicUrl(SearchMusicInfo info) { try { int ntype = 0;//ALL switch (info.MusicFormat) { case SearchMusicFormat.MP3: ntype = 1; break; case SearchMusicFormat.WMA: ntype = 2; break; } string sosoUrl = "http://cgi.music.soso.com/fcgi-bin/m.q?w={0}&p=1&t={1}"; return string.Format(sosoUrl, new object[] { info.MusicName + CommonSymbol.SYMBOL_SPACE + info.SingerName, ntype.ToString() }); } catch { return string.Empty; } } /// <summary> /// 指定当前页面编码方式 /// </summary> /// <returns></returns> public Encoding PageEncode() { return Encoding.GetEncoding("GB2312"); }

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

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

/* * eg. * //<result> * //<lrc id="124962" artist="Jason Mraz" title="I/'m Yours" /> * //<lrc id="108753" artist="Jason Mraz" title="17 I'm Yours (Original Demo)" /> * //<lrc id="240096" artist="Jason Mraz (英汉对照)" title="I'm Yours" /> * //<lrc id="54134" artist="Jason Mraz" title="I'm Yours" /> * //<lrc id="3252" artist="Jason Mraz" title="I'm Yours (Corrected Album Version)" /> * //<lrc id="73843" artist="Jason Mraz (Album)" title="I'm Yours" /> * //<lrc id="60945" artist="Jason Mraz (MV version)" title="I'm Yours" /> * //<lrc id="57234" artist="smelly" title="I'm yours" /> * //<lrc id="27861" artist="Lonny Bereal" title="I'm Yours" /> * //<lrc id="278148" artist="TWO-MIX" title="I'M YOURS" /> * //</result> */

而在搜索歌词时,我们使用的是千千静听提供的接口,他和百度搜索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工程主要负责这些操作。

/// <summary> /// Runner初始化 /// 功能:插件加载 /// </summary> public static bool Initialize() { MusicClientConfig innermcc = MusicClientConfig.GetInstance(); //获取“Plugin”目录下的文件 string[] filepaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "/Plugin/","*.dll"); foreach (string ItemPath in filepaths) { FileInfo fi = new FileInfo(ItemPath); //找出后缀为DLL的文件,并实例化它 if (fi.Extension.ToLower().Equals(".dll")) { Assembly assembly = Assembly.LoadFile(ItemPath); //动态实例化类库 object objSearch = assembly.CreateInstance(string.Format("CMusicSearch.{0}.MainSearch", fi.Name.Replace(fi.Extension, string.Empty)), false); if (objSearch == null) { continue; } //加载音乐搜索插件 if (objSearch is IMusicSearch) { innermcc.MusicSearcher.Add(new KeyValuePair<string, IMusicSearch>(Guid.NewGuid().ToString(), (IMusicSearch)objSearch)); } //加载歌词搜索插件 else if (objSearch is ILRCSearch) { //lrcSearcher.Add(new KeyValuePair<string,ILRCSearch>(Guid.NewGuid().ToString(),(ILRCSearch)objSearch)); innermcc.LRCSearcher.Add((ILRCSearch)objSearch); } } } if (innermcc.MusicSearcher.Count < 1) { return false; } return true; }

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

/// <summary> /// 搜索歌曲方法 /// </summary> /// <param name="info">搜索信息</param> /// <returns>歌曲列表</returns> public List<MusicInfo> SearchM(SearchMusicInfo info) { Crawler crawler = new Crawler(); List<MusicInfo> lstMusic = new List<MusicInfo>(); // 遍历插件,搜索音乐 foreach (var item in mcc.MusicSearcher) { //根据加载的插件所提供的方法,获取音乐信息 lstMusic.AddRange(crawler.GetMusicList(info, item.Value)); } MusicDistinctHelper.Distinct(ref lstMusic); //TODO:在各自的插件中过滤MusicFormat后,在这里是否最终输出也过滤一次 return lstMusic; } /// <summary> /// 搜索歌词列表方法 /// </summary> /// <param name="info">搜索信息</param> /// <returns>歌词列表</returns> public List<MusicLrcInfo> SearchL(SearchMusicInfo info) { Crawler crawler = new Crawler(); List<MusicLrcInfo> lstMusic = new List<MusicLrcInfo>(); foreach (var item in mcc.LRCSearcher) { //根据加载的插件所提供的方法,获取歌词信息 lstMusic.AddRange(crawler.GetMusicLrcList(info, item)); } return lstMusic; }

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

MSLRCRunner Finder = new MSLRCRunner(); //搜索对象 private void button1_Click(object sender, EventArgs e) { try { Thread searchThread = new Thread( delegate() { SearchMusicInfo info = new SearchMusicInfo() { MusicName = EncodeConverter.UrlEncode(textBox1.Text.Trim()), MusicFormat = SearchMusicFormat.MP3 }; var list = Finder.SearchM(info); info.MusicName = textBox1.Text; var lstlrc = Finder.SearchL(info); //多线程使用UI控件 this.Invoke(new Action<List<MusicInfo>, List<MusicLrcInfo>>(DataBind), new object[] { list, lstlrc }); }); searchThread.Start(); } catch (Exception ex) { MessageBox.Show(ex.Message); } }

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

.

四 总结

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

实现自己的音乐搜索软件(二)相关推荐

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

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

  2. 酷狗软件测试用例,仿酷狗的音乐搜软件的开发与测试.doc

    仿酷狗的音乐搜软件的开发与测试 仿酷狗的音乐搜索软件的开发与测试 摘 要 当前,面对浩瀚的网络资源,搜索引擎为所有网上冲浪的用户提供了一个入口,毫不夸张的说,所有的用户都可以搜索引擎到达自己想去的网上 ...

  3. 二十八:微信公众帐号开发-应用实例之音乐搜索

    此篇文章为转载 引言及内容概要 微信公众平台支持向用户回复音乐消息,用户收到音乐消息后,点击即可播放音乐.通过音乐消息,公众账号可以实现音乐搜索(歌曲点播)功能,即用户输入想听的音乐名称,公众账号返回 ...

  4. 几个很特别的音乐搜索网站

    几个很特别的音乐搜索网站 关键词: 搜索 音乐 网站 ┊ 杂粹 ┊ 推荐: ┊ 来源: 有意思吧 ┊ 收藏 随着网络越来越深入我们的生活,我们会经常使用一些搜索引擎来搜索自己想要的歌曲,比如百度MP3 ...

  5. 2人同步听歌软件_【Android】魔音II 1.3 无损音乐下载软件震撼来袭,原音乐狂作者全新开发的一款安卓应用,低调使用!...

    大家好,今天为大家推荐魔音II1.3,原音乐狂作者全新开发的一款安卓应用,完全免费无广告,付费歌曲无损音乐下载工具,可以批量下载.采集热门推荐新歌榜.热歌榜.DJ歌曲.歌单推荐,音乐搜索具有多条无损音 ...

  6. 音乐学习软件需求文档

    音乐学习APP需求分析文档 1 当前现状 1.1 经济现状 CPI是反映与居民生活有关的消费品及服务价格水平的变动情况的重要宏观经济指标.2021年全年,全国居民消费价格比上年上涨0.9%,其中教育文 ...

  7. [PC工具]音乐下载软件,MP3音乐下载器

    绿色软件,简单粗暴. 我搜索了几个歌名,好像没有APE.FLAC无损格式,都是MP3的音乐. 1搜索,2试听,3下载 ▼ 下载连接 ▼ 关注公众号[DLGG创客DIY] 后台发送"MP3&q ...

  8. [工具]再更新音乐下载软件,MP3音乐无损音乐下载器

    昨天发的音乐下载软件我没有多做几次测试,导致误导大家了,昨天分享的那个只能听音乐不能下载,知道后我又赶紧为大家找了一款(一个网站),不知道什么情况链接不能发自动回复里,我也不敢直接放文章里,大家回复转 ...

  9. [Android工具]更新音乐下载软件,MP3音乐无损音乐下载器

    更新音乐下载软件,前两次发的安卓软件又不能用了,虽然是正常情况,之前也早有心里准备,但一发现不能用还是十分不爽的!没办法,盼望着哪天能把他们服务器里所有音乐都下载下来... 不能让大家没得用,我又找了 ...

最新文章

  1. Apriori算法通俗详解_fpgrowth2_关联分析评估
  2. HikariPool使用MySQL/MariaDB数据库报错解决:java.sql.SQLException: Access denied for user 'root'@'localhost' (u
  3. PHP获取 当前页面名称、主机名、URL完整地址、URL参数、获取IP
  4. 那个写同龄人正在抛弃你的作者,道理都懂,怎么你还是loser
  5. vaadin_在Vaadin和JSF之间选择
  6. 曾经的 Java IDE 王者 Eclipse 真的没落了?21 款插件让它强大起来!
  7. STM32F407使用MFRC522射频卡调试及程序移植成功
  8. 轻量级网页安全漏洞扫描工具-Wapiti
  9. win10杜比全景声评测_Win10安装杜比全景声音效教程
  10. 智能计算机翻译,爱译网-智能翻译技术-人工智能翻译技术
  11. java编程器宣传费湖南岚鸿驱动_EasyPRO系列通用编程器驱动程序
  12. 【麒麟操作系统软件商店老是闪退?--麒麟系统软件商店卸载与重装(小白教程)】
  13. 2021年中国VR/AR行业市场投融资现状分析:VR/AR技术领域融资实现双增长[图]
  14. 事务的隔离级别(附Redis的简述)
  15. LTE终端状态汇总:
  16. 怎么检查计算机启动程序,如何查看电脑开机启动项
  17. Python快速学习08:模块的操作
  18. 查看变量 IFS 的值
  19. 【20211008】为什么分布式服务框架越来越火
  20. Serv-U 15 架设FTP文件服务器 图文教程

热门文章

  1. html中list-style-type与list-style的区别
  2. ThreadPoolExecutor不抛异常
  3. 系统与软件安全研究(六)
  4. CSDN 博客排版技巧
  5. 区块链产业规模约达50亿元,已成为数字经济发展重要引擎
  6. Windows Service服务程序的原理及实现(0)服务主函数 控制处理函数
  7. 超漂亮的内存修改器,可以媲美某类似软件,UI界面反复调整 功能更加人性化,更多隐藏功能待您发现
  8. Codeforces 1045C Hyperspace Highways (看题解) 圆方树
  9. ubuntu上BIND9安装及设置转发遇到的坑
  10. Windows Server 2016