Asp.net平台下教育动态信息的定时采集 [杭州市拱墅区教研室 赵旵]
由于工作的关系,有的教师需要每天浏览许多教育网站,以了解最新信息。网站数目多,查看费时费力,为了减轻他们的负担,有些单位专门安排一个人每天浏览各网站,并把有关信息打印出来分发给大家阅读。但大家仍觉得不方便,原因主要有以下三点:一是增加了传送环节,导致信息延误;二是负责查阅和选择信息的人员难以区分信息对阅读者的重要程度,容易遗漏重要信息,或编入阅读者不需要的信息;三是浪费纸张,失去了教育信息化的优势。
笔者在Asp.net平台下用C#编写了一个信息采集的程序,该程序会自动将采集到的信息作为一个模块放在区信息网的网站中。这样,需要每天浏览不同教育网站的教师只要通过这个模块,就可以看到众多教育网站的主要信息,方便地从中寻找自己需要的内容。
本文介绍这一方法。
一、读取各网站相关模块的信息列表
由于不可能直接获得网站数据库内容,在各网站不提供WebService服务和RSS服务的情况下,只能通过分析网页的源代码来得到所需要的信息。
1.查看源代码
例如:要获取“中国基础教育网”的“今日新闻信息”,在浏览器中输入http://www.cbe21.com/访问“中国基础教育网”网站,然后在浏览器中查看源代码,可发现有以下这部分代码:
……
〈table width="100%" border="0" cellspacing="0" cellpadding="0"〉
〈tr〉
〈td 〉
〈span class="px12"〉·〈/span〉
〈a href="public/news/html/210101/2007_05/20070523_37222.html?PHPSESSID=
aba8ad4323e907b2754d9cd188643931" class="a2" target="_blank"〉让燃烧的激情更理性--记内蒙古科技大学校长
〈/tr〉
……
下面所要做的工作就是让服务器获取这部分代码、分析代码、提取有效信息,然后重组放到自己的网站中。
2.获取源代码
命名空间System.Net下的类System.Net.HttpWebRequest和System.Net.HttpWebResponse负责发送以及接收请求,使用户能够直接与使用Http的服务器交互。
首先,根据指定的网页地址创建HttpWebRequest对象:
HttpWebRequest request=(HttpWebRequest)HttpWebRequest.Create(URL);
这里URL为字符串http://www.cbe21.com/。
然后,创建HttpWebRequest对象接收服务器要返回的信息,也就是网页的源代码:
HttpWebResponse response=(HttpWebResponse)request.GetResponse();
最后,将获取的信息读取出来即可。
System.IO.StreamReader sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.Default);
Encoding是网页使用的编码,在这里设为缺省值Default。
将读出的内容赋值到字符串strHtml string strHtml=sr.ReadToEnd();
3.提取信息
在分析源代码的基础上,有两种方法可以提取所需要的信息。
一种是利用字符串的操作函数IndexOf找到指定位置,然后在指定的位置上利用Substring()切割所需要的信息,这里不详细举例。该方法简单易操作,但效率不高。
另一种是利用正则表达式进行字符串的匹配,提取相应的信息。正则表达式,就是用某种模式去匹配一类字符串的一个公式。它提供了功能强大、灵活而又高效的方法来处理文本。正则表达式的全面模式匹配表示法可以快速分析大量文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。
根据上面的源代码分析,可以建立如下的正则表达式:
string strRegex=@" 〈a\s+href="" (?〈Url〉.*?)""[\s\S]*?〉(?〈Topic〉.*?)〈/tr〉";
然后,通过Match对象就很容易得到标题列表和相关信息,如信息发布时间,信息发布部门等。代码如下:
Regex r;
MatchCollection m;
R=new Regex(strRegex,RegexOptions.IgnoreCase);
M=r.Matches(strHtml);
StringBuilder sb = new StringBuilder();
sb.Append("〈table width='100%' cellspacing='0' cellpadding='0' border='0'〉");
for (int i = 0; i 〈 m.Count; i++)
{
sb.Append("〈tr〉〈td align='left');
sb.Append("〈a href='"+ m[i].Groups["ID"].Value + "'");
sb.Append(" target='_blank'〉");
sb.Append(m[i].Groups["Topic"].Value + "〈/a〉");
sb.Append("〈/td〉〈/tr〉");
}
sb.Append("〈/table〉");
二、定时采集,生成静态数据
每次页面生成都进行信息数据采集,会给双方服务器造成压力。每隔一定时间生成静态数据保存在本地供阅读,可以减轻双方服务器的运行负荷。
1.添加运行计数器
全局类Global.asax中的Application_Start函数在应用程序启动时运行,在该函数中添加计数器,每隔4小时运行一次。
void Application_Start(object sender, EventArgs e)
{
System.Timers.Timer myTime = new System.Timers.Timer(4 * 60 * 60 * 1000);
myTime.AutoReset = true; //如果是false,则执行一次
myTime.Enabled = true; //
myTime.Elapsed += new ElapsedEventHandler(getOutInfFunction);
}
其中getOutInfFunction为获取其它网站信息的函数。
2.定时保存信息
下面在函数getOutInfFunction中实现定时保存信息的功能,抓取的信息既可以保存在数据库中,也可以保存在XML文件或其它文本文件中。本文介绍如何将抓取的信息保存在XML文件中。
首先新建一个XML文件OutInf.xml,格式如下:
〈?xml version="1.0" encoding="utf-8"?〉
〈appSettings〉
〈!--中国基础教育网www.cbe21.com--〉
〈add key="outInf1" value=""〉
〈/appSettings〉
编写公共函数用于设置XML文件的相关值:
public static void SetValue(string key, string strValue, string FileName)
{
string XPath = "/appSettings/add[@key='?']";
XmlDocument outInfConfig = new XmlDocument();
outInfConfig.Load(FileName);//在Application_Start 中FileName的地址需要物理地址。
XmlNode addKey = outInfConfig.SelectSingleNode((XPath.Replace("?", key)));
if (addKey == null)
{
throw new ArgumentException("没有找到〈add key='" + key + "' value=.../〉的配置节");
}
addKey.Attributes["value"].InnerText = strValue;
SourVerConfig.Save(FileName);
}
在getOutInfFunction()函数中添加语句,向XML文件中写入信息。
void getOutInfFunction()
{
……//第一部分为“读取各网站相关模块的信息列表”的“提取信息”内容相关代码。
SetValue("outInf1",sb.ToString(),"FileName"); //其中FileName为需要保存的文件的路径地址与文件名。
}
3.读取数据,呈现网页
至此,前期工作已基本完成。读取数据库内数据或xml文件中的数据呈现于网页中即可。
编写公共函数用于读取XML文件中的相关值:
public static string GetValue(string key, string FileName)
{
string XPath = "/appSettings/add[@key='?']";
XmlDocument outInfConfig = new XmlDocument();
outInfConfig.Load(FileName);
XmlNode addKey = outInfConfig.SelectSingleNode((XPath.Replace("?", key)));
if (addKey == null)
{
throw new ArgumentException("没有找到〈add key='" + key + "' value=.../〉的配置节");
}
return addKey.Attributes["value"].InnerText;
}
在网页中放置数据呈现控件,并将获取的信息写入。如:
sp_outInf.InnerHtml = GetValue("outInf1", "xml文件所在的路径");
三、不足与思考
该方法只对能获取到源代码的网页信息有效,而难以获取基于框架或网页内含帧的网页信息。同时,如果对方网站改版或更新模块,则有可能要重新进行源代码的分析并提取信息。
笔者认为,信息采集成功的关键在于写出正确高效的提取信息的正则表达式。正则表达式直接写比较容易出错,推荐使用工具对正则表达式进行调试,正确后再放入程序代码中使用。如Code Architects Regex Tester或mTracer都可以对正则表达式进行调试验证。
参考文献:
[1] 张海.献给开发者的大礼-打造CSDN论坛专用阅读器[N]. 电脑报,2006-11-06,(F19).
[2] 张蓓.Asp.Net通用模块及典型系统开发[M]. 北京:人民邮电出版社,2006.
|
|