简单的网页采集程序(ASP.NET MVC4)
因为懒人太多,造成现在网页数据采集非常的流行,我也来写个简单的记录一下。
之前写了MVC的基本框架的搭建随笔,后面因为公司太忙,个人感情问题:(,导致不想写了,就写了两篇给删除了,现在就搁浅了,
本人是马鞍山人,喜欢看看老家的招聘信息,看看我有没有机会回家发展,回家找妹子:),这是马鞍山的招聘网站 http://www.masrc.com.cn/
因他的一些信息只显示单位不显示具体的招聘职位,所以我闲着蛋疼,我就想做一个采集站,将数据采集出来,好方便浏览..
下面就是显示的页面,对我这个写代码的来说,根本找不到关于我的职位呀,一个一个的点多累啊,而且他的搜索有点问题,总搜索不出我要的....
看他的HTML代码:
基本都table加A标签的组合,家乡的网站,我就不吐槽了....
注意他的链接格式记录一下: <a class="black_9" href="*"><font>*</font></a>
开始写代码吧...
我用的是VS2012 + ASP.NET MVC4,请忽略这些细节....
因为不需要默认的样式,所以选择一个空的ASP.NET MVC项目
下面是目录结构
新建一个HomeControllers并建立一个基础Action Index (mvc基础就不讲了,重点是采集对吧)
接下来,根据上面分析的链接方式<a class="black_9" href="*"><font>*</font></a>
写一段采集需要的正则表达式
string htmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*><font[^>]*>(?<text>(?:(?!</?a\b).)*)</font></a>";//分组
这段正则表达式将我需要的信息作了分组,比如说href的指向链接url 和a标签的text文本 <font>在我需要的链接都有,所以需要去掉
现在初步的思路是将web页面的当中的a标签通过这个正则全部取出
先查看该网站文本的编码方式,在获取文本流时需要
如图,该网站用的gb2312的编码方式
下面是采集的代码有注释:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text.RegularExpressions; using System.Web; using System.Web.Mvc; namespace YanNis.WebSite.Collect.MasRC.Controllers { public class HomeController : Controller { public ActionResult Index() { //a标签的正则 string htmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*><font[^>]*>(?<text>(?:(?!</?a\b).)*)</font></a>";//分组 //需要采集的页面地址 string WebLink = "http://www.masrc.com.cn/"; //创建一个请求 WebRequest webReq = System.Net.WebRequest.Create(WebLink); //获取响应 WebResponse webRes = webReq.GetResponse(); //使用GB2312的编码方式 获取响应的文本流 StreamReader sReader = new StreamReader(webRes.GetResponseStream(), System.Text.Encoding.GetEncoding("gb2312")); //将文本读出 string content = sReader.ReadToEnd(); //正则匹配 MatchCollection matchCollecti = Regex.Matches(content, htmlregstr, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline); //创建一个Model用于前台查看 List<WebLink> WebLinks = new List<WebLink>(); //遍历每一个匹配 foreach (Match ma in matchCollecti) { //获取重要的信息 string title = ma.Groups["text"].Value; string link = ma.Groups["url"].Value; //因为需要的链接地址都有showMemberzpDetail.asp 所以用他做为过滤 if (ma.Success && link.IndexOf("showMemberzpDetail") >= 0) { WebLinks.Add(new WebLink { Title = title, Url = link }); } } //返回 MODEL return View(WebLinks); } } public class WebLink { public WebLink() { Desc = new List<WebLink>(); } public string Title { get; set; } public string Url { get; set; } public List<WebLink> Desc { get; set; } } }
View代码:
@model IEnumerable<YanNis.WebSite.Collect.MasRC.Controllers.WebLink> <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> <ul> @foreach (var m in Model) { <li><a href="@m.Url">@m.Title</a></li> } </ul> </div> </body> </html>
效果如下:
已经获取了,接下来就要去找到链接页面的详细岗位了,如图:
惯例,查看他的链接代码
还是一样 table+a的组合
还是刚刚那个正则,但是没有font这个标签所以稍加改动
string Detailhtmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*>(?<text>(?:(?!</?a\b).)*)</a>";
接下来的思路是根据获取指向的链接的页面,将岗位的链接获取在手
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text.RegularExpressions; using System.Web; using System.Web.Mvc; namespace YanNis.WebSite.Collect.MasRC.Controllers { public class HomeController : Controller { string htmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*><font[^>]*>(?<text>(?:(?!</?a\b).)*)</font></a>"; string Detailhtmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*>(?<text>(?:(?!</?a\b).)*)</a>"; public ActionResult Index() { //a标签的正则 //需要采集的页面地址 string url = "http://www.masrc.com.cn/"; //创建一个请求 WebRequest webReq = System.Net.WebRequest.Create(url); //获取响应 WebResponse webRes = webReq.GetResponse(); //使用GB2312的编码方式 获取响应的文本流 StreamReader sReader = new StreamReader(webRes.GetResponseStream(), System.Text.Encoding.GetEncoding("gb2312")); //将文本读出 string content = sReader.ReadToEnd(); //正则匹配 MatchCollection matchCollecti = Regex.Matches(content, htmlregstr, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline); //创建一个Model用于前台查看 List<WebLink> WebLinks = new List<WebLink>(); //遍历每一个匹配 foreach (Match ma in matchCollecti) { //获取重要的信息 string title = ma.Groups["text"].Value; string link = ma.Groups["url"].Value; //因为需要的链接地址都有showMemberzpDetail.asp 所以用他做为过滤 if (ma.Success && link.IndexOf("showMemberzpDetail") >= 0) { WebLinks.Add(new WebLink { Title = title, Url = url + link, Desc = GetDetailData(url + link) }); } } //返回 MODEL return View(WebLinks); } public List<WebLink> GetDetailData(string url) { //创建一个请求 WebRequest webReq = System.Net.WebRequest.Create(url); //获取响应 WebResponse webRes = webReq.GetResponse(); //使用GB2312的编码方式 获取响应的文本流 StreamReader sReader = new StreamReader(webRes.GetResponseStream(), System.Text.Encoding.GetEncoding("gb2312")); //将文本读出 string content = sReader.ReadToEnd(); //正则匹配 MatchCollection matchCollecti = Regex.Matches(content, Detailhtmlregstr, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline); //创建一个Model用于前台查看 List<WebLink> WebLinks = new List<WebLink>(); //遍历每一个匹配 foreach (Match ma in matchCollecti) { //获取重要的信息 string title = ma.Groups["text"].Value; string link = ma.Groups["url"].Value; //因为需要的链接地址都有showMemberzpDetail.asp 所以用他做为过滤 if (ma.Success && link.IndexOf("showPosDetail_new") >= 0) { WebLinks.Add(new WebLink { Title = title, Url = url + link }); } } return WebLinks; } } public class WebLink { public WebLink() { Desc = new List<WebLink>(); } public string Title { get; set; } public string Url { get; set; } public List<WebLink> Desc { get; set; } } }
VIEW代码
@model IEnumerable<YanNis.WebSite.Collect.MasRC.Controllers.WebLinks> <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> <ul> @foreach (var m in Model) { <li><a href="@m.Url">@m.Title</a> <ul> @foreach (var n in m.Desc) { <li><a href="@n.Url">@n.Title</a></li> } </ul> </li> } </ul> </div> </body> </html>
效果:
有没有发现 其实Controller里有很多相同重复的代码,我们可以将其简化,增加可读性,使用递归去获取
简化后的代码:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text.RegularExpressions; using System.Web; using System.Web.Mvc; namespace YanNis.WebSite.Collect.MasRC.Controllers { public class HomeController : Controller { string urllink = "http://www.masrc.com.cn/"; public ActionResult Index() { string htmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*><font[^>]*>(?<text>(?:(?!</?a\b).)*)</font></a>"; List<WebLinks> WebLinks = GetDetailData("", htmlregstr, "showMemberzpDetail"); //返回 MODEL return View(WebLinks); } public List<WebLinks> GetDetailData(string url, string Regstr, string Filterstr) { if (url == "") url = urllink; //创建一个请求 WebRequest webReq = System.Net.WebRequest.Create(url); //获取响应 WebResponse webRes = webReq.GetResponse(); //使用GB2312的编码方式 获取响应的文本流 StreamReader sReader = new StreamReader(webRes.GetResponseStream(), System.Text.Encoding.GetEncoding("gb2312")); //将文本读出 string content = sReader.ReadToEnd(); //正则匹配 MatchCollection matchCollecti = Regex.Matches(content, Regstr, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline); //创建一个Model用于前台查看 List<WebLinks> WebLinks = new List<WebLinks>(); //没有了<font> 增加一个<td> string Detailhtmlregstr = @"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)?[^>]*>(?<text>(?:(?!</?a\b).)*)</a>"; //遍历每一个匹配 foreach (Match ma in matchCollecti) { //获取重要的信息 string title = ma.Groups["text"].Value; string link = ma.Groups["url"].Value; //过滤 if (ma.Success && link.IndexOf(Filterstr) >= 0) { WebLinks w = new WebLinks() { Title = title, Url = urllink + link }; if (Filterstr != "showPosDetail_new") w.Desc = GetDetailData(w.Url, Detailhtmlregstr, "showPosDetail_new"); WebLinks.Add(w); } } return WebLinks; } } public class WebLinks { public WebLinks() { Desc = new List<WebLinks>(); } public string Title { get; set; } public string Url { get; set; } public List<WebLinks> Desc { get; set; } } }
很简单吧,采集是多么的简单呀
如何防采集呢,可以学习学习淘宝,之前通过淘宝客链接采集过淘宝的页面,普通办法是无法采集到,只能获取空白的页面,是通过添加修改HEADERS才能获取到,可以借鉴一下。明天就把怎么通过淘宝客的链接获取淘宝宝贝详情页的方法给放出来,我有可能会忘记哦^^
这段代码是作为演示,代码可以继续优化,增加可读性和响应速度,也可改为AJAX异步获取...