偶然一次在vs2012默认的项目文件夹里发现了以前自己做的一个关于SEO的类库,主要是用来查询某个网址的收录次数还有网站的排行数,后来重构了下,今天拿出来写篇文章,说说自己是如何思考的并完成的。
二、问题描述
首先需要考虑的是能够支持哪些搜索引擎的查询,首先是百度,然后是必应、搜狗、搜搜、360。本来想支持Google但是一想不对,根本不好访问的,所以暂时不算在内。而我们实际要做的就是根据一个网址能够检索出这个网址的在各个搜索引擎的收录次数以及在不同关键词下的网址排行,这里出入的只有网址还有若干的关键词,而输出则是该网址在不同搜索引擎下的收录次数以及在各个关键词下的排行数。
但是这里有个问题,就是排行数,如果检索的网址在前100还好,如果排名很后面,那么问题就来了,那样会让用户等待很长时间才能看到结果,但是用户可能只想知道排行前100的具体排名,而那些超过的则只要显示100以后就可以了,而这些就需要我们前期考虑好,这样后面的程序才好做。
三、解决思路
相信很多人都能够想到,就是利用WebClient将将需要的页面下载下来,然后用正则从中获取我们感兴趣的部分,然后利用程序去处理。而关键难度就是在这个正则的编写,首先我们先从简单的开始。
四、收录次数
首先是网站的收录次数,我们可以在百度中输入site:www.cnblogs.com/然后我们就可以看到如下的页面:
而我们所需要的收录次数就是 5,280,000 这段数字,我们接着查看页面元素:
接着我们再观察其他的搜索引擎可以发现都是类似的,所以我们的思路这个时候应该就得出了,最后就是如何组织网址,这部分我们看地址栏?wd=site%3Awww.cnblogs.com%2F这段就知道怎么写了。
稍等这个时候我们可能心急一个一个实现,这样后面我们就没法集中的调用,同时也会影响以后的新增,所以我们要规定一个要实现收录数功能的抽象类,这样就能够在不知晓具体实现的情况统一使用,并且还能够在以后轻松的新增新的搜索引擎,而这种方式属于策略模式(Stategry),下面我们来慢慢分析出这个抽象类的具体内容。
首先每个实现这个抽象类的具体类都应该是对应某个搜索引擎,那么就需要有一个基本网址,同时还要留下占位符,比如根据上面百度的这个我们就得出这样一个字符串
http://www.baidu.com/s?wd=site%3A{0}
其中{0}就是为真正需要检索网址的占位符,获取下载页面的路径是所有具体类都需要的所以我们直接将实现放在抽象类中,比如下面的代码:
1 /// <summary> 2 /// 服务提供者 3 /// </summary> 4 protected String SearchProvider { get; set; } 5 6 /// <summary> 7 /// 需要检索的网址 8 /// </summary> 9 protected String SiteUrl { get; set; } 10 11 /// <summary> 12 /// 搜索服务提供网址 13 /// </summary> 14 protected String BaseUrl { get; set; } 15 16 /// <summary> 17 /// 后页面网址 18 /// </summary> 19 /// <param name="site">需要查询的网址</param> 20 /// <returns>拼接后的网址</returns> 21 protected String GetDownUrl(string site) 22 { 23 return string.Format(BaseUrl, HttpUtility.UrlEncode(site)); 24 }
其中SiteUrl和SearchProvider是用来保存检索网址和搜索引擎名称。
上面我们说了将会利用WebClient来下载页面,所以初始化WebClient的工作也在抽象类中完成,尽可能的减少重复代码,而为了防止阻塞当前线程所以我们采用了Async方法。
具体代码如下所示:
1 /// <summary> 2 /// 查询在该搜索引擎中的收录次数 3 /// </summary> 4 /// <param name="siteurl">网站URL</param> 5 public void SearchIncludeCount(string siteurl) 6 { 7 SiteUrl = siteurl; 8 WebClient client = new WebClient(); 9 client.Encoding = Encoding.UTF8; 10 client.DownloadStringCompleted += DownloadStringCompleted; 11 client.DownloadStringAsync(new Uri(GetDownUrl(siteurl))); 12 } 13 14 /// <summary> 15 /// 检索收录次数的具体实现 16 /// 子类必须要实现该方法 17 /// </summary> 18 /// <param name="sender"></param> 19 /// <param name="e"></param> 20 protected abstract void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e);
当WebClient完成下载后将会回调DownloadStringCompleted方法,而这个方法的是抽象方法也就意味着具体类必须要实现这个方法。
虽然我们内部的实现是异步的但是对于其他开发者调用这个方法还是同步的,所以我们就需要借助委托因此我们还要新建一个委托类型:
/// <summary> /// 当完成一个网站的收录查询后回调 /// </summary> public Action<SiteIncludeCountResult> OnComplatedOneSite { get; set; }
其中SiteIncludeCountResult的结构如下所示:
1 /// <summary> 2 /// 用于网站收录中委托的参数 3 /// </summary> 4 public class SiteIncludeCountResult 5 { 6 /// <summary> 7 /// 收录次数 8 /// </summary> 9 public long IncludeCount { get; set; } 10 11 /// <summary> 12 /// 搜索引擎类型 13 /// </summary> 14 public String SearchType { get; set; } 15 16 /// <summary> 17 /// 网站URL 18 /// </summary> 19 public String SiteUrl { get; set; } 20 } 21 22 最后还有一个方法用于DownloadStringCompleted完成后回调OnComplatedOneSite委托: 23 /// <summary> 24 /// 完成处理后调用该方法将结果返回 25 /// </summary> 26 /// <param name="result">网址的收录数结果</param> 27 protected void SetCompleted(SiteIncludeCountResult result) 28 { 29 if (OnComplatedOneSite != null) 30 OnComplatedOneSite(result); 31 }