前天公司面试题-使用B/S模式写一个程序,用来抓取百度或SOSO中对关键字的说明,尽量不使用服务器控件。
从帝都回到长沙快一周了,一切都比较顺利,回长沙之间就联系了几家公司,前天去面试了,去了后测试题就只有一道:抓取从百度或SOSO对关键字的解释说明,使用B/S模式。
想了想还是挺容易的一开始的思路所想的思路是这样的。
1.使用一个HTML页面,放一个文本框,一个按钮,一个DIV容器。点击按钮取到文本框的内容,异步的发送AJAX请求至后台的ashx。
2.在后面的ASHX页面,接收传过来的关键字,用一个webclient来下载SOSO的关键字页面的源代码,拿到页面源代码后,分析有关关键字的解释说明的格式,使用正则去匹配。将匹配上的存到一个集合中。再把集合中的内容组装成JSON格式发住前台。
3.在前台接收到JSON数据后,再动态的放到DIV容器中。
主要代码如下:
前台页面代码:
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml" > 3 <head> 4 <title>CatchDemo</title> 5 6 <script src="JS/jquery-1.7.2.js" type="text/javascript"></script> 7 <script type="text/javascript"> 8 $(function(){ 9 $("#btn_submit").click(function(){ //注册点击事件 10 var kword=$("#txt_keyword").val(); //取得文本框的内容 11 $("#contentlist").empty(); //再次搜索前清空容器 12 $.post("CatchDemo.ashx",{kword:kword},function(data){ //发送异步请求 13 var json =eval(data); //将返回过来的JSON字符串解析成数组 14 alert(json.length); 15 for(var i=0;i<json.length;i++) //遍历数组并动态添加DIV,在DIV中添加解析出来的文本 16 { 17 var $row=$("<div></div>"); 18 $row.text(json[i].row) 19 $("#contentlist").append($row); 20 } 21 22 23 }); 24 25 }); 26 }) 27 28 </script> 29 </head> 30 <body> 31 <input type="text" id="txt_keyword" value="耳鼻喉" /> 32 <input type="button" id="btn_submit" value="submit"/> 33 <div id="contentlist"> 34 </div> 35 </body> 36 </html>
ASHX页面代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Net; 6 using System.Text; 7 using System.Text.RegularExpressions; 8 using System.Web.Services; 9 10 namespace Ent.Xuz.CatchDemo 11 { 12 /// <summary> 13 /// $codebehindclassname$ 的摘要说明 14 /// </summary> 15 [WebService(Namespace = "http://tempuri.org/")] 16 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 17 public class CatchDemo : IHttpHandler 18 { 19 20 public void ProcessRequest(HttpContext context) 21 { 22 context.Response.ContentType = "text/plain"; 23 string kword = context.Request.Form["kword"]; //从上一个面面接受来的参数 24 kword = context.Server.UrlEncode(kword); //进入URL编码 25 string url1 = @"http://www.soso.com/q?w="; //SOSO的搜索地址 26 string urlpath = url1 + kword; //拼接编码后的URL地址 27 string webcode = GetRCodeFormWC(urlpath); //得到源文件,待分析出特定字符串 28 List<string> list = GetInformationNow(webcode); //获得所需信息,放到list集合中,待转化为json发向前台 29 30 string jsonres = ListToJson(list); //转化JSON数组 31 //string jsonres = ListToString(list); //转化为**|***|**的格式/暂时不用 32 context.Response.Write(jsonres); 33 } 34 35 /// <summary> 36 /// 创建webclient并通过构建好的地址下载源文件 37 /// </summary> 38 /// <param name="path">URL地址</param> 39 /// <returns></returns> 40 private string GetRCodeFormWC(string path) 41 { 42 WebClient wc = new WebClient(); 43 wc.Encoding = Encoding.Default; //避免乱码对其进行Default编码;百度的是UTF8格式的(起先用的UTF8乱码) 44 return wc.DownloadString(path); 45 } 46 47 /// <summary> 48 /// 对获取到的源文件,通过正则进行抓取,将抓取的内容放到List集合中 49 /// </summary> 50 /// <param name="buffer">源文件内容</param> 51 /// <returns></returns> 52 private List<string> GetInformation(string buffer) 53 { 54 List<string> list = new List<string>(); 55 // string rexstr = "href=\"([^\"]+)"; //抓取页面里的超链接 以href="开始,直到出现下一个引号时终止; 56 string rexstr = "<p class=\"ds\">([^p]+)"; 57 MatchCollection mc = Regex.Matches(buffer, rexstr); 58 foreach (Match item in mc) 59 { 60 if (item.Success) 61 { 62 string temp = item.Groups[1].Value; //中华网健康频道<em>耳鼻喉</em>专题介绍神经性耳聋、中耳炎、老年性耳聋,药物性耳鸣、神经性耳鸣、过敏性鼻炎、鼻窦炎、鼻息肉、慢性鼻炎耳鸣、耳聋等<em>耳鼻喉</em>顽症信息。...</ 63 //temp = temp.Replace("<em>","").Replace("</em>","").Replace("</",""); //暂时先用Replace解决,把<em>,</em>,</ 替换成空删除 64 temp= Regex.Replace(temp, @"<em>|</em>|</", ""); 65 list.Add(temp); 66 67 } 68 } 69 return list; 70 } 71 72 73 /// <summary> 74 /// 对获取到的源文件,通过正则进行抓取,将抓取的内容放到List集合中 75 /// </summary> 76 /// <param name="buffer">源文件内容</param> 77 /// <returns></returns> 78 private List<string> GetInformationNow(string buffer) 79 { 80 List<string> list = new List<string>(); 81 string resstar = "<em>|</em>|<span class=\"ask\">|</span>"; //先空删除源文件中的<em>|</em>|<span class=\"ask\">|</span>标签 82 string temp = Regex.Replace(buffer, resstar, ""); 83 string rexstr = "<p class=\"ds\">([^p]+)"; //再抓取页面中以 <p class="ds">开头 直到再次出现p标记终止 84 MatchCollection mc = Regex.Matches(temp, rexstr); //得到 "字符串...</" 85 foreach (Match item in mc) 86 { 87 if (item.Success) 88 { 89 string temp1 = item.Groups[1].Value; 90 temp1 = Regex.Replace(temp1, "</", ""); //再将"</"空删除 91 list.Add(temp1); 92 93 } 94 } 95 return list; 96 } 97 98 99 #region 转化为字符串**|**|的格式 100 /// <summary> 101 /// 将存放在list中的信息转换字符串**|**|的格式 102 /// </summary> 103 /// <param name="list"></param> 104 /// <returns></returns> 105 private string ListToString(List<string> list) 106 { 107 StringBuilder builder = new StringBuilder(); 108 foreach (string item in list) 109 { 110 builder.Append(item + "|"); 111 } 112 string temp = builder.ToString().TrimEnd('|'); 113 return temp; 114 115 } 116 #endregion 117 118 /// <summary> 119 /// 将存放在list中的信息转换成JSON格式的数据 120 /// </summary> 121 /// <param name="list"></param> 122 /// <returns></returns> 123 private string ListToJson(List<string> list) 124 { 125 StringBuilder builder = new StringBuilder(); 126 //[{"row":content},{"row":content},{"row":content}]将数据拼接成此格式 127 builder.Append("["); 128 for (int i = 0; i < list.Count; i++) 129 { 130 builder.AppendFormat("{{\"row\":\"{0}\"}},", list[i].ToString()); 131 } 132 string buffer = builder.ToString(); 133 buffer = buffer.TrimEnd(','); 134 buffer = buffer + "]"; 135 return buffer; 136 137 } 138 139 140 public bool IsReusable 141 { 142 get 143 { 144 return false; 145 } 146 } 147 } 148 }
其实测试的时候,我最开始是抓的百度的,但对于百度的要抓的内容是如下格式:
<br><font size=-1><font color=#CC0000>耳鼻喉</font>,国家重点(医保十佳)规模,名医荟萃,技术精湛,连续10年患者口碑最佳.微创技术,不开刀,有效治疗,7月感恩活动季,费用最低,成功率NO.1即刻预约享手术费半价优惠.</font><br>
<br><font size=-1><font color=#CC0000>耳鼻喉</font> 国防科大医院是湘雅医院双向转诊合作医院,省市医保!国家二级甲等医院,59年公立医院历史,专业<font color=#CC0000>耳鼻喉</font>专家亲诊,<font color=#CC0000>耳鼻喉</font>权威疗法,标本兼治</font><br>
最失败的是测试的时候用正则来匹配这一格式的内容我卡壳了。不过急中生智为了通过测试,保正程序完整能通过,我自己把要抓的内容改成了抓取页面上的所有超链接。这样就只要再去改正则就完事了。后来下午才知道,这个小测试居然是公司正好要用的东西。直接放到面试的时候让面试的人来写了,想法真好。当然测试也小通过。下午告诉我他们要抓SOSO的让我改用SOSO的 下午不是测试就不急不忙了 把正则和一些细节的东西改好交给他们了。
有时候个人觉得,应聘时,机试题可以适当的自己改改需求,首先是要保证能跑起来就行,然后代码风格一定要规范,专业。引用我启蒙老师最喜欢的话就是:“命名即注释;该有注释的地方不要省,说不定个把月后你拿到以前自己写的代码没有注释也要理上半天才能理清。”