C#写一个采集器
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Web;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions; namespace chinaz
{
class Program
{
static void Main(string[] args)
{ string cookie = null;
using (StreamReader sr = new StreamReader("cookie.txt"))
{
cookie = sr.ReadToEnd();
sr.Close();
}
//string tmp = SRWebClient.GetPage(
// "http://bbs.chinaz.com/Members.html?
// page=1&sort=CreateDate&desc=true&keyword=",
// Encoding.UTF8, cookie);
int a = int.Parse(Console.ReadLine());
int b = int.Parse(Console.ReadLine());
string url = Console.ReadLine(); Hashtable hash = new Hashtable();
Encoding encoding = Encoding.GetEncoding(Console.ReadLine()); for (int i = a; i <= b; i++)
{
string html = SRWebClient.GetPage(string.Format(url, i), encoding, cookie);
//Console.WriteLine(html);
if (html != null && html.Length > )
{
Match m = Regex.Match(html,
@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
while (m != null && m.Value != null && m.Value.Trim() != string.Empty)
{
if (!hash.Contains(m.Value))
{
Console.WriteLine(m.Value);
using (StreamWriter sw = new StreamWriter("mail.txt", true))
{
sw.WriteLine(m.Value);
sw.Close();
}
hash.Add(m.Value, string.Empty);
}
m = m.NextMatch();
} }
} Console.Write("完成");
Console.ReadLine();
}
} public class SRWebClient
{
public CookieCollection cookie;
public SRWebClient()
{
cookie = null;
} #region 从包含多个 Cookie 的字符串读取到 CookieCollection 集合中
private static void AddCookieWithCookieHead(
ref CookieCollection cookieCol, string cookieHead, string defaultDomain)
{
if (cookieCol == null) cookieCol = new CookieCollection();
if (cookieHead == null) return;
string[] ary = cookieHead.Split(';');
for (int i = ; i < ary.Length; i++)
{
Cookie ck = GetCookieFromString(ary[i].Trim(), defaultDomain);
if (ck != null)
{
cookieCol.Add(ck);
}
}
}
#endregion #region 读取某一个 Cookie 字符串到 Cookie 变量中
private static Cookie GetCookieFromString(
string cookieString, string defaultDomain)
{
string[] ary = cookieString.Split(',');
Hashtable hs = new Hashtable();
for (int i = ; i < ary.Length; i++)
{
string s = ary[i].Trim();
int index = s.IndexOf("=");
if (index > )
{
hs.Add(s.Substring(, index), s.Substring(index + ));
}
}
Cookie ck = new Cookie();
foreach (object Key in hs.Keys)
{
if (Key.ToString() == "path")
ck.Path = hs[Key].ToString(); else if (Key.ToString() == "expires")
{
//ck.Expires=DateTime.Parse(hs[Key].ToString();
}
else if (Key.ToString() == "domain")
ck.Domain = hs[Key].ToString();
else
{
ck.Name = Key.ToString();
ck.Value = hs[Key].ToString();
}
}
if (ck.Name == "") return null;
if (ck.Domain == "") ck.Domain = defaultDomain;
return ck;
}
#endregion /**/
/// <TgData>
/// <Alias>下载Web源代码</Alias>
/// </TgData>
public string DownloadHtml(string URL, bool CreateCookie)
{
try
{
HttpWebRequest request =
HttpWebRequest.Create(URL) as HttpWebRequest;
if (cookie != null)
{
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookie);
}
request.AllowAutoRedirect = false;
//request.MaximumAutomaticRedirections = 3;
request.Timeout = ; HttpWebResponse res = (HttpWebResponse)request.GetResponse();
string r = ""; System.IO.StreamReader S1 =
new System.IO.StreamReader(res.GetResponseStream(),
System.Text.Encoding.Default);
try
{
r = S1.ReadToEnd();
if (CreateCookie)
cookie = res.Cookies;
}
catch (Exception er)
{
//Log l = new Log();
//l.writelog("下载Web错误", er.ToString());
}
finally
{
res.Close();
S1.Close();
} return r;
} catch
{ } return string.Empty;
} /**/
/// <TgData>
/// <Alias>下载文件</Alias>
/// </TgData>
public long DownloadFile(string FileURL,
string FileSavePath, bool CreateCookie)
{
long Filelength = ;
HttpWebRequest req = HttpWebRequest.Create(FileURL) as HttpWebRequest; if (cookie != null)
{
req.CookieContainer = new CookieContainer();
req.CookieContainer.Add(cookie);
}
req.AllowAutoRedirect = true; HttpWebResponse res = req.GetResponse() as HttpWebResponse;
if (CreateCookie)
cookie = res.Cookies;
System.IO.Stream stream = res.GetResponseStream();
try
{
Filelength = res.ContentLength; byte[] b = new byte[]; int nReadSize = ;
nReadSize = stream.Read(b, , ); System.IO.FileStream fs = System.IO.File.Create(FileSavePath);
try
{
while (nReadSize > )
{
fs.Write(b, , nReadSize);
nReadSize = stream.Read(b, , );
}
}
finally
{
fs.Close();
}
}
catch (Exception er)
{
//Log l = new Log();
//l.writelog("下载文件错误", er.ToString());
}
finally
{
res.Close();
stream.Close();
} return Filelength;
} /**/
/// <TgData>
/// <Alias>提交数据</Alias>
/// </TgData>
public string Request(string RequestPageURL,
RequestData Data, bool CreateCookie)
{
StreamReader reader = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
try
{
string StrUrl = RequestPageURL;
request = HttpWebRequest.Create(StrUrl) as HttpWebRequest; string postdata = Data.GetData();
request.Referer = RequestPageURL;
request.AllowAutoRedirect = false;
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; "+
"Windows NT 5.2; SV1; Maxthon; "+
".NET CLR 1.1.4322; .NET CLR 2.0.50727)";
request.Timeout = ; if (cookie != null)
{
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookie);
} Uri u = new Uri(StrUrl); if (postdata.Length > ) //包含要提交的数据 就使用Post方式
{
request.ContentType = "application/x-www-form-urlencoded"; //作为表单请求
request.Method = "POST"; //方式就是Post //把提交的数据换成字节数组
Byte[] B = System.Text.Encoding.UTF8.GetBytes(postdata);
request.ContentLength = B.Length; System.IO.Stream SW = request.GetRequestStream(); //开始提交数据
SW.Write(B, , B.Length);
SW.Close();
} response = request.GetResponse() as HttpWebResponse;
if (CreateCookie)
//cookie = response.Cookies;
AddCookieWithCookieHead(ref cookie,
response.Headers["Set-Cookie"], request.RequestUri.Host);
reader = new StreamReader(response.GetResponseStream(), Encoding.Default); return reader.ReadToEnd();
}
catch (Exception ex)
{
string x = ex.StackTrace;
}
finally
{
if (response != null)
response.Close();
} return string.Empty;
} public bool PostDownload(RequestData Data, out string file)
{
file = null;
StreamReader reader = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
try
{
string StrUrl = "http://www.imobile.com.cn/wapdiyringdownload.php";
request = HttpWebRequest.Create(StrUrl) as HttpWebRequest; string postdata = Data.GetData();
request.Referer = StrUrl;
request.AllowAutoRedirect = false;
request.UserAgent =
"Mozilla/4.0 (compatible; MSIE 6.0; "+
"Windows NT 5.2; SV1; Maxthon; "+
".NET CLR 1.1.4322; .NET CLR 2.0.50727)";
request.Timeout = ; if (cookie != null)
{
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookie);
} Uri u = new Uri(StrUrl); if (postdata.Length > ) //包含要提交的数据 就使用Post方式
{
request.ContentType = "application/x-www-form-urlencoded"; //作为表单请求
request.Method = "POST"; //方式就是Post //把提交的数据换成字节数组
Byte[] B = System.Text.Encoding.UTF8.GetBytes(postdata);
request.ContentLength = B.Length; System.IO.Stream SW = request.GetRequestStream(); //开始提交数据
SW.Write(B, , B.Length);
SW.Close();
} response = request.GetResponse() as HttpWebResponse;
string des = response.Headers["Content-Disposition"].Trim();
file = des.Substring(des.IndexOf("filename=") + );
file = new Random().Next().ToString() + "/" + file; System.IO.Stream stream = response.GetResponseStream();
try
{
int Filelength = (int)response.ContentLength; byte[] b = new byte[]; int nReadSize = ;
nReadSize = stream.Read(b, , ); System.IO.FileStream fs = System.IO.File.Create("f:/mobileMusic/" + file);
try
{
while (nReadSize > )
{
fs.Write(b, , nReadSize);
nReadSize = stream.Read(b, , );
}
}
finally
{
fs.Close();
}
}
catch (Exception er)
{
//Log l = new Log();
//l.writelog("下载文件错误", er.ToString());
}
finally
{
response.Close();
stream.Close();
}
}
catch (Exception ex)
{
string x = ex.StackTrace;
}
finally
{
if (response != null)
response.Close();
}
return true;
}
#region GetPage
/// <summary>
/// 获取源代码
/// </summary>
/// <param name="url"></param>
/// <param name="coding"></param>
/// <param name="TryCount"></param>
/// <returns></returns>
public static string GetPage(string url, Encoding encoding, int TryCount)
{
for (int i = ; i < TryCount; i++)
{
string result = GetPage(url, encoding, null);
if (result != null && result != string.Empty)
return result;
} return string.Empty;
} /// <summary>
/// 获取源代码
/// </summary>
/// <param name="url"></param>
/// <param name="coding"></param>
/// <returns></returns>
public static string GetPage(string url, Encoding encoding, string cookie)
{
HttpWebRequest request = null;
HttpWebResponse response = null;
StreamReader reader = null;
try
{
request = (HttpWebRequest)WebRequest.Create(url);
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2;)";
request.Timeout = ;
request.AllowAutoRedirect = false;
if (cookie != null)
request.Headers["Cookie"] = cookie; response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK &&
response.ContentLength < * )
{
reader =
new StreamReader(response.GetResponseStream(), encoding);
string html = reader.ReadToEnd(); return html;
}
}
catch
{
}
finally
{ if (response != null)
{
response.Close();
response = null;
}
if (reader != null)
reader.Close(); if (request != null)
request = null; } return string.Empty;
}
#endregion
} public class RequestData
{
Hashtable hash = new Hashtable(); public RequestData()
{ } public string GetData()
{
string r = ""; foreach (string key in hash.Keys)
{
if (r.Length > ) r += "&";
r += key + "=" + hash[key];
} return r;
} public void AddField(string Field, string Value)
{
hash[Field] = Value;
} }
}
C#抓取网站下的链接下的网页数据怎么做??
1读取此网站的页面源代码
2利用正则取得所有超连接的内容
3把取得的超连接内容循环,再次操作1,2的步骤,这次2中写逻辑你想要的 数据 ---读取网页源代码---
protected void Page_Load(object sender, EventArgs e)
{
string strtemp;
strtemp = GetURLContent("http://go.microsoft.com/fwlink/?LinkId=25817", "utf-8");
//Response.ContentType = "application/x-www-form-urlencoded";
Response.Write(strtemp);
}
string GetURLContent(string url,string EncodingType)
{
string PetiResp = "";
Stream mystream;
//"http://go.microsoft.com/fwlink/?LinkId=25817"
//"utf-8"
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
req.AllowAutoRedirect = true;
System.Net.HttpWebResponse resp = (System.Net.HttpWebResponse)req.GetResponse();
if (resp.StatusCode == System.Net.HttpStatusCode.OK)
{
mystream = resp.GetResponseStream();
System.Text.Encoding encode = System.Text.Encoding.GetEncoding(EncodingType);
StreamReader readStream = new StreamReader(mystream, encode);
char[] cCont = new char[];
int count = readStream.Read(cCont, , );
while (count > )
{
// Dumps the 256 characters on a string and displays the string to the console.
String str = new String(cCont, , count);
PetiResp += str;
count = readStream.Read(cCont, , );
}
resp.Close();
return PetiResp;
}
resp.Close();
return null;
}
spider
C#蜘蛛程序spider 技术说明书 山东大学 李若寒
[email protected]
-- 一. 开发背景 提到搜索引擎,大家都不陌生,例如大家熟悉的google,百度,网络的信息浩如烟海,搜索引擎如海中灯塔,在人获得知识过程中起了极其巨大的作用. 然而网上的信息极其巨大,即使google这样的搜索巨人也不能覆盖全部页面,事实上google每28天完全更新一次数据库,而且其注重的只是网上的静态页面,这部分只占全球网页的十分之一左右. 例如:当你想从山大网上找一些考研的信息,由于对网站的不熟悉,不知道在哪部分找,而用google等搜索引擎搜索,找到的东西太多太杂,而且很多都不是山大网上的,不符合自己的初衷.这个时候,你就需要一个"蜘蛛程序",它能在很短的时间内,检索某个网站的全部页面,找寻你所需要的信息. 本软件就是一个用c#写成的蜘蛛程序,其核心组件spider.dll可独立使用,如有需要,直接在你的程序里添加对spider.dll的引用即可。 熟悉搜索引擎的同学可能知道,google,baidu等网站就是利用蜘蛛程序将Web页面收集到到自己的数据库,以便用户查询。 不仅仅是搜索引擎,个人用户可以用蜘蛛程序下载Web页面以便脱机使用,网站开发者可以利用蜘蛛程序扫描自己的Web检查无效的链接……对于不同的用户,蜘蛛程序有不同的用途。 项目文档里有全部的源代码,你可以按照自己的要求修改源代码以符合自己的要求 二. 软件原理 蜘蛛程序,正如它的名字一样,就象现实当中的蜘蛛在它的Web(蜘蛛网)上旅行,蜘蛛程序也按照类似的方式在Web链接织成的网上旅行。 蜘蛛程序需要一个初始链接(出发点),它会首先扫描起始页面包含的链接,然后访问这些链接指向的页面,再分析和追踪那些页面包含的链接。从理论上看,最终蜘蛛程序会访问到Internet上的每一个页面,因为Internet上几乎每一个页面总是被其他或多或少的页面引用。 原理图 三.分析设计
如上图所示,我将蜘蛛程序按程序流程分成如下几大主模块
() 链接处理模块:url类 () 连接模块:http类 () Html解析模块:HtmlAnalysis类,Attribute类,Attributelist类 () 存储结构模块:Hashtb类 队列 UrlQueue: 先进先出表,用于存储得到的所有Url;
Hashtable UrlHash: 用于存储访问过的链接,以访重复访问 () 流程调用模块:Spider类 用于整体流程的调用,实现原理图中的大循环 () 网页重要度计算算法:
. Value= ;
. Value= Value – Url.Count(‘\’) + ;
. Value= Value - Url.Count(‘?’) - Url.Count(‘#’) ;
. Return Value ;
是Internet上一种很有用的程序,搜索引擎利用蜘蛛程序将Web页面收集到数据 库,企业利用蜘蛛程序监视竞争对手的网站并跟踪变动,个人用户用蜘蛛程序下载Web
是Internet上一种很有用的程序,搜索引擎利用蜘蛛程序将Web页面收集到数据 库,企业利用蜘蛛程序监视竞争对手的网站并跟踪变动,个人用户用蜘蛛程序下载Web页面以便脱机使用,开发者利用蜘蛛程序扫描自己的Web检查无效的链 接……对于不同的用户,蜘蛛程序有不同的用途。那么,蜘蛛程序到底是怎样工作的呢?
蜘蛛是一种半自动的程序,就象现实当中的蜘蛛在它的Web(蜘蛛网)上旅行一样,蜘蛛程序也按照类似的方式在Web链接织成的网上旅行。蜘蛛程序之 所以是半自动的,是因为它总是需要一个初始链接(出发点),但此后的运行情况就要由它自己决定了,蜘蛛程序会扫描起始页面包含的链接,然后访问这些链接指 向的页面,再分析和追踪那些页面包含的链接。从理论上看,最终蜘蛛程序会访问到Internet上的每一个页面,因为Internet上几乎每一个页面总 是被其他或多或少的页面引用。
本文介绍如何用C#语言构造一个蜘蛛程序,它能够把整个网站的内容下载到某个指定的目录,程序的运行界面如图一。你可以方便地利用本文提供的几个核心类构造出自己的蜘蛛程序。
C#特别适合于构造蜘蛛程序,这是因为它已经内置了HTTP访问和多线程的能力,而这两种能力对于蜘蛛程序来说都是非常关键的。下面是构造一个蜘蛛程序要解决的关键问题:
· HTML分析:需要某种HTML解析器来分析蜘蛛程序遇到的每一个页面。
· 页面处理:需要处理每一个下载得到的页面。下载得到的内容可能要保存到磁盘,或者进一步分析处理。
· 多线程:只有拥有多线程能力,蜘蛛程序才能真正做到高效。
· 确定何时完成:不要小看这个问题,确定任务是否已经完成并不简单,尤其是在多线程环境下。
一、HTML解析
C#语言本身不包含解析HTML的能力,但支持XML解析;不过,XML有着严格的语法,为XML设计的解析器对HTML来说根本没用,因为 HTML的语法要宽松得多。为此,我们需要自己设计一个HTML解析器。本文提供的解析器是高度独立的,你可以方便地将它用于其它用C#处理HTML的场 合。
本文提供的HTML解析器由ParseHTML类实现,使用非常方便:首先创建该类的一个实例,然后将它的Source属性设置为要解析的HTML文档:
ParseHTML parse = new ParseHTML(); parse.Source = "<p>Hello World</p>";
接下来就可以利用循环来检查HTML文档包含的所有文本和标记。通常,检查过程可以从一个测试Eof方法的while循环开始:
while(!parse.Eof()) { char ch = parse.Parse();
Parse方法将返回HTML文档包含的字符--它返回的内容只包含那些非HTML标记的字符,如果遇到了HTML标记,Parse方法将返回0值,表示现在遇到了一个HTML标记。遇到一个标记之后,我们可以用GetTag()方法来处理它。
if(ch==) { HTMLTag tag = parse.GetTag(); }
一般地,蜘蛛程序最重要的任务之一就是找出各个HREF属性,这可以借助C#的索引功能完成。例如,下面的代码将提取出HREF属性的值(如果存在的话)。
Attribute href = tag["HREF"]; string link = href.Value; 获得Attribute对象之后,通过Attribute.Value可以得到该属性的值。
二、处理HTML页面
下面来看看如何处理HTML页面。首先要做的当然是下载HTML页面,这可以通过C#提供的HttpWebRequest类实现:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_uri); response = request.GetResponse(); stream = response.GetResponseStream();
接下来我们就从request创建一个stream流。在执行其他处理之前,我们要先确定该文件是二进制文件还是文本文件,不同的文件类型处理方式也不同。下面的代码确定该文件是否为二进制文件。
if( !response.ContentType.ToLower().StartsWith("text/") ) { SaveBinaryFile(response); return null; } string buffer = "",line;
如果该文件不是文本文件,我们将它作为二进制文件读入。如果是文本文件,首先从stream创建一个StreamReader,然后将文本文件的内容一行一行加入缓冲区。
reader = new StreamReader(stream); while( (line = reader.ReadLine())!=null ) { buffer+=line+"\r\n"; } 装入整个文件之后,接着就要把它保存为文本文件。 SaveTextFile(buffer);
下面来看看这两类不同文件的存储方式。
二进制文件的内容类型声明不以"text/"开头,蜘蛛程序直接把二进制文件保存到磁盘,不必进行额外的处理,这是因为二进制文件不包含HTML,因此也不会再有需要蜘蛛程序处理的HTML链接。下面是写入二进制文件的步骤。
首先准备一个缓冲区临时地保存二进制文件的内容。 byte []buffer = new byte[]; 接下来要确定文件保存到本地的路径和名称。如果要把一个myhost.com网站的内容下载到本地 的c:\test文件夹,二进制文件的网上路径和名称是http://myhost.com/images/logo.gif,则 本地路径和名称应当是c:\test\images\logo.gif。与此同时,我们还要确保c:\test目录下已 经创建了images子目录。这部分任务由convertFilename方法完成。 string filename = convertFilename( response.ResponseUri ); convertFilename方法分离HTTP地址,创建相应的目录结构。确定了输出文件的名字和路径之 后就可以打开读取Web页面的输入流、写入本地文件的输出流。 Stream outStream = File.Create( filename ); Stream inStream = response.GetResponseStream(); 接下来就可以读取Web文件的内容并写入到本地文件,这可以通过一个循环方便地完成。 int l; do { l = inStream.Read(buffer,0, buffer.Length); if(l>0) outStream.Write(buffer,0,l); } while(l>0); 写入整个文件之后,关闭输入流、输出流。 outStream.Close(); inStream.Close();
比较而言,下载文本文件更容易一些。文本文件的内容类型总是以"text/"开头。假设文件已被下载并保存到了一个字符串,这个字符串可以用来分析网页包含的链接,当然也可以保存为磁盘上的文件。下面代码的任务就是保存文本文件。
string filename = convertFilename( m_uri ); StreamWriter outStream = new StreamWriter( filename ); outStream.Write(buffer); outStream.Close();
在这里,我们首先打开一个文件输出流,然后将缓冲区的内容写入流,最后关闭文件。
三、多线程
多线程使得计算机看起来就象能够同时执行一个以上的操作,不过,除非计算机包含多个处理器,否则,所谓的同时执行多个操作仅仅是一种模拟出来的效果 --靠计算机在多个线程之间快速切换达到"同时"执行多个操作的效果。一般而言,只有在两种情况下多线程才能事实上提高程序运行的速度。第一种情况是计算 机拥有多个处理器,第二种情况是程序经常要等待某个外部事件。
对于蜘蛛程序来说,第二种情况正是它的典型特征之一,它每发出一个URL请求,总是要等待文件下载完毕,然后再请求下一个URL。如果蜘蛛程序能够同时请求多个URL,显然能够有效地减少总下载时间。
为此,我们用DocumentWorker类封装所有下载一个URL的操作。每当一个DocumentWorker的实例被创建,它就进入循环,等待下一个要处理的URL。下面是DocumentWorker的主循环:
while(!m_spider.Quit ) { m_uri = m_spider.ObtainWork(); m_spider.SpiderDone.WorkerBegin(); string page = GetPage(); if(page!=null) ProcessPage(page); m_spider.SpiderDone.WorkerEnd(); }
这个循环将一直运行,直至Quit标记被设置成了true(当用户点击"Cancel"按钮时,Quit标记就被设置成true)。在循环之内,我 们调用ObtainWork获取一个URL。ObtainWork将一直等待,直到有一个URL可用--这要由其他线程解析文档并寻找链接才能获得。 Done类利用WorkerBegin和WorkerEnd方法来确定何时整个下载操作已经完成。
从图一可以看出,蜘蛛程序允许用户自己确定要使用的线程数量。在实践中,线程的最佳数量受许多因素影响。如果你的机器性能较高,或者有两个处理器,可以设置较多的线程数量;反之,如果网络带宽、机器性能有限,设置太多的线程数量其实不一定能够提高性能。
四、任务完成了吗?
利用多个线程同时下载文件有效地提高了性能,但也带来了线程管理方面的问题。其中最复杂的一个问题是:蜘蛛程序何时才算完成了工作?在这里我们要借助一个专用的类Done来判断。
首先有必要说明一下"完成工作"的具体含义。只有当系统中不存在等待下载的URL,而且所有工作线程都已经结束其处理工作时,蜘蛛程序的工作才算完成。也就是说,完成工作意味着已经没有等待下载和正在下载的URL。
Done类提供了一个WaitDone方法,它的功能是一直等待,直到Done对象检测到蜘蛛程序已完成工作。下面是WaitDone方法的代码。
public void WaitDone() { Monitor.Enter(this); while ( m_activeThreads> ) { Monitor.Wait(this); } Monitor.Exit(this); }
WaitDone方法将一直等待,直到不再有活动的线程。但必须注意的是,下载开始的最初阶段也没有任何活动的线程,所以很容易造成蜘蛛程序一开始 就立即停止的现象。为解决这个问题,我们还需要另一个方法WaitBegin来等待蜘蛛程序进入"正式的"工作阶段。一般的调用次序是:先调用 WaitBegin,再接着调用WaitDone,WaitDone将等待蜘蛛程序完成工作。下面是WaitBegin的代码:
public void WaitBegin() { Monitor.Enter(this); while ( !m_started ) { Monitor.Wait(this); } Monitor.Exit(this); }
WaitBegin方法将一直等待,直到m_started标记被设置。m_started标记是由WorkerBegin方法设置的。工作线程在 开始处理各个URL之时,会调用WorkerBegin;处理结束时调用WorkerEnd。WorkerBegin和WorkerEnd这两个方法帮助 Done对象确定当前的工作状态。下面是WorkerBegin方法的代码:
public void WorkerBegin() { Monitor.Enter(this); m_activeThreads++; m_started = true; Monitor.Pulse(this); Monitor.Exit(this); }
WorkerBegin方法首先增加当前活动线程的数量,接着设置m_started标记,最后调用Pulse方法以通知(可能存在的)等待工作线 程启动的线程。如前所述,可能等待Done对象的方法是WaitBegin方法。每处理完一个URL,WorkerEnd方法会被调用:
public void WorkerEnd() { Monitor.Enter(this); m_activeThreads--; Monitor.Pulse(this); Monitor.Exit(this); }
WorkerEnd方法减小m_activeThreads活动线程计数器,调用Pulse释放可能在等待Done对象的线程--如前所述,可能在等待Done对象的方法是WaitDone方法。
结束语:本文介绍了开发Internet蜘蛛程序的基础知识,下面提供的源代码将帮助你进一步深入理解本文的主题。这里提供的代码非常灵活,你可以方便地将它用于自己的程序。
源码下载地址:http://myblog.workgroup.cn/files/folders/csharp/entry1639.aspx
用C#2.0实现网络蜘蛛(WebSpider)
摘要:本文讨论了如何使用C#.0实现抓取网络资源的网络蜘蛛。使用这个程序,可以通过一个入口网址(如 http://www.comprg.com.cn)来扫描整个互联网的网址,并将这些扫描到的网址所指向的网络资源下载到本地。然后可以利用其他的分析 工具对这些网络资源做进一步地分析,如提取关键词、分类索引等。也可以将这些网络资源作为数据源来实现象Google一样的搜索引擎。
关键词:C#2.0,Html,网络蜘蛛, 键树,正则表达式
一、引言 在最近几年,以Google为首的搜索引擎越来越引起人们的关注。由于在Google出现之前,很多提供搜索服务的公司都是使用人工从网络上搜集信息,并 将这些信息分类汇总后作为搜索引擎的数据源。如yahoo公司一开始就是通过数千人不停地从网上搜集供查询的信息。这样做虽然信息的分类会很人性化,也比 较准确,但是随着互联网信息爆炸式地增长,通过人工的方式来搜集信息已经不可能满足网民对信息的需求了。然而,这一切随着Google的出现而得到了彻底 改变。Google一反常规的做法,通过程序7*24地从网上不停地获取网络资源,然后通过一些智能算法分析这些被下载到本地的网络资源,最后将这些分析 后的数据进行索引后就形成了一套完整的基本上不需要人工干预的搜索引擎。使用这种模式的搜索引擎甚至可以在几天之内就可获取Internet中的所有信 息,同时也节省了大量的资金和时间成本。而这种搜索引擎最重要的组成部分之一就是为搜索引擎提供数据源的网络蜘蛛。也就是说,实现网络蜘蛛是实现搜索引擎 的第一步,也是最重要的一步。 二、网络蜘蛛的基本实现思想和实现步骤 网络蜘蛛的主要作用是从Internet上不停地下载网络资源。它的基本实现思想就是通过一个或多个入口网址来获取更多的URL,然后通过对这些URL所 指向的网络资源下载并分析后,再获得这些网络资源中包含的URL,以此类推,直到再没有可下的URL为止。下面是用程序实现网络蜘蛛的具体步骤。 . 指定一个(或多个)入口网址(如http://www.comprg.com.cn),并将这个网址加入到下载队列中(这时下载队列中只有一个或多个入口网址)。
. 负责下载网络资源的线程从下载队列中取得一个或多个URL,并将这些URL所指向的网络资源下载到本地(在下载之前,一般应该判断一下这个URL是否已经 被下载过,如果被下载过,则忽略这个URL)。如果下载队列中没有URL,并且所有的下载线程都处于休眠状态,说明已经下载完了由入口网址所引出的所有网 络资源。这时网络蜘蛛会提示下载完成,并停止下载。
. 分析这些下载到本地的未分析过的网络资源(一般为html代码),并获得其中的URL(如标签<a>中href属性的值)。
. 将第3步获得的URL加入到下载队列中。并重新执行第2步。 三、实现数据的输入输出 从实现网络蜘蛛的步骤中我们可以看出,下载队列的读、写URL的操作一直贯穿于整个系统中。虽然这个下载队列可以用.Queue类实现,但是各位读者要清 楚地知道,在互联网上的URL可不是几十个、几百个这么少。而是以千万计的。这么多的URL显然不能保存在内存中的Queue对象中。因此,我们需要将它 保存在容量更大的存储空间中,这就是硬盘。
本文采用了一个普通的文本文件来保存需要下载和分析的URL(这个文本文件也就是下载队列)。存储格式是每一行为一个URL。既然将URL都保存在了文本文件中,就需要对这个文本文件进行读写。因此,在本节实现了一个用于操作这个文本文件的FileIO类。
在实现FileIO类之前,先来说一下要如何操作这个文本文件。既然要将这个文件作为队列使用,那么就需要对这个文件进行追加行和从文件开始部分读取数据操作。让我们首先来实现向文件中追加行操作。实现代码如下: 向文件中追加行的实现代码
// 这两个变量为类全局变量
private FileStream fsw;
private StreamWriter sw; // 创建用于向文件中追加行的文件流和StreamWriter对象
public void OpenWriteFile(string file)
{
if (!File.Exists(file)) // 如果文件不存在,先创建这个文件
File.Create(file).Close();
// 以追加模式打开这个文件
fsw = new FileStream(file, FileMode.Append ,FileAccess.Write, FileShare.ReadWrite);
// 根据创建的FileStream对象来创建StreamWriter对象
sw = new StreamWriter(fsw);
}
// 关闭写文件流
public void CloseWriteFile()
{
if (fsr != null)
fsw.Close();
}
// 向文件中追加一行字符串
public void WriteLine(string s)
{
sw.WriteLine(s);
sw.Flush(); // 刷新写入缓冲区,使这一行对于读文件流可见
} 在实现上述的代码时要注意,在创建FileStream对象时,必须使用FileShare.ReadWrite,否则这个文件无法被两个或两个以上的 Stream打开,也就是说下面要介绍的读文件流将无法操作这个被写文件流打开的文件。从文件中读取行的实现代码如下: 从文件中读取行的实现代码
// 这两个变量为类全局变量
private FileStream fsr;
private StreamReader sr; // 创建用于读取文件行的文件流和StreamWriter对象
public void OpenReadFile(string file)
{
if (!File.Exists(file)) // 如果文件不存在,首先创建这个文件
File.Create(file).Close();
fsr = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Read,
FileShare.ReadWrite);
sr = new StreamReader(fsr);
}
// 关闭读文件流
public void CloseReadFile()
{
if(fsr != null)
fsr.Close();
}
// 从文件中读取一行
public string ReadLine()
{
if(sr.EndOfStream) // 如果文件流指针已经指向文件尾部,返回null
return null;
return sr.ReadLine();
} 除了上述的读写文件的代码外,FileIO还提供了一个IsEof方法用来判断文件流指针是否位于文件尾部。IsEof方法的实现代码如下如下: IsEof方法的实现代码
// 用于判断文件流指针是否位于文件尾部
public bool IsEof()
{
return sr.EndOfStream;
} FileIO类不仅仅用于对下载队列的读写。在后面我们还会讲到,网络蜘蛛通过多线程下载网络资源时,每一个线程将自己下载的网络资源保存在属于自己的一 个目录中。每个这样的目录都有一个index.txt文件,这个文件保存了当前目录的网络资源的URL。向index.txt文件中追加URL也用到了 FileIO(index.txt不需要读取,只需要不断地追加行)。 四、线程类的实现 要想使网络蜘蛛在有限的硬件环境下尽可能地提高下载速度。最廉价和快捷的方法就是使用多线程。在.net framework2.0中提供了丰富的线程功能。其中的核心线程类是Thread。一般可使用如下的代码创建并运行一个线程: 在C#中使用线程的演示代码
private void fun()
{
// 线程要执行的代码
}
public void testThread()
{
Thread thread;
thread = new Thread(fun); // 创建一个Thread对象,并将fun设为线程运行的方法
thread.Start(); // 运行一个线程
} 虽然上面的代码比较简单地创建并运行了一个线程,但是这段代码看起来仍然不够透明,也就是客户端在调用线程时仍然需要显式地使用Thread类。下面我们 来实现一个用于创建线程的MyThread类。C#中的任何类只需要继承这个类,就可以自动变成一个线程类。MyThread类的代码如下: MyThread类的实现代码
// 任何C#类继承MyThread后,就会自动变成一个线程类
class MyThread
{
private Thread thread;
public MyThread()
{
thread = new Thread(run); // 创建Thread对象
}
// 用于运行线程代码的方法,MyThread的子类必须覆盖这个方法
public virtual void run()
{
}
public void start()
{
thread.Start(); // 开始运行线程,也就是开始执行run方法
}
// 使当前线程休眠millisecondsTimeout毫秒
public void sleep(int millisecondsTimeout)
{
Thread.Sleep(millisecondsTimeout);
}
} 我们可参照如下的代码使用MyThread类: 测试的ThreadClass类的代码
class ThreadClass : MyThread
{
public override void run()
{
// 要执行的线程代码
}
} // 测试ThreadClass类
public void testThreadClass()
{
ThreadClass tc = new ThreadClass();
tc.start(); // 开始运行线程,也就是执行run方法
} 各位读者可以看看,上面的代码是不是要比直接使用Thread类更方便、直观、易用,还有些面向对象的感觉! 五、用多线程下载网络资源 一般来说,网络蜘蛛都是使用多线程来下载网络资源的。至于如何使用多线程来下载,各个版本的网络蜘蛛不尽相同。为了方便和容易理解,本文所讨论的网络蜘蛛 采用了每一个线程负责将网络资源下载到一个属于自己的目录中,也就是说,每一个线程对应一个目录。而在当前目录中下载的网络资源达到一定的数目后(如 ),这个线程就会再建立一个新目录,并从0开始计数继续下载网络资源。在本节中将介绍一个用于下载网络资源的线程类 DownLoadThread。这个类的主要功能就是从下载队列中获得一定数量的URL,并进行下载和分析。在DownLoadThread类中涉及到很 多其他重要的类,这些类将在后面的部分介绍。在这里我们先看一下DownLoadThread类的实现代码。 DownLoadThread类的代码
class DownLoadThread : MyThread
{
// ParseResource类用于下载和分析网络资源
private ParseResource pr = new ParseResource();
private int currentCount = ; // 当前下载目录中的网页数
// 用于向每个线程目录中的index.txt中写当前目录的URL
private FileIO fileIO = new FileIO();
private string path; // 当前的下载目录(后面带“\")
private string[] patterns; // 线程不下载符合patterns中的正则表达式的URL
public bool stop = false; // stop为true,线程退出
public int threadID; // 当前线程的threadID,用于区分其他的线程 public DownLoadThread(string[] patterns)
{
pr.findUrl += findUrl; // 为findUrl事件赋一个方法
this.patterns = patterns;
}
// 这是一个事件方法,每获得一个URL时发生
private void findUrl(string url)
{
Common.addUrl(url); // 将获得的URL加到下载队列中
}
private void openFile() // 打开下载目录中的index.txt文件
{
fileIO.CloseWriteFile();
fileIO.OpenWriteFile(path + Common.indexFile);
}
public override void run() // 线程运行方法
{
LinkedList<string> urls = new LinkedList<string>();
path = Common.getDir(); // 获得下载目录
openFile();
while (!stop)
{
// 当下载队列中没有URL时,进行循环等待
while (!stop && urls.Count == )
{
Common.getUrls(urls, ); // 从下载队列中获得20个url
if (urls.Count == ) // 如果未获得url
{
// 通知系统当前线程已处于等待状态,
// 如果所有的线程都处于等待状态,
// 说明所有的网络资源都被下载完了
Common.threadWait(threadID);
sleep(); // 当前线程休眠5秒
}
}
StringBuilder sb = new StringBuilder();
foreach (string url in urls) // 循环对这20个url进行循环下载分析
{
if (stop) break;
// 如果当前下载目录的资源文件数大于等于最大文件数目时,
// 建立一个新目录,并继续下载
if (currentCount >= Common.maxCount)
{
path = Common.getDir();
openFile();
currentCount = ; // 目录
}
// 每个下载资源文件名使用5位的顺序号保存(没有扩展名),
// 如00001、00002。下面的语句是格式化文件名
string s = string.Format("{0:D5}", currentCount + );
sb.Remove(, sb.Length);
sb.Append(s);
sb.Append(":");
sb.Append(url);
try
{
// 下载和分析当前的url
pr.parse(url, path + s, patterns);
Common.Count++;
// 将当前的url写入index.txt
fileIO.WriteLine(sb.ToString());
currentCount++;
}
catch (Exception e)
{ }
}
urls.Clear();
}
}
}
} 六、分析网络资源 对下载的网络资源进行分析是网络蜘蛛中最重要的功能之一。这里网络资源主要指的是html代码中<a>标签的href属性值。状态和状态之间会根据从html文件中读入的字符进行切换。下面是状态之间切换的描述。 状态0:读入'<'字符后切换到状态1,读入其他的字符,状态不变。
状态1:读入'a'或'A',切换到状态2,读入其他的字符,切换到状态0。
状态2:读入空格或制表符(\t),切换到状态3,读入其他的字符,切换到状态0。
状 态3:读入'>',成功获得一个<a>,读入其他的字符,状态不变。为了更容易说明问题。在本文给出的网络蜘蛛中只提取了html代码 中<a>中的href属性中的url。本文中所采用的分析方法是分步进行提取href。首先将html代码中的<a>标签整个提 出来。不包括</a>和前面的字符,如<a href="http://www.comprg.com.cn">comprg</a>中只提取<a href="http://www.comprg.com.cn">,而comprg</a>将被忽略,因为这里并没有url。
本文使用了一个状态机来的提取<a>,这个状态机分为五个状态( 至 )。第一个状态是初始态,最后一个状态为终止态,如果到达最后一个状态,说明已经成功获得了一个<a> 状态机如图1所示。 图1 最后一个双环的状态是最终态。下面让我们来看看获得<a>的实现代码。 getA方法的实现
// 获得html中的<a>
private void getA()
{
char[] buffer = new char[];
int state = ;
String a = ""; while (!sr.EndOfStream)
{
int n = sr.Read(buffer, , buffer.Length);
for (int i = ; i < n; i++)
{
switch (state)
{
case : // 状态0
if (buffer[i] == '<') // 读入的是'<'
{
a += buffer[i];
state = ; // 切换到状态1
}
break;
case : // 状态1
if (buffer[i] == 'a' || buffer[i] == 'A') // 读入是'a'或'A'
{
a += buffer[i];
state = ; // 切换到状态2
}
else
{
a = "";
state = ; // 切换到状态0
}
break;
case : // 状态2
if (buffer[i] == ' ' || buffer[i] == '\t') // 读入的是空格或'\t'
{
a += buffer[i];
state = ;
}
else
{
a = "";
state = ; // 切换到状态0
}
break;
case : // 状态3
if (buffer[i] == '>') // 读入的是'>',已经成功获得一个<a>
{
a += buffer[i];
try
{
string url = getUrl(getHref(a)); // 获得<a>中的href属性的值
if (url != null)
{
if (findUrl != null)
findUrl(url); // 引发发现url的事件 }
}
catch (Exception e)
{
}
state = ; // 在获得一个<a>后,重新切换到状态0
}
else
a += buffer[i];
break;
}
}
}
} 在getA方法中除了切换到状态0外,其他的状态切换都将已经读入的字符赋给String变量a,如果最后发现变量a中的字符串不可能是<a>后,就将a清空,并切换到状态0后重新读入字符。
在getA方法中使用了一个重要的方法getHref来从<a>中获得href部分。getHref方法的实现如下: getHref方法的实现
// 从<a>中获得Href
private String getHref(string a)
{
try
{
string p = @"href\s*=\s*('[^']*'|""[^""]*""|\S+\s+)"; // 获得Href的正则表达式
MatchCollection matches = Regex.Matches(a, p,
RegexOptions.IgnoreCase |
RegexOptions.ExplicitCapture); foreach (Match nextMatch in matches)
{
return nextMatch.Value; // 返回href
}
return null;
}
catch (Exception e)
{
throw e;
}
} 在getHref方法中使用了正则表达式从<a>中获得href。在<a>中正确的href属性格式有三种情况,这三种情况的主要区别是url两边的符号,如单引号、双引号或没有符号。这三种情况如下所示:
情况1: <a href = "http://www.comprg.com.cn" > comprg</a>
情况2: <a href = 'http://www.comprg.com.cn' > comprg</a>
情况3: <a href = http://www.comprg.com.cn > comprg</a>
getHref方法中的p存储了用于过滤这三种情况的href,也就是说,使用正则表达式可以获得上述三种情况的href如下: 从情况1获得得的href:href = "http://www.comprg.com.cn"
从情况2获得得的href:href = 'http://www.comprg.com.cn'
从情况3获得得的href:href = http://www.comprg.com.cn 在获得上述的href后,需要将url提出来。这个功能由getUrl完成,这个方法的实现代码如下: getUrl方法的实现
// 从href中提取url
private String getUrl(string href)
{
try
{
if (href == null) return href;
int n = href.IndexOf('='); // 查找'='位置
String s = href.Substring(n + );
int begin = , end = ;
string sign = "";
if (s.Contains("\"")) // 第一种情况
sign = "\"";
else if (s.Contains("'")) // 第二种情况
sign = "'";
else // 第三种情况
return getFullUrl(s.Trim());
begin = s.IndexOf(sign);
end = s.LastIndexOf(sign); return getFullUrl(s.Substring(begin + , end - begin - ).Trim());
}
catch (Exception e)
{
throw e;
}
} 在获得url时有一点应该注意。有的url使用的是相对路径,也就是没有“http://host”部分,但将url保存时需要保存它们的完整路径。这就需要根据相对路径获得它们的完整路径。这个功能由getFullUrl方法完成。这个方法的实现代码如下: getFullUrl方法的实现代码
// 将相对路径变为绝对路径
private String getFullUrl(string url)
{
try
{
if (url == null) return url;
if (processPattern(url)) return null; // 过滤不想下载的url
// 如果url前有http://或https://,为绝对路径,按原样返回
if (url.ToLower().StartsWith("http://") || url.ToLower().StartsWith("https://"))
return url;
Uri parentUri = new Uri(parentUrl);
string port = "";
if (!parentUri.IsDefaultPort)
port = ":" + parentUri.Port.ToString();
if (url.StartsWith("/")) // url以"/"开头,直接放在host后面
return parentUri.Scheme + "://" + parentUri.Host + port + url;
else // url不以"/"开头,放在url的路径后面
{
string s = "";
s = parentUri.LocalPath.Substring(, parentUri.LocalPath.LastIndexOf("/"));
return parentUri.Scheme + "://" + parentUri.Host + port + s + "/" + url;
}
}
catch (Exception e)
{
throw e;
}
} 在ParseResource中还提供了一个功能就是通过正则表达式过滤不想下载的url,这个功能将通过processPattern方法完成。实现代码如下: processPattern方法的实现代码
// 如果返回true,表示url符合pattern,否则,不符合模式
private bool processPattern(string url)
{
foreach (string p in patterns)
{ if (Regex.IsMatch(url, p, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)
&& !p.Equals(""))
return true;
}
return false;
}
ParseResource类在分析html代码之前,先将html下载到本地的线程目录中,再通过FileStream打开并读取待分析的数据。ParseResource类其他的实现代码请读者参阅本文提供的源代码。 七、键树的实现 在获取Url的过程中,难免重复获得一些Url。这些重复的Url将大大增加网络蜘蛛的下载时间,以及会导致其他的分析工具重复分析同一个html。因 此,就需要对过滤出重复的Url,也就是说,要使网络蜘蛛下载的Url都是唯一的。达到这个目的的最简单的方法就是将已经下载过的Url保存到一个集合 中,然后在下载新的Url之前,在这个集合中查找这个新的Url是否被下载过,如果下载过,就忽略这个Url。
这个功能从表面上看非常简单,但由于我们处理的是成千上万的Url,要是将这些Url简单地保存在类似List一样的集合中,不仅会占用大量的内存空间, 而且当Url非常多时,如一百万。这时每下载一个Url,就要从这一百万的Url中查找这个待下载的Url是否存在。虽然可以使用某些查找算法(如折半查 找)来处理,但当数据量非常大时,任何查找算法的效率都会大打折扣。因此,必须要设计一种新的存储结构来完成这个工作。这个新的数据存储结构需要具有两个 特性: . 尽可能地减少存储Url所使用的内存。
. 查找Url的速度尽可能地快(最好的可能是查找速度和Url的数量无关)。 下面先来完成第一个特性。一般一个Url都比较长,如平均每个Url有50个字符。如果有很多Url,每个Url占50个字符,一百万个Url就是会占用 50M的存储空间。而我们保存Url的目的只有一个,就是查找某一个Url是否存在。因此,只需要将Url的Hashcode保存起来即可。由于 Hashcode为Int类型,因此,Hashcode要比一个Url字符串使用更少的存储空间。
对于第二个特性,我们可以使用数据结构中的键树来解决。假设有一个数是4532。首先将其转换为字符串。然后每个键树节点有10个(0至9)。这样4532的存储结构如图2所示: 图2 从上面的数据结构可以看出,查找一个整数只和这个整数的位数有关,和整数的数量无关。这个键树的实现代码如下: KeyTree的实现代码
class KeyTreeNode // 键树节点的结构
{
// 指向包含整数下一个的结点的指针
public KeyTreeNode[] pointers = new KeyTreeNode[];
// 结束位标志,如果为true,表示当前结点为整数的最后一位
public bool[] endFlag = new bool[];
}
class KeyTree
{
private KeyTreeNode rootNode = new KeyTreeNode(); // 根结点
// 向键树中添加一个无符号整数
public void add(uint n)
{
string s = n.ToString();
KeyTreeNode tempNode = rootNode;
int index = ;
for (int i = ; i < s.Length; i++)
{
index = int.Parse(s[i].ToString()); // 获得整数每一位的值
if (i == s.Length - ) // 在整数的最后一位时,将结束位设为true
{
tempNode.endFlag[index] = true;
break;
}
if (tempNode.pointers[index] == null) // 当下一个结点的指针为空时,新建立一个结点对象
tempNode.pointers[index] = new KeyTreeNode();
tempNode = tempNode.pointers[index];
}
}
// 判断一个整数是否存在
public bool exists(uint n)
{
string s = n.ToString();
KeyTreeNode tempNode = rootNode;
int index = ;
for (int i = ; i < s.Length; i++)
{
if (tempNode != null)
{
index = int.Parse(s[i].ToString());
// 当整数的最后一位的结束标志为true时,表示n存在
if((i == s.Length - )&& (tempNode.endFlag[index] == true))
return true;
else
tempNode = tempNode.pointers[index];
}
else
return false;
}
return false;
}
} 上面代码中的KeyTreeNode之所以要使用结束标志,而不根据指针是否为空判断某个整数的存在,是因为可能存在长度不相等的整数,如4321和 。如果只使用指针判断。保存4321后,432也会被认为存在。而如果用结束标志后,在值为2的节点的结束标志为false,因此,表明432并不 存在。下面的UrlFilter使用了上面的键树来处理Url。 UrlFilter类的实现代码
// 用于将url重新组合后再加到键树中
// 如http://www.comprg.com.cn和http://www.comprg.com.cn/是一样的
// 因此,它们的hashcode也要求一样
class UrlFilter
{
public static KeyTree urlHashCode = new KeyTree();
private static object syncUrlHashCode = new object();
private static string processUrl(string url) // 重新组合Url
{
try
{
Uri uri = new Uri(url);
string s = uri.PathAndQuery;
if(s.Equals("/"))
s = "";
return uri.Host + s;
}
catch(Exception e)
{
throw e;
}
}
private static bool exists(string url) // 判断url是否存在
{
try
{
lock (syncUrlHashCode)
{
url = processUrl(url);
return urlHashCode.exists((uint)url.GetHashCode());
}
}
catch (Exception e)
{
throw e;
}
} public static bool isOK(string url)
{
return !exists(url);
}
// 加处理完的Url加到键树中
public static void addUrl(string url)
{
try
{
lock (syncUrlHashCode)
{
url = processUrl(url);
urlHashCode.add((uint)url.GetHashCode());
}
}
catch (Exception e)
{
throw e;
}
} } 八、其他部分的实现 到现在为止,网络蜘蛛所有核心代码都已经完成了。下面让我们做一个界面来使下载过程可视化。界面如图3所示。 图3 这个界面主要通过一个定时器每2秒钟获得个一次网络蜘蛛的下载状态。包括获得的URL数和已经下载的网络资源数。其中这些状态信息都保存在一个Common类的静态变量中。Common类和主界面的代码请读者参阅本文提供的源代码。 九、结束语 至此,网络蜘蛛程序已经全部完成了。但在实际应用中,光靠一台机器下载整个的网络资源是远远不够的。这就需要通过多台机器联合下载。然而这就会给我们带来 一个难题。就是这些机器需要对已经下载的Url进行同步。读者可以根据本文提供的例子,将其改成分布式的可多机同时下载的网络蜘蛛。这样网络蜘蛛的下载速 度将会有一个质的飞跃。
分类: Data Mining
自己用C#写一个采集器、蜘蛛
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Web;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions; namespace chinaz {
class Program
{
static void Main(string[] args)
{ string cookie = null; using (StreamReader sr = new StreamReader("cookie.txt"))
{
cookie = sr.ReadToEnd();
sr.Close();
} int a = int.Parse(Console.ReadLine());
int b = int.Parse(Console.ReadLine());
string url = Console.ReadLine(); Hashtable hash = new Hashtable(); Encoding encoding = Encoding.GetEncoding(Console.ReadLine()); for (int i = a; i <= b; i++) {
string html =
SRWebClient.GetPage(string.Format(url, i), encoding, cookie);
//Console.WriteLine(html);
if (html != null && html.Length > )
{
Match m =
Regex.Match(html,
@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
while (m != null && m.Value != null &&
m.Value.Trim() != string.Empty)
{
if (!hash.Contains(m.Value))
{
Console.WriteLine(m.Value);
using (StreamWriter sw =
new StreamWriter("mail.txt", true))
{
sw.WriteLine(m.Value);
sw.Close();
}
hash.Add(m.Value, string.Empty);
}
m = m.NextMatch();
} } } Console.Write("完成"); Console.ReadLine();
}
} public class SRWebClient {
public CookieCollection cookie;
public SRWebClient()
{
cookie = null;
} #region 从包含多个 Cookie 的字符串读取到 CookieCollection 集合中 private static void AddCookieWithCookieHead(ref CookieCollection cookieCol,
string cookieHead, string defaultDomain)
{
if (cookieCol == null) cookieCol = new CookieCollection();
if (cookieHead == null) return;
string[] ary = cookieHead.Split(';');
for (int i = ; i < ary.Length; i++)
{
Cookie ck = GetCookieFromString(ary[i].Trim(), defaultDomain);
if (ck != null)
{
cookieCol.Add(ck);
}
}
}
#endregion #region 读取某一个 Cookie 字符串到 Cookie 变量中 private static Cookie GetCookieFromString(string cookieString,
string defaultDomain)
{
string[] ary = cookieString.Split(',');
Hashtable hs = new Hashtable();
for (int i = ; i < ary.Length; i++)
{
string s = ary[i].Trim();
int index = s.IndexOf("=");
if (index > )
{
hs.Add(s.Substring(, index), s.Substring(index + ));
}
}
Cookie ck = new Cookie();
foreach (object Key in hs.Keys)
{
if (Key.ToString() == "path") ck.Path = hs[Key].ToString(); else if (Key.ToString() == "expires") {
//ck.Expires=DateTime.Parse(hs[Key].ToString();
}
else if (Key.ToString() == "domain")
ck.Domain = hs[Key].ToString();
else
{
ck.Name = Key.ToString();
ck.Value = hs[Key].ToString();
}
}
if (ck.Name == "") return null;
if (ck.Domain == "") ck.Domain = defaultDomain;
return ck;
}
#endregion /**/ /// <TgData>
/// <Alias>下载Web源代码</Alias>
/// </TgData>
public string DownloadHtml(string URL, bool CreateCookie)
{
try
{
HttpWebRequest request =HttpWebRequest.Create(URL) as HttpWebRequest;
if (cookie != null)
{
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookie);
}
request.AllowAutoRedirect = false;
//request.MaximumAutomaticRedirections = 3;
request.Timeout = ; HttpWebResponse res = (HttpWebResponse)request.GetResponse(); string r = ""; System.IO.StreamReader S1 = new System.IO.StreamReader(res.GetResponseStream(),
System.Text.Encoding.Default);
try
{
r = S1.ReadToEnd();
if (CreateCookie)
cookie = res.Cookies;
}
catch (Exception er)
{
//Log l = new Log();
//l.writelog("下载Web错误", er.ToString());
}
finally
{
res.Close();
S1.Close();
} return r; } catch { } return string.Empty; } /**/ /// <TgData>
/// <Alias>下载文件</Alias>
/// </TgData>
public long DownloadFile(string FileURL,
string FileSavePath, bool CreateCookie)
{
long Filelength = ;
HttpWebRequest req =
HttpWebRequest.Create(FileURL) as HttpWebRequest; if (cookie != null) {
req.CookieContainer = new CookieContainer();
req.CookieContainer.Add(cookie);
}
req.AllowAutoRedirect = true; HttpWebResponse res = req.GetResponse() as HttpWebResponse;
if (CreateCookie)
cookie = res.Cookies;
System.IO.Stream stream = res.GetResponseStream();
try
{
Filelength = res.ContentLength; byte[] b = new byte[]; int nReadSize = ; nReadSize = stream.Read(b, , ); System.IO.FileStream fs = System.IO.File.Create(FileSavePath);
try
{
while (nReadSize > )
{
fs.Write(b, , nReadSize);
nReadSize = stream.Read(b, , );
}
}
finally
{
fs.Close();
}
}
catch (Exception er)
{
//Log l = new Log();
//l.writelog("下载文件错误", er.ToString());
}
finally
{
res.Close();
stream.Close();
} return Filelength; } /**/ /// <TgData>
/// <Alias>提交数据</Alias>
/// </TgData>
public string Request(string RequestPageURL,
RequestData Data, bool CreateCookie)
{
StreamReader reader = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
try
{
string StrUrl = RequestPageURL;
request = HttpWebRequest.Create(StrUrl) as HttpWebRequest; string postdata = Data.GetData(); request.Referer = RequestPageURL;
request.AllowAutoRedirect = false;
request.UserAgent =
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2;"+
"SV1; Maxthon; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
request.Timeout = ; if (cookie != null) {
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookie);
} Uri u = new Uri(StrUrl); if (postdata.Length > ) //包含要提交的数据 就使用Post方式 {
//作为表单请求
request.ContentType =
"application/x-www-form-urlencoded";
request.Method = "POST"; //方式就是Post //把提交的数据换成字节数组 Byte[] B = System.Text.Encoding.UTF8.GetBytes(postdata);
request.ContentLength = B.Length; //开始提交数据 System.IO.Stream SW = request.GetRequestStream();
SW.Write(B, , B.Length);
SW.Close();
} response = request.GetResponse() as HttpWebResponse; if (CreateCookie)
//cookie = response.Cookies;
AddCookieWithCookieHead(ref cookie,
response.Headers["Set-Cookie"],
request.RequestUri.Host);
reader = new StreamReader(response.GetResponseStream(),
Encoding.Default); return reader.ReadToEnd(); }
catch (Exception ex)
{
string x = ex.StackTrace;
}
finally
{
if (response != null)
response.Close();
} return string.Empty; } public bool PostDownload(RequestData Data, out string file) {
file = null;
StreamReader reader = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
try
{
string StrUrl =
"http://www.imobile.com.cn/wapdiyringdownload.php";
request = HttpWebRequest.Create(StrUrl) as HttpWebRequest; string postdata = Data.GetData(); request.Referer = StrUrl;
request.AllowAutoRedirect = false;
request.UserAgent =
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2;"+
"SV1; Maxthon; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
request.Timeout = ; if (cookie != null) {
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookie);
} Uri u = new Uri(StrUrl); if (postdata.Length > ) //包含要提交的数据 就使用Post方式 {
request.ContentType =
"application/x-www-form-urlencoded"; //作为表单请求
request.Method = "POST"; //方式就是Post //把提交的数据换成字节数组 Byte[] B = System.Text.Encoding.UTF8.GetBytes(postdata);
request.ContentLength = B.Length; System.IO.Stream SW = request.GetRequestStream(); //开始提交数据
SW.Write(B, , B.Length);
SW.Close();
} response = request.GetResponse() as HttpWebResponse; string des =
response.Headers["Content-Disposition"].Trim();
file = des.Substring(des.IndexOf("filename=") + );
file = new Random().Next().ToString() + "/" + file; System.IO.Stream stream = response.GetResponseStream(); try
{
int Filelength = (int)response.ContentLength; byte[] b = new byte[]; int nReadSize = ; nReadSize = stream.Read(b, , ); System.IO.FileStream fs = System.IO.File.Create("f:/mobileMusic/" + file);
try
{
while (nReadSize > )
{
fs.Write(b, , nReadSize);
nReadSize = stream.Read(b, , );
}
}
finally
{
fs.Close();
}
}
catch (Exception er)
{
//Log l = new Log();
//l.writelog("下载文件错误", er.ToString());
}
finally
{
response.Close();
stream.Close();
}
}
catch (Exception ex)
{
string x = ex.StackTrace;
}
finally
{
if (response != null)
response.Close();
}
return true;
}
#region GetPage
/// <summary>
/// 获取源代码
/// </summary>
/// <param name="url"></param>
/// <param name="coding"></param>
/// <param name="TryCount"></param>
/// <returns></returns>
public static string GetPage(string url,
Encoding encoding, int TryCount)
{
for (int i = ; i < TryCount; i++)
{
string result = GetPage(url, encoding, null);
if (result != null && result != string.Empty)
return result;
} return string.Empty; } /// <summary> /// 获取源代码
/// </summary>
/// <param name="url"></param>
/// <param name="coding"></param>
/// <returns></returns>
public static string GetPage(string url, Encoding encoding,
string cookie)
{
HttpWebRequest request = null;
HttpWebResponse response = null;
StreamReader reader = null;
try
{
request = (HttpWebRequest)WebRequest.Create(url);
request.UserAgent =
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2;)";
request.Timeout = ;
request.AllowAutoRedirect = false;
if (cookie != null)
request.Headers["Cookie"] = cookie; response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode == HttpStatusCode.OK &&
response.ContentLength < * )
{
reader =
new StreamReader(response.GetResponseStream(), encoding);
string html = reader.ReadToEnd(); return html; }
}
catch
{
}
finally
{ if (response != null) {
response.Close();
response = null;
}
if (reader != null)
reader.Close(); if (request != null) request = null; } return string.Empty; }
#endregion
} public class RequestData {
Hashtable hash = new Hashtable(); public RequestData() { } public string GetData() {
string r = ""; foreach (string key in hash.Keys) {
if (r.Length > ) r += "&";
r += key + "=" + hash[key];
} return r; } public void AddField(string Field, string Value) {
hash[Field] = Value;
} } }
C# 过滤HTML标签的几种方法 精华整理哦
/**/ /// <summary>
/// 去除HTML标记
/// </summary>
/// <param name="NoHTML">包括HTML的源码 </param>
/// <returns>已经去除后的文字</returns>
public static string NoHTML(string Htmlstring)
{
//删除脚本
Htmlstring = Regex.Replace(Htmlstring, @"<script[^>]*?>.*?</script>", "",
RegexOptions.IgnoreCase);
//删除HTML
Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+", "",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"<!--.*", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "\"",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", "<",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);", " ",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "\xa1",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "\xa2",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "\xa3",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "\xa9",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&#(\d+);", "",
RegexOptions.IgnoreCase); Htmlstring.Replace("<", "");
Htmlstring.Replace(">", "");
Htmlstring.Replace("\r\n", "");
Htmlstring = HttpContext.Current.Server.HtmlEncode(Htmlstring).Trim();
return Htmlstring;
}
/**/ ///提取HTML代码中文字的C#函数
/// <summary>
/// 去除HTML标记
/// </summary>
/// <param name="strHtml">包括HTML的源码 </param>
/// <returns>已经去除后的文字</returns>
using System;
using System.Text.RegularExpressions;
public class StripHTMLTest
{
public static void Main()
{
string s = StripHTML(
"<HTML><HEAD><TITLE>中国石龙信息平台</TITLE>< /HEAD><BODY>faddfs龙信息平台</BODY></HTML>");
Console.WriteLine(s);
}
public static string StripHTML(string strHtml)
{
string[]aryReg =
{
@"<script[^>]*?>.*?</script>",
@"<(\/\s*)?!?((\w+:)?\w+)(\w+(\s*=?\s*(([""'])(\\["
"'tbnr]|[^\7])*?\7|\w+)|.{0})|\s)*?(\/\s*)?>", @"([\r\n])[\s]+", @
"&(quot|#34);", @"&(amp|#38);", @"&(lt|#60);", @"&(gt|#62);", @
"&(nbsp|#160);", @"&(iexcl|#161);", @"&(cent|#162);", @"&(pound|#163);",
@"&(copy|#169);", @"&#(\d+);", @"-->", @"<!--.*\n"
};
string[]aryRep =
{
"", "", "", "\"", "&", "<", ">", " ", "\xa1", //chr(161),
"\xa2", //chr(162),
"\xa3", //chr(163),
"\xa9", //chr(169),
"", "\r\n", ""
};
string newReg = aryReg[];
string strOutput = strHtml;
for (int i = ; i < aryReg.Length; i++)
{
Regex regex = new Regex(aryReg[i], RegexOptions.IgnoreCase);
strOutput = regex.Replace(strOutput, aryRep[i]);
}
strOutput.Replace("<", "");
strOutput.Replace(">", "");
strOutput.Replace("\r\n", "");
return strOutput;
}
}
写一个静态方法移除HTML标签
#region
///移除HTML标签
/**/ /// <summary>
/// 移除HTML标签
/// </summary>
/// <param name="HTMLStr">HTMLStr</param>
public static string ParseTags(string HTMLStr)
{
return System.Text.RegularExpressions.Regex.Replace(HTMLStr, "<[^>]*>", "");
}
#endregion /// 取出文本中的图片地址
#region
/// 取出文本中的图片地址
/**/ /// <summary>
/// 取出文本中的图片地址
/// </summary>
/// <param name="HTMLStr">HTMLStr</param>
public static string GetImgUrl(string HTMLStr)
{
string str = string.Empty;
string sPattern = @"^<img\s+[^>]*>";
Regex r = new Regex(@"<img\s+[^>]*\s*src\s*=\s*([']?)(?<url>\S+)'?[^>]*>",
RegexOptions.Compiled);
Match m = r.Match(HTMLStr.ToLower());
if (m.Success)
str = m.Result("${url}");
return str;
}
#endregion
C# 获取IIS站点及虚拟目录信息
参考信息:http://msdn.microsoft.com/en-us/library/ms524578.aspx
using System;
using System.DirectoryServices;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DirectoryEntry rootEntry = new DirectoryEntry("IIS://localhost/w3svc");
int siteID = ; foreach (DirectoryEntry entry in rootEntry.Children)
{
if (entry.SchemaClassName.Equals("IIsWebServer", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Name: {0}", entry.Name);
Console.WriteLine("Path: {0}", IISWorker.GetWebsitePhysicalPath(entry));
Console.WriteLine("ServerBindings: {0}", entry.Properties["ServerBindings"].Value);
Console.WriteLine();
DirectoryEntry virEntry = new DirectoryEntry(entry.Path + "/ROOT");
foreach (DirectoryEntry entryVirtual in virEntry.Children)
{
if (entryVirtual.SchemaClassName.Equals("IIsWebVirtualDir", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("SchemaClassName: {0}", entryVirtual.SchemaClassName);
Console.WriteLine("Name: {0}", entryVirtual.Name);
Console.WriteLine("Path: {0}", entryVirtual.Properties["Path"].Value);
Console.WriteLine();
}
}
int ID = Convert.ToInt32(entry.Name);
if (ID >= siteID)
{
siteID = ID + ;
}
}
}
}
}
public class IISWorker
{
/// <summary>
/// 得到网站的物理路径
/// </summary>
/// <param name="rootEntry">网站节点</param>
/// <returns></returns>
public static string GetWebsitePhysicalPath(DirectoryEntry rootEntry)
{
string physicalPath = "";
foreach (DirectoryEntry childEntry in rootEntry.Children)
{
if ((childEntry.SchemaClassName == "IIsWebVirtualDir") && (childEntry.Name.ToLower() == "root"))
{
if (childEntry.Properties["Path"].Value != null)
{
physicalPath = childEntry.Properties["Path"].Value.ToString();
}
else
{
physicalPath = "";
}
}
}
return physicalPath;
}
}
}
C# 获取网页中匹配代码的正则 (获得字符串中开始和结束字符串中间得值)
<div>1div</div>
<a>1a</a>
<p>1p</p>
<p>2p</p>
<div>2div</div>
<a>2a</a>
<p>3p</p>
<p>4p</p>
<a>3a</a>
<p>5p</p>
<div>3div</div>
<a>4a</a>
<p>6p</p>
<span>1span</span> 现在的问题是:有N多DIV,N多p,N多A标签以及最多1个span,想只获取所有p里的内容以及最后一个span里的内容(其中获取P的内容有一个条件,那就是只有前面有一个A标签的P的内容才会被获取),span或许有或许没有,如果有就获取,如果没有就不获取求:
C#的正则表达式
using System.Text.RegularExpressions; 代码
string restult = "";
foreach(Match m in Regex.Matches(str ,@"(?ins)(?<=(</a>\s*<(?<mark>p[^>]*>)|<(?<mark>span)[^>]*>))[\s\S]+?(?=</\k<mark>)"))
{
restult +=m.Value;//就是你要的结果 MessageBox.Show(m.Value);
} 或是用
foreach(Match m in Regex.Matches(yourHtml,@"(?is)(</a>\s*<(?<mark>p[^>]*>)|<(?<mark>span)[^>]*>)(?<data>[\s\S]+?)</\k<mark>"))
{
m.Groups["data"].Value;//
} 或是>>>>>>获得字符串中开始和结束字符串中间得值 代码
#region 获得字符串中开始和结束字符串中间得值
/// <summary>
/// 获得字符串中开始和结束字符串中间得值
/// </summary>
/// <param name="begin">开始匹配标记</param>
/// <param name="end">结束匹配标记</param>
/// <param name="html">Html字符串</param>
/// <returns>返回中间字符串</returns>
public static MatchCollection GetMidValue(string begin, string end, string html)
{
Regex reg = new Regex("(?<=(" + begin + "))[.\\s\\S]*?(?=(" + end + "))", RegexOptions.Multiline | RegexOptions.Singleline);
return reg.Matches(html);
}
#endregion 代码
/// <summary>
/// 获得字符串中开始和结束字符串中间得值
/// </summary>
/// <param name="str"></param>
/// <param name="s">开始</param>
/// <param name="e">结束</param>
/// <returns></returns>
private string getvalue(string str, string start, string end)
{
Regex rg = new Regex("(?<=(" + start + "))[.\\s\\S]*?(?=(" + end + "))", RegexOptions.Multiline | RegexOptions.Singleline); return rg.Match(str).Value;
} //正则抽取单个Table , 可根据table内的某个标识字符, good !
如果仅仅是以“会员资料”这样的做为参考标识,用我上面写的稍稍改造就可以了,问题的复杂在于,如果以“”或者“”做为参考标识,就要考虑到<table>标签嵌套的问题,既要保证取包含参考标识的最内层<table>,又要保证<table>和</table>配对匹配 代码
Match mm = Regex.Match(html, @"<table[^>]*>(((<table[^>]*>(?<o>)|</table>(?<-o>)|(?!</?table)[\s\S])*)(?(o)(?!)))\b" + "会员资料" + @"\b(?:(?!<table[^>]*>)[\s\S])*?(((<table[^>]*>(?<o>)|</table>(?<-o>)|(?!</?table)[\s\S])*)(?(o)(?!)))</table>", RegexOptions.IgnoreCase); 输入的参考标识中如果有正则中有特殊意义的字符,需要对其进行预处理,另外需要在程序中进行异常处理,这个自己处理下吧
如果源字符串中同时多处出现输入的参考标识,这里取第一个出现的参考标识所在的<table> //正则抽取单个Table中 , 解析tb中的内容......... 代码
Match mm = Regex.Match(html, @"<table[^>]*>(((<table[^>]*>(?<o>)|</table>(?<-o>)|(?!</?table)[\s\S])*)(?(o)(?!)))\b" + "会员输赢资料" + @"\b(?:(?!<table[^>]*>)[\s\S])*?(((<table[^>]*>(?<o>)|</table>(?<-o>)|(?!</?table)[\s\S])*)(?(o)(?!)))</table>", RegexOptions.IgnoreCase);
if (mm.Success)
{
//MessageBox.Show(mm.Value); //MatchCollection mdd = GetMidValue("<td", "</td>", mm.Value);
//foreach (Match m in mdd)
//{
// for (int i = 1; i < m.Groups.Count; i++)
// {
// restult += m.Groups[i].Value;//就是你要的结果
// }
//} MatchCollection mc = Regex.Matches(mm.Value, @"<td[^>]*>\s*(?<content>[\s\S]*?)\s*</td>", RegexOptions.IgnoreCase);
foreach(Match m in mc)
{
for (int i = ; i < m.Groups.Count; i++)
{
restult += m.Groups[i].Value + "\n";
}
}
MessageBox.Show(restult);
}
C# 生成Google Sitemap
Google Sitemaps是Google的一个和网站管理员相关的工具,有点像BLOG的RSS功能,是一个方便自己的服务,如果大家(互联网上所有的信息源)都采用了这种方式提交自己的更新的话,Google就再也不用派出那么多爬虫辛辛苦苦的到处乱窜了,任何一个站点,只要有更新,便会自动“通知”Google,方便Google进行索引。
看看Google 自己的介绍。
一般来说,有两种类型的 sitemap。 第一种类型的 sitemap 是通常按部分列出您网站网页的 HTML 网页,用于帮助用户查找所需的信息。
XML Sitemap - 通常称为 Sitemap(使用大写 S),是您向 Google 提供有关您网站信息的一种方法。 这正是我们要在本文中讨论的 Sitemap 类型。
用最简单的话来说,Sitemap 就是您网站上网页的列表。 创建并提交 Sitemap 有助于确保 Google 知道您网站上的所有网页,包括 Google 的正常抓取过程可能无法找到的网址。
如果网站属于下列情况,那么 Sitemap 会特别实用: 网站含动态内容。 网站有不容易被 Googlebot 在抓取过程中发现的页面,如有大量富 AJAX 或 Flash 内容的页面。 网站为新网站且指向网站的链接不多。 (Googlebot 会跟随链接从一个网页到另一个网页抓取网络,因此,如果您的网站没有很好地链接,我们可能很难发现它。) 网站有大量内容页存档,这些内容页相互没有很好地链接,或根本就没有链接。
您还可以使用 Sitemap 向 Google 提供有关您网页的其他信息,包括: 您网站上网页的更改频率。 例如:您可能每日都更新产品页,但每几个月才更新"我的简介"页一次。 各网页上次修改的日期。 您网站上各网页的相对重要性。 例如:主页的相对重要性为 1.0,类别页的相对重要性为 0.8,而个人博客条目或产品页的相对重要性则为 0.5。这个优先级只是说明特定网址相对于您网站上其他网址的重要性,并不会影响您的网页在搜索结果中的排名。
Sitemap 会向 Google 提供有关您网站的其他信息,从而使我们抓取网络的常规方法得到补充。 我们希望 Sitemap 可帮助我们更及时地抓取您网站的更多内容,但我们不能保证您 Sitemap 中的网址会添加到 Google 索引中。 网站不会因提交 Sitemap 而受到处罚。
当你的Sitemap 放在一个文件太大时(1000个链接 以上或者文件超过10M),就需要把它分成多个Sitemap 文件,并建立 SitemapIndex 文件来提交。
Sitemap 索引文件
Sitemap 索引文件的 XML 格式与 Sitemap 文件的 XML 格式非常相似。 Sitemap 索引文件使用以下 XML 标记: <sitemapindex> - 文件头尾的父标记。 <sitemap> - 文件中列出的每个 Sitemap 的父标记(<sitemapindex> 的子标记) <loc> - Sitemap 的位置(<sitemap> 的子标记) <lastmod> - Sitemap 的上次修改日期(可选)
我提供了2个静态函数CreateSitemap 和 CreateSitemapIndex 来生成 Sitemap 文件和SitemapIndex 文件,虽然很简单,当想用的时候可以不用自己写了J。你可以修改一下,写一个适合生成自己网站Sitemap的小工具,用起来就方便了。
看看分别生成的Sitemap 文件和SitemapIndex 文件的截图: 声明:
本文版权归作者和CS 程序员之窗所有,欢迎转载,转载必须保留以下版权信息,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
作者:Starts_2000
出处:http://www.csharpwin.com CS 程序员之窗。
你可以免费使用或修改提供的源代码,但请保留源代码中的版权信息,详情请查看:
CS程序员之窗开源协议(http://www.csharpwin.com/csol.html)。
C# 蜘蛛
"蜘蛛"(Spider)是Internet上一种很有用的程序,搜索引擎利用蜘蛛程序将Web页面收集到数据库,企业利用蜘蛛程序监视竞争对手的网站并跟踪变动,个人用户用蜘蛛程序下载Web页面以便脱机使用,开发者利用蜘蛛程序扫描自己的Web检查无效的链接……对于不同的用户,蜘蛛程序有不同的用途。那么,蜘蛛程序到底是怎样工作的呢? 蜘蛛是一种半自动的程序,就象现实当中的蜘蛛在它的Web(蜘蛛网)上旅行一样,蜘蛛程序也按照类似的方式在Web链接织成的网上旅行。蜘蛛程序之所以是半自动的,是因为它总是需要一个初始链接(出发点),但此后的运行情况就要由它自己决定了,蜘蛛程序会扫描起始页面包含的链接,然后访问这些链接指向的页面,再分析和追踪那些页面包含的链接。从理论上看,最终蜘蛛程序会访问到Internet上的每一个页面,因为Internet上几乎每一个页面总是被其他或多或少的页面引用。 本文介绍如何用C#语言构造一个蜘蛛程序,它能够把整个网站的内容下载到某个指定的目录,程序的运行界面如图一。你可以方便地利用本文提供的几个核心类构造出自己的蜘蛛程序。 图1 C#特别适合于构造蜘蛛程序,这是因为它已经内置了HTTP访问和多线程的能力,而这两种能力对于蜘蛛程序来说都是非常关键的。下面是构造一个蜘蛛程序要解决的关键问题: ⑴ HTML分析:需要某种HTML解析器来分析蜘蛛程序遇到的每一个页面。 ⑵ 页面处理:需要处理每一个下载得到的页面。下载得到的内容可能要保存到磁盘,或者进一步分析处理。 ⑶ 多线程:只有拥有多线程能力,蜘蛛程序才能真正做到高效。 ⑷ 确定何时完成:不要小看这个问题,确定任务是否已经完成并不简单,尤其是在多线程环境下。 一、HTML解析 C#语言本身不包含解析HTML的能力,但支持XML解析;不过,XML有着严格的语法,为XML设计的解析器对HTML来说根本没用,因为HTML的语法要宽松得多。为此,我们需要自己设计一个HTML解析器。本文提供的解析器是高度独立的,你可以方便地将它用于其它用C#处理HTML的场合。 本文提供的HTML解析器由ParseHTML类实现,使用非常方便:首先创建该类的一个实例,然后将它的Source属性设置为要解析的HTML文档: ParseHTML parse = new ParseHTML();
parse.Source = "<p>Hello World</p>";
接下来就可以利用循环来检查HTML文档包含的所有文本和标记。通常,检查过程可以从一个测试Eof方法的while循环开始: while(!parse.Eof())
{
char ch = parse.Parse(); Parse方法将返回HTML文档包含的字符--它返回的内容只包含那些非HTML标记的字符,如果遇到了HTML标记,Parse方法将返回0值,表示现在遇到了一个HTML标记。遇到一个标记之后,我们可以用GetTag()方法来处理它。 if(ch==)
{
HTMLTag tag = parse.GetTag();
} 一般地,蜘蛛程序最重要的任务之一就是找出各个HREF属性,这可以借助C#的索引功能完成。例如,下面的代码将提取出HREF属性的值(如果存在的话)。 Attribute href = tag["HREF"];
string link = href.Value; 获得Attribute对象之后,通过Attribute.Value可以得到该属性的值。 二、处理HTML页面 下面来看看如何处理HTML页面。首先要做的当然是下载HTML页面,这可以通过C#提供的HttpWebRequest类实现: HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_uri);
response = request.GetResponse();
stream = response.GetResponseStream(); 接下来我们就从request创建一个stream流。在执行其他处理之前,我们要先确定该文件是二进制文件还是文本文件,不同的文件类型处理方式也不同。下面的代码确定该文件是否为二进制文件。 if( !response.ContentType.ToLower().StartsWith("text/") )
{
SaveBinaryFile(response);
return null;
}
string buffer = "",line; 如果该文件不是文本文件,我们将它作为二进制文件读入。如果是文本文件,首先从stream创建一个StreamReader,然后将文本文件的内容一行一行加入缓冲区。 reader = new StreamReader(stream);
while( (line = reader.ReadLine())!=null )
{
buffer+=line+"\r\n";
} 装入整个文件之后,接着就要把它保存为文本文件。 SaveTextFile(buffer); 下面来看看这两类不同文件的存储方式。 二进制文件的内容类型声明不以"text/"开头,蜘蛛程序直接把二进制文件保存到磁盘,不必进行额外的处理,这是因为二进制文件不包含HTML,因此也不会再有需要蜘蛛程序处理的HTML链接。下面是写入二进制文件的步骤。 首先准备一个缓冲区临时地保存二进制文件的内容。 byte []buffer = new byte[]; 接下来要确定文件保存到本地的路径和名称。如果要把一个myhost.com网站的内容下载到本地的c:\test文件夹,二进制文件的网上路径和名称是http://myhost.com/images/logo.gif,则本地路径和名称应当是c:\test\images\logo.gif。与此同时,我们还要确保c:\test目录下已经创建了images子目录。这部分任务由convertFilename方法完成。 string filename = convertFilename( response.ResponseUri ); convertFilename方法分离HTTP地址,创建相应的目录结构。确定了输出文件的名字和路径之后就可以打开读取Web页面的输入流、写入本地文件的输出流。 Stream outStream = File.Create( filename );
Stream inStream = response.GetResponseStream(); 接下来就可以读取Web文件的内容并写入到本地文件,这可以通过一个循环方便地完成。 int l;
do
{
l = inStream.Read(buffer,,
buffer.Length);
if(l>)
outStream.Write(buffer,,l);
} while(l>); 写入整个文件之后,关闭输入流、输出流。 outStream.Close();
inStream.Close(); 比较而言,下载文本文件更容易一些。文本文件的内容类型总是以"text/"开头。假设文件已被下载并保存到了一个字符串,这个字符串可以用来分析网页包含的链接,当然也可以保存为磁盘上的文件。下面代码的任务就是保存文本文件。 string filename = convertFilename( m_uri );
StreamWriter outStream = new StreamWriter( filename );
outStream.Write(buffer);
outStream.Close(); 在这里,我们首先打开一个文件输出流,然后将缓冲区的内容写入流,最后关闭文件。
三、多线程 多线程使得计算机看起来就象能够同时执行一个以上的操作,不过,除非计算机包含多个处理器,否则,所谓的同时执行多个操作仅仅是一种模拟出来的效果--靠计算机在多个线程之间快速切换达到"同时"执行多个操作的效果。一般而言,只有在两种情况下多线程才能事实上提高程序运行的速度。第一种情况是计算机拥有多个处理器,第二种情况是程序经常要等待某个外部事件。 对于蜘蛛程序来说,第二种情况正是它的典型特征之一,它每发出一个URL请求,总是要等待文件下载完毕,然后再请求下一个URL。如果蜘蛛程序能够同时请求多个URL,显然能够有效地减少总下载时间。 为此,我们用DocumentWorker类封装所有下载一个URL的操作。每当一个DocumentWorker的实例被创建,它就进入循环,等待下一个要处理的URL。下面是DocumentWorker的主循环: while(!m_spider.Quit )
{
m_uri = m_spider.ObtainWork(); m_spider.SpiderDone.WorkerBegin();
string page = GetPage();
if(page!=null)
ProcessPage(page);
m_spider.SpiderDone.WorkerEnd();
} 这个循环将一直运行,直至Quit标记被设置成了true(当用户点击"Cancel"按钮时,Quit标记就被设置成true)。在循环之内,我们调用ObtainWork获取一个URL。ObtainWork将一直等待,直到有一个URL可用--这要由其他线程解析文档并寻找链接才能获得。Done类利用WorkerBegin和WorkerEnd方法来确定何时整个下载操作已经完成。 从图一可以看出,蜘蛛程序允许用户自己确定要使用的线程数量。在实践中,线程的最佳数量受许多因素影响。如果你的机器性能较高,或者有两个处理器,可以设置较多的线程数量;反之,如果网络带宽、机器性能有限,设置太多的线程数量其实不一定能够提高性能。 四、任务完成了吗? 利用多个线程同时下载文件有效地提高了性能,但也带来了线程管理方面的问题。其中最复杂的一个问题是:蜘蛛程序何时才算完成了工作?在这里我们要借助一个专用的类Done来判断。 首先有必要说明一下"完成工作"的具体含义。只有当系统中不存在等待下载的URL,而且所有工作线程都已经结束其处理工作时,蜘蛛程序的工作才算完成。也就是说,完成工作意味着已经没有等待下载和正在下载的URL。 Done类提供了一个WaitDone方法,它的功能是一直等待,直到Done对象检测到蜘蛛程序已完成工作。下面是WaitDone方法的代码。 public void WaitDone()
{
Monitor.Enter(this);
while ( m_activeThreads> )
{
Monitor.Wait(this);
}
Monitor.Exit(this);
} WaitDone方法将一直等待,直到不再有活动的线程。但必须注意的是,下载开始的最初阶段也没有任何活动的线程,所以很容易造成蜘蛛程序一开始就立即停止的现象。为解决这个问题,我们还需要另一个方法WaitBegin来等待蜘蛛程序进入"正式的"工作阶段。一般的调用次序是:先调用WaitBegin,再接着调用WaitDone,WaitDone将等待蜘蛛程序完成工作。下面是WaitBegin的代码: public void WaitBegin()
{
Monitor.Enter(this);
while ( !m_started )
{
Monitor.Wait(this);
}
Monitor.Exit(this);
} WaitBegin方法将一直等待,直到m_started标记被设置。m_started标记是由WorkerBegin方法设置的。工作线程在开始处理各个URL之时,会调用WorkerBegin;处理结束时调用WorkerEnd。WorkerBegin和WorkerEnd这两个方法帮助Done对象确定当前的工作状态。下面是WorkerBegin方法的代码: public void WorkerBegin()
{
Monitor.Enter(this);
m_activeThreads++;
m_started = true;
Monitor.Pulse(this);
Monitor.Exit(this);
} WorkerBegin方法首先增加当前活动线程的数量,接着设置m_started标记,最后调用Pulse方法以通知(可能存在的)等待工作线程启动的线程。如前所述,可能等待Done对象的方法是WaitBegin方法。每处理完一个URL,WorkerEnd方法会被调用: public void WorkerEnd()
{
Monitor.Enter(this);
m_activeThreads--;
Monitor.Pulse(this);
Monitor.Exit(this);
} WorkerEnd方法减小m_activeThreads活动线程计数器,调用Pulse释放可能在等待Done对象的线程--如前所述,可能在等待Done对象的方法是WaitDone方法。 结束语:本文介绍了开发Internet蜘蛛程序的基础知识,下面提供的源代码将帮助你进一步深入理解本文的主题。这里提供的代码非常灵活,你可以方便地将它用于自己的程序。
C# 抓取网页里面的所有链接!
这几天偶尔看见了,C#抓取网页的链接。的代码。感觉当时做的很简单。呵呵。也没多考虑什么过程。先把简单的给大家拿出来看看。如果大家有什么意见或者有好的方法可以共同交流。谢谢!一下仅供参考: using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms; using System.Xml;
using System.Net;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions; namespace text
{
public partial class Form1 : Form
{
string strCode;
ArrayList alLinks;
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
MessageBox.Show("请输入网址");
return;
}
string strURL = textBox1.Text.ToString().Trim();
if (strURL.Substring(, ) != @"http://")
{
strURL = @"http://" + strURL;
}
MessageBox.Show("正在获取页面代码,请稍后...");
strCode = GetPageSource(strURL);
MessageBox.Show("正在提取超链接,请稍侯...");
alLinks = GetHyperLinks(strCode);
MessageBox.Show("正在写入文件,请稍侯...");
WriteToXml(strURL, alLinks);
}
// 获取指定网页的HTML代码
public static string GetPageSource(string URL)
{
Uri uri = new Uri(URL);
HttpWebRequest hwReq = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse hwRes = (HttpWebResponse)hwReq.GetResponse();
hwReq.Method = "Get";
hwReq.KeepAlive = false;
StreamReader reader = new StreamReader(hwRes.GetResponseStream(), System.Text.Encoding.GetEncoding("GB2312"));
return reader.ReadToEnd();
}
// 提取HTML代码中的网址
public static ArrayList GetHyperLinks(string htmlCode)
{
ArrayList al = new ArrayList();
string strRegex = @"http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";
Regex r = new Regex(strRegex, RegexOptions.IgnoreCase);
MatchCollection m = r.Matches(htmlCode);
for (int i = ; i <= m.Count - ; i++)
{
bool rep = false;
string strNew = m[i].ToString();
// 过滤重复的URL
foreach (string str in al)
{
if (strNew == str)
{
rep = true;
break;
}
}
if (!rep) al.Add(strNew);
}
al.Sort();
return al;
}
// 把网址写入xml文件
static void WriteToXml(string strURL, ArrayList alHyperLinks)
{
XmlTextWriter writer = new XmlTextWriter("HyperLinks.xml", Encoding.UTF8);
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument(false);
writer.WriteDocType("HyperLinks", null, "urls.dtd", null);
writer.WriteComment("提取自" + strURL + "的超链接");
writer.WriteStartElement("HyperLinks");
writer.WriteStartElement("HyperLinks", null);
writer.WriteAttributeString("DateTime", DateTime.Now.ToString()); foreach (string str in alHyperLinks)
{
string title = GetDomain(str);
string body = str;
writer.WriteElementString(title, null, body);
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.Flush();
writer.Close();
}
// 获取网址的域名后缀
static string GetDomain(string strURL)
{
string retVal;
string strRegex = @"(\.com/|\.net/|\.cn/|\.org/|\.gov/)";
Regex r = new Regex(strRegex, RegexOptions.IgnoreCase);
Match m = r.Match(strURL);
retVal = m.ToString();
strRegex = @"\.|/$";
retVal = Regex.Replace(retVal, strRegex, "").ToString();
if (retVal == "")
retVal = "other";
return retVal;
}
}
}
C# 抓取网页上的文字,保存到数据库
<
string content = string.Empty;
string str = string.Format(http://www.qingxi360/info/168.htm);
WebRequest wrq = WebRequest.Create(str); WebResponse wrs = wrq.GetResponse();
using (StreamReader dr = new StreamReader(wrs.GetResponseStream(), Encoding.GetEncoding("gb2312")))
{
content = dr.ReadToEnd();//这就是全部内容 int start = content.IndexOf("相关搜索");
int end = content.IndexOf("");
string StrOldHtml = content.Substring(start, end - start);
StrOldHtml = StrOldHtml.Replace("相关搜索", "");
StrOldHtml = StrOldHtml.Replace(" ", "");
content.add(content);
OK,处理完毕,只是在线交流,无其他用途
在这里只是简单的演示下,有事可以给我留言。谢谢
C#根据网址获取域名,目录名,文件名函数
#region 获取网址中域名,目录,文件名的函数
public string GetUrlDomainName(string strHtmlPagePath)
{
string p = @"http://[^/.]*/.(?<domain>[^/]*)";
Regex reg = new Regex(p,RegexOptions.IgnoreCase);
Match m = reg.Match(strHtmlPagePath);
return m.Groups["domain"].Value;
}
public string[] GetUrlFolerName(string strHtmlPagePath)
{
//抓取网址字符串中的文件目录 int at = ;
int start = ;
int notei=;
int endi=;
int[] myIntArray =new int[];
string[] ArrayFolderName=null;
string NewFolderName;
while((start < strHtmlPagePath.Length) && (at > -))
{
at = strHtmlPagePath.IndexOf('/', start);
if (at == -) break;
myIntArray[notei]=at;
start = at+;
notei = notei+;
endi=at;
}
ArrayFolderName=new string[notei-];
for(int i=;i<notei;i++)
{
if(myIntArray[i]>)
{
if (myIntArray[i+]>)
{
NewFolderName=strHtmlPagePath.Substring(myIntArray[i]+,myIntArray[i+]-myIntArray[i]-);
ArrayFolderName.SetValue(NewFolderName,i);
}
}
}
return ArrayFolderName;
}
public string GetUrlFileName(string strHtmlPagePath)
{
//抓取网址字符串中的文件名称
int at = ;
int start = ;
int notei=;
int endi=;
int[] myIntArray = new int[];
string NewFileName="";
while((start < strHtmlPagePath.Length) && (at > -))
{
at = strHtmlPagePath.IndexOf('/', start);
if (at == -) break;
myIntArray[notei]=at;
start = at+;
notei = notei+;
endi=at;
}
for(int i=;i<notei;i++)
{
if(myIntArray[i]>)
{
if (myIntArray[i+]==)
{
NewFileName=strHtmlPagePath.Substring(myIntArray[i]+,strHtmlPagePath.Length-myIntArray[i]-); }
}
}
return NewFileName.ToLower(); }
#endregion
C#构造蜘蛛程序介绍-HTML解析
“蜘蛛”(Spider)是一种半自动的程序,就象现实当中的蜘蛛在它的Web(蜘蛛网)上旅行一样,蜘蛛程序也按照类似的方式在Web链接织成的网上旅行。蜘蛛程序之所以是半自动的,是因为它总是需要一个初始链接(出发点),但此后的运行情况就要由它自己决定了,蜘蛛程序会扫描起始页面包含的链接,然后访问这些链接指向的页面,再分析和追踪那些页面包含的链接。从理论上看,最终蜘蛛程序会访问到Internet上的每一个页面,因为Internet上几乎每一个页面总是被其他或多或少的页面引用。
本文介绍如何用C#语言构造一个蜘蛛程序,它能够把整个网站的内容下载到某个指定的目录,程序的运行界面如图一。你可以方便地利用本文提供的几个核心类构造出自己的蜘蛛程序。 图1
C#特别适合于构造蜘蛛程序,这是因为它已经内置了HTTP访问和多线程的能力,而这两种能力对于蜘蛛程序来说都是非常关键的。下面是构造一个蜘蛛程序要解决的关键问题:
⑴ HTML分析:需要某种HTML解析器来分析蜘蛛程序遇到的每一个页面;
⑵ 页面处理:需要处理每一个下载得到的页面。下载得到的内容可能要保存到磁盘,或者进一步分析处理;
⑶ 多线程:只有拥有多线程能力,蜘蛛程序才能真正做到高效;
⑷ 确定何时完成:不要小看这个问题,确定任务是否已经完成并不简单,尤其是在多线程环境下。
一、HTML解析
C#语言本身不包含解析HTML的能力,但支持XML解析;不过,XML有着严格的语法,为XML设计的解析器对HTML来说根本没用,因为HTML的语法要宽松得多。为此,我们需要自己设计一个HTML解析器。本文提供的解析器是高度独立的,你可以方便地将它用于其它用C#处理HTML的场合。
本文提供的HTML解析器由ParseHTML类实现,使用非常方便:首先创建该类的一个实例,然后将它的Source属性设置为要解析的HTML文档:
ParseHTML parse = new ParseHTML();parse.Source = "Hello World";
接下来就可以利用循环来检查HTML文档包含的所有文本和标记。通常,检查过程可以从一个测试Eof方法的while循环开始:
while(!parse.Eof()){char ch = parse.Parse();
Parse方法将返回HTML文档包含的字符--它返回的内容只包含那些非HTML标记的字符,如果遇到了HTML标记,Parse方法将返回0值,表示现在遇到了一个HTML标记。遇到一个标记之后,我们可以用GetTag()方法来处理它。
if(ch==){HTMLTag tag = parse.GetTag();}
一般地,蜘蛛程序最重要的任务之一就是找出各个HREF属性,这可以借助C#的索引功能完成。例如,下面的代码将提取出HREF属性的值(如果存在的话)。
Attribute href = tag["HREF"];string link = href.Value;
获得Attribute对象之后,通过Attribute.Value可以得到该属性的值。
二、处理HTML页面
下面来看看如何处理HTML页面。首先要做的当然是下载HTML页面,这可以通过C#提供的HttpWebRequest类实现:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_uri);response = request.GetResponse();stream = response.GetResponseStream();
接下来我们就从request创建一个stream流。在执行其他处理之前,我们要先确定该文件是二进制文件还是文本文件,不同的文件类型处理方式也不同。下面的代码确定该文件是否为二进制文件。
if( !response.ContentType.ToLower().StartsWith("text/") ){SaveBinaryFile(response);return null;}string buffer = "",line;
如果该文件不是文本文件,我们将它作为二进制文件读入。如果是文本文件,首先从stream创建一个StreamReader,然后将文本文件的内容一行一行加入缓冲区。
reader = new StreamReader(stream);while( (line = reader.ReadLine())!=null ){buffer+=line+"\r\n";}
装入整个文件之后,接着就要把它保存为文本文件。
SaveTextFile(buffer);
下面来看看这两类不同文件的存储方式。
二进制文件的内容类型声明不以"text/"开头,蜘蛛程序直接把二进制文件保存到磁盘,不必进行额外的处理,这是因为二进制文件不包含HTML,因此也不会再有需要蜘蛛程序处理的HTML链接。下面是写入二进制文件的步骤。
首先准备一个缓冲区临时地保存二进制文件的内容。 byte []buffer = new byte[];
接下来要确定文件保存到本地的路径和名称。如果要把一个myhost.com网站的内容下载到本地的c:\test文件夹,二进制文件的网上路径和名称是http://myhost.com/images/logo.gif,则本地路径和名称应当是c:\test\images\logo.gif。与此同时,我们还要确保c:\test目录下已经创建了images子目录。这部分任务由convertFilename方法完成。
string filename = convertFilename( response.ResponseUri );
convertFilename方法分离HTTP地址,创建相应的目录结构。确定了输出文件的名字和路径之后就可以打开读取Web页面的输入流、写入本地文件的输出流。
Stream outStream = File.Create( filename );Stream inStream = response.GetResponseStream();
接下来就可以读取Web文件的内容并写入到本地文件,这可以通过一个循环方便地完成。
int l;do{l = inStream.Read(buffer,,buffer.Length);if(l>)outStream.Write(buffer,,l);} while(l>);
写入整个文件之后,关闭输入流、输出流。
outStream.Close();inStream.Close();
比较而言,下载文本文件更容易一些。文本文件的内容类型总是以"text/"开头。假设文件已被下载并保存到了一个字符串,这个字符串可以用来分析网页包含的链接,当然也可以保存为磁盘上的文件。下面代码的任务就是保存文本文件。
string filename = convertFilename( m_uri );StreamWriter outStream = new StreamWriter( filename );outStream.Write(buffer);outStream.Close();
在这里,我们首先打开一个文件输出流,然后将缓冲区的内容写入流,最后关闭文件。
C#过滤html标签的方法
//过滤内容里的html标签
private string checkStr(string html)
{
System.Text.RegularExpressions.Regex regex1 = new System.Text.RegularExpressions.Regex(@"<script[\s\S]+</script *>", RegexOptions.IgnoreCase);
Regex regex2 = new Regex(@" href *= *[\s\S]*script *:", RegexOptions.IgnoreCase);
Regex regex3 = new Regex(@" no[\s\S]*=", RegexOptions.IgnoreCase);
Regex regex4 = new Regex(@"<iframe[\s\S]+</iframe *>", RegexOptions.IgnoreCase);
Regex regex5 = new Regex(@"<frameset[\s\S]+</frameset *>", RegexOptions.IgnoreCase);
Regex regex6 = new Regex(@"\<img[^\>]+\>", RegexOptions.IgnoreCase); Regex regex7 = new Regex(@"</p>", RegexOptions.IgnoreCase);
Regex regex8 = new Regex(@"<p>", RegexOptions.IgnoreCase);
Regex regex9 = new Regex(@"<[^>]*>", RegexOptions.IgnoreCase);
html = regex1.Replace(html, ""); //过滤<script></script>标记
html = regex2.Replace(html, ""); //过滤href=javascript: (<A>) 属性 网管网bitsCN.com
html = regex3.Replace(html, " _disibledevent="); //过滤其它控件的on...事件
html = regex4.Replace(html, ""); //过滤iframe
html = regex5.Replace(html, ""); //过滤frameset
html = regex6.Replace(html, ""); //过滤frameset
html = regex7.Replace(html, ""); //过滤frameset
html = regex8.Replace(html, ""); //过滤frameset
html = regex9.Replace(html, "");
html = html.Replace(" ", ""); html = html.Replace("</strong>", "");
html = html.Replace("<strong>", "");
return html;
}
C#抓取和分析网页的类
主要功能有:
、提取网页的纯文本,去所有html标签和javascript代码
、提取网页的链接,包括href和frame及iframe
、提取网页的title等(其它的标签可依此类推,正则是一样的)
、可以实现简单的表单提交及cookie保存
/*
* Author:Sunjoy at CCNU
* 如果您改进了这个类请发一份代码给我(ccnusjy 在gmail.com)
*/
using System;
using System.Data;
using System.Configuration;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
/// <summary>
/// 网页类
/// </summary>
public class WebPage
{
#region 私有成员
private Uri m_uri; //网址
private List<Link> m_links; //此网页上的链接
private string m_title; //此网页的标题
private string m_html; //此网页的HTML代码
private string m_outstr; //此网页可输出的纯文本
private bool m_good; //此网页是否可用
private int m_pagesize; //此网页的大小
private static Dictionary<string, CookieContainer> webcookies = new Dictionary<string, CookieContainer>();//存放所有网页的Cookie
private string m_post; //此网页的登陆页需要的POST数据
private string m_loginurl; //此网页的登陆页
#endregion
#region 私有方法
/// <summary>
/// 这私有方法从网页的HTML代码中分析出链接信息
/// </summary>
/// <returns>List<Link></returns>
private List<Link> getLinks()
{
if (m_links.Count == )
{
Regex[] regex = new Regex[];
regex[] = new Regex("(?m)<a[^><]+href=(/"|')?(?<url>([^>/"'//s)])+)(/"|')?[^>]*>(?<text>(//w|//W)*?)</", RegexOptions.Multiline | RegexOptions.IgnoreCase);
regex[] = new Regex("<[i]*frame[^><]+src=(/"|')?(?<url>([^>/"'//s)])+)(/"|')?[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase);
for (int i = ; i < ; i++)
{
Match match = regex[i].Match(m_html);
while (match.Success)
{
try
{
string url = new Uri(m_uri, match.Groups["url"].Value).AbsoluteUri;
string text = "";
if (i == ) text = new Regex("(<[^>]+>)|(//s)|( )|&|/"", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(match.Groups["text"].Value, "");
Link link = new Link(url, text);
m_links.Add(link);
}
catch(Exception ex){Console.WriteLine(ex.Message); };
match = match.NextMatch();
}
}
}
return m_links;
} /// <summary>
/// 此私有方法从一段HTML文本中提取出一定字数的纯文本
/// </summary>
/// <param name="instr">HTML代码</param>
/// <param name="firstN">提取从头数多少个字</param>
/// <param name="withLink">是否要链接里面的字</param>
/// <returns>纯文本</returns>
private string getFirstNchar(string instr, int firstN, bool withLink)
{
if (m_outstr == "")
{
m_outstr = instr.Clone() as string;
m_outstr = new Regex(@"(?m)<script[^>]*>(/w|/W)*?</script[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase ).Replace(m_outstr, "");
m_outstr = new Regex(@"(?m)<style[^>]*>(/w|/W)*?</style[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase ).Replace(m_outstr, "");
m_outstr = new Regex(@"(?m)<select[^>]*>(/w|/W)*?</select[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase ).Replace(m_outstr, "");
if (!withLink) m_outstr = new Regex(@"(?m)<a[^>]*>(/w|/W)*?</a[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(m_outstr, "");
Regex objReg = new System.Text.RegularExpressions.Regex("(<[^>]+?>)| ", RegexOptions.Multiline | RegexOptions.IgnoreCase);
m_outstr = objReg.Replace(m_outstr, "");
Regex objReg2 = new System.Text.RegularExpressions.Regex("(//s)+", RegexOptions.Multiline | RegexOptions.IgnoreCase);
m_outstr = objReg2.Replace(m_outstr, " ");
}
return m_outstr.Length > firstN ? m_outstr.Substring(, firstN) : m_outstr;
}
/// <summary>
/// 此私有方法返回一个IP地址对应的无符号整数
/// </summary>
/// <param name="x">IP地址</param>
/// <returns></returns>
private uint getuintFromIP(IPAddress x)
{
Byte[] bt = x.GetAddressBytes();
uint i = (uint)(bt[] * * * );
i += (uint)(bt[] * * );
i += (uint)(bt[] * );
i += (uint)(bt[]);
return i;
}
#endregion
#region 公有文法
/// <summary>
/// 此公有方法提取网页中一定字数的纯文本,包括链接文字
/// </summary>
/// <param name="firstN">字数</param>
/// <returns></returns>
public string getContext(int firstN)
{
return getFirstNchar(m_html, firstN, true);
}
/// <summary>
/// 此公有方法提取网页中一定字数的纯文本,不包括链接文字
/// </summary>
/// <param name="firstN"></param>
/// <returns></returns>
public string getContextWithOutLink(int firstN)
{
return getFirstNchar(m_html, firstN, false);
}
/// <summary>
/// 此公有方法从本网页的链接中提取一定数量的链接,该链接的URL满足某正则式
/// </summary>
/// <param name="pattern">正则式</param>
/// <param name="count">返回的链接的个数</param>
/// <returns>List<Link></returns>
public List<Link> getSpecialLinksByUrl(string pattern,int count)
{
if(m_links.Count==)getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
int cnt = ;
while (i.MoveNext() && cnt<count)
{
if (new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase ).Match(i.Current.url).Success)
{
SpecialLinks.Add(i.Current);
cnt++;
}
}
return SpecialLinks;
}
/// <summary>
/// 此公有方法从本网页的链接中提取一定数量的链接,该链接的文字满足某正则式
/// </summary>
/// <param name="pattern">正则式</param>
/// <param name="count">返回的链接的个数</param>
/// <returns>List<Link></returns>
public List<Link> getSpecialLinksByText(string pattern,int count)
{
if (m_links.Count == ) getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
int cnt = ;
while (i.MoveNext() && cnt < count)
{
if (new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase ).Match(i.Current.text).Success)
{
SpecialLinks.Add(i.Current);
cnt++;
}
}
return SpecialLinks;
}
/// <summary>
/// 此公有方法获得所有链接中在一定IP范围的链接
/// </summary>
/// <param name="_ip_start">起始IP</param>
/// <param name="_ip_end">终止IP</param>
/// <returns></returns>
public List<Link> getSpecialLinksByIP(string _ip_start, string _ip_end)
{
IPAddress ip_start = IPAddress.Parse(_ip_start);
IPAddress ip_end = IPAddress.Parse(_ip_end);
if (m_links.Count == ) getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
while (i.MoveNext())
{
IPAddress ip;
try
{
ip = Dns.GetHostEntry(new Uri(i.Current.url).Host).AddressList[];
}
catch { continue; }
if(getuintFromIP(ip)>=getuintFromIP(ip_start) && getuintFromIP(ip)<=getuintFromIP(ip_end))
{
SpecialLinks.Add(i.Current);
}
}
return SpecialLinks;
}
/// <summary>
/// 这公有方法提取本网页的纯文本中满足某正则式的文字
/// </summary>
/// <param name="pattern">正则式</param>
/// <returns>返回文字</returns>
public string getSpecialWords(string pattern)
{
if (m_outstr == "") getContext(Int16.MaxValue);
Regex regex = new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase );
Match mc=regex.Match(m_outstr);
if (mc.Success)
return mc.Groups[].Value;
return string.Empty;
}
#endregion
#region 构造函数 private void Init(string _url)
{ try
{
m_uri = new Uri(_url);
m_links = new List<Link>();
m_html = "";
m_outstr = "";
m_title = "";
m_good = true;
if (_url.EndsWith(".rar") || _url.EndsWith(".dat") || _url.EndsWith(".msi"))
{
m_good = false;
return;
}
HttpWebRequest rqst = (HttpWebRequest)WebRequest.Create(m_uri);
rqst.AllowAutoRedirect = true;
rqst.MaximumAutomaticRedirections = ;
rqst.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
rqst.KeepAlive = true;
rqst.Timeout = ;
lock (WebPage.webcookies)
{
if (WebPage.webcookies.ContainsKey(m_uri.Host))
rqst.CookieContainer = WebPage.webcookies[m_uri.Host];
else
{
CookieContainer cc = new CookieContainer();
WebPage.webcookies[m_uri.Host] = cc;
rqst.CookieContainer = cc;
}
}
HttpWebResponse rsps = (HttpWebResponse)rqst.GetResponse();
Stream sm = rsps.GetResponseStream();
if (!rsps.ContentType.ToLower().StartsWith("text/") || rsps.ContentLength > << )
{
rsps.Close();
m_good = false;
return;
}
Encoding cding = System.Text.Encoding.Default;
string contenttype=rsps.ContentType.ToLower();
int ix = contenttype.IndexOf("charset=");
if (ix != -)
{
try
{
cding = System.Text.Encoding.GetEncoding(rsps.ContentType.Substring(ix + "charset".Length + ));
}
catch
{
cding = Encoding.Default;
}
m_html = new StreamReader(sm, cding).ReadToEnd();
}
else
{
m_html = new StreamReader(sm, cding).ReadToEnd();
Regex regex = new Regex("charset=(?<cding>[^=]+)?/"",RegexOptions.IgnoreCase);
string strcding = regex.Match(m_html).Groups["cding"].Value;
try
{
cding = Encoding.GetEncoding(strcding);
}
catch{
cding = Encoding.Default;
}
byte[] bytes=Encoding.Default.GetBytes(m_html.ToCharArray());
m_html = cding.GetString(bytes);
if (m_html.Split('?').Length > )
{
m_html=Encoding.Default.GetString(bytes);
}
} m_pagesize = m_html.Length;
m_uri = rsps.ResponseUri;
rsps.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message+m_uri.ToString());
m_good = false; }
}
public WebPage(string _url)
{
string uurl = "";
try
{
uurl = Uri.UnescapeDataString(_url);
_url = uurl;
}
catch { };
Regex re = new Regex("(?<h>[^/x00-/xff]+)");
Match mc = re.Match(_url);
if (mc.Success)
{
string han = mc.Groups["h"].Value;
_url = _url.Replace(han, System.Web.HttpUtility.UrlEncode(han, Encoding.GetEncoding("GB2312")));
}
Init(_url);
}
public WebPage(string _url, string _loginurl, string _post)
{
string uurl = "";
try
{
uurl = Uri.UnescapeDataString(_url);
_url = uurl;
}
catch { };
Regex re = new Regex("(?<h>[^/x00-/xff]+)");
Match mc = re.Match(_url);
if (mc.Success)
{
string han = mc.Groups["h"].Value;
_url = _url.Replace(han, System.Web.HttpUtility.UrlEncode(han, Encoding.GetEncoding("GB2312")));
}
if (_loginurl.Trim() == "" || _post.Trim() == "" || WebPage.webcookies.ContainsKey(new Uri(_url).Host))
{
Init(_url);
}
else
{
#region 登陆
string indata = _post;
m_post = _post;
m_loginurl = _loginurl;
byte[] bytes = Encoding.Default.GetBytes(_post);
CookieContainer myCookieContainer = new CookieContainer();
try
{
//新建一个CookieContainer来存放Cookie集合
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(_loginurl);
//新建一个HttpWebRequest
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.AllowAutoRedirect = false;
myHttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
myHttpWebRequest.Timeout = ;
myHttpWebRequest.KeepAlive = true;
myHttpWebRequest.ContentLength = bytes.Length;
myHttpWebRequest.Method = "POST";
myHttpWebRequest.CookieContainer = myCookieContainer;
//设置HttpWebRequest的CookieContainer为刚才建立的那个myCookieContainer
Stream myRequestStream = myHttpWebRequest.GetRequestStream();
myRequestStream.Write(bytes, , bytes.Length);
myRequestStream.Close();
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
foreach (Cookie ck in myHttpWebResponse.Cookies)
{
myCookieContainer.Add(ck);
}
myHttpWebResponse.Close();
}
catch
{
Init(_url);
return;
}
#endregion
#region 登陆后再访问页面
try
{
m_uri = new Uri(_url);
m_links = new List<Link>();
m_html = "";
m_outstr = "";
m_title = "";
m_good = true;
if (_url.EndsWith(".rar") || _url.EndsWith(".dat") || _url.EndsWith(".msi"))
{
m_good = false;
return;
}
HttpWebRequest rqst = (HttpWebRequest)WebRequest.Create(m_uri);
rqst.AllowAutoRedirect = true;
rqst.MaximumAutomaticRedirections = ;
rqst.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
rqst.KeepAlive = true;
rqst.Timeout = ;
rqst.CookieContainer = myCookieContainer;
lock (WebPage.webcookies)
{
WebPage.webcookies[m_uri.Host] = myCookieContainer;
}
HttpWebResponse rsps = (HttpWebResponse)rqst.GetResponse();
Stream sm = rsps.GetResponseStream();
if (!rsps.ContentType.ToLower().StartsWith("text/") || rsps.ContentLength > << )
{
rsps.Close();
m_good = false;
return;
}
Encoding cding = System.Text.Encoding.Default;
int ix = rsps.ContentType.ToLower().IndexOf("charset=");
if (ix != -)
{
try
{
cding = System.Text.Encoding.GetEncoding(rsps.ContentType.Substring(ix + "charset".Length + ));
}
catch
{
cding = Encoding.Default;
}
}
m_html = new StreamReader(sm, cding).ReadToEnd();
m_pagesize = m_html.Length;
m_uri = rsps.ResponseUri;
rsps.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message+m_uri.ToString());
m_good = false; }
#endregion
}
}
#endregion
#region 属性
/// <summary>
/// 通过此属性可获得本网页的网址,只读
/// </summary>
public string URL
{
get
{
return m_uri.AbsoluteUri;
}
}
/// <summary>
/// 通过此属性可获得本网页的标题,只读
/// </summary>
public string Title
{
get
{
if (m_title == "")
{
Regex reg = new Regex(@"(?m)<title[^>]*>(?<title>(?:/w|/W)*?)</title[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase );
Match mc = reg.Match(m_html);
if (mc.Success)
m_title= mc.Groups["title"].Value.Trim();
}
return m_title;
}
} /// <summary>
/// 此属性获得本网页的所有链接信息,只读
/// </summary>
public List<Link> Links
{
get
{
if (m_links.Count == ) getLinks();
return m_links;
}
}
/// <summary>
/// 此属性返回本网页的全部纯文本信息,只读
/// </summary>
public string Context
{
get
{
if (m_outstr == "") getContext(Int16.MaxValue);
return m_outstr;
}
}
/// <summary>
/// 此属性获得本网页的大小
/// </summary>
public int PageSize
{
get
{
return m_pagesize;
}
}
/// <summary>
/// 此属性获得本网页的所有站内链接
/// </summary>
public List<Link> InsiteLinks
{
get
{
return getSpecialLinksByUrl("^http://"+m_uri.Host,Int16.MaxValue);
}
}
/// <summary>
/// 此属性表示本网页是否可用
/// </summary>
public bool IsGood
{
get
{
return m_good;
}
}
/// <summary>
/// 此属性表示网页的所在的网站
/// </summary>
public string Host
{
get
{
return m_uri.Host;
}
} /// <summary>
/// 此网页的登陆页所需的POST数据
/// </summary>
public string PostStr
{
get
{
return m_post;
}
}
/// <summary>
/// 此网页的登陆页
/// </summary>
public string LoginURL
{
get
{
return m_loginurl;
}
}
#endregion
}
/// <summary>
/// 链接类
/// </summary>
public class Link
{
public string url; //链接网址
public string text; //链接文字
public Link(string _url, string _text)
{
url = _url;
text = _text;
}
}
C#抓取和分析网页的类(提取、正文、内容、链接)
抓取和分析网页的类。
主要功能有:
、提取网页的纯文本,去所有html标签和javascript代码
、提取网页的链接,包括href和frame及iframe
、提取网页的title等(其它的标签可依此类推,正则是一样的)
、可以实现简单的表单提交及cookie保存
/*
* Author:Sunjoy at CCNU
* 如果您改进了这个类请发一份代码给我(ccnusjy 在gmail.com)
*/ using System;
using System.Data;
using System.Configuration;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
/// <summary>
/// 网页类
/// </summary>
public class WebPage
{ #region 私有成员
private Uri m_uri; //网址
private List<Link> m_links; //此网页上的链接
private string m_title; //此网页的标题
private string m_html; //此网页的HTML代码
private string m_outstr; //此网页可输出的纯文本
private bool m_good; //此网页是否可用
private int m_pagesize; //此网页的大小
private static Dictionary<string, CookieContainer> webcookies = new Dictionary<string, CookieContainer>();//存放所有网页的Cookie
private string m_post; //此网页的登陆页需要的POST数据
private string m_loginurl; //此网页的登陆页
#endregion #region 私有方法
/// <summary>
/// 这私有方法从网页的HTML代码中分析出链接信息
/// </summary>
/// <returns>List<Link></returns>
private List<Link> getLinks()
{
if (m_links.Count == )
{
Regex[] regex = new Regex[];
regex[] = new Regex("(?m)<a[^><]+href=(\"|')?(?<url>([^>\"'\\s)])+)(\"|')?[^>]*>(?<text>(\\w|\\W)*?)</", RegexOptions.Multiline | RegexOptions.IgnoreCase);
regex[] = new Regex("<[i]*frame[^><]+src=(\"|')?(?<url>([^>\"'\\s)])+)(\"|')?[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase);
for (int i = ; i < ; i++)
{
Match match = regex[i].Match(m_html);
while (match.Success)
{
try
{
string url = new Uri(m_uri, match.Groups["url"].Value).AbsoluteUri;
string text = "";
if (i == ) text = new Regex("(<[^>]+>)|(\\s)|( )|&|\"", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(match.Groups["text"].Value, "");
Link link = new Link(url, text);
m_links.Add(link);
}
catch(Exception ex){Console.WriteLine(ex.Message); };
match = match.NextMatch();
}
}
}
return m_links;
} /// <summary>
/// 此私有方法从一段HTML文本中提取出一定字数的纯文本
/// </summary>
/// <param name="instr">HTML代码</param>
/// <param name="firstN">提取从头数多少个字</param>
/// <param name="withLink">是否要链接里面的字</param>
/// <returns>纯文本</returns>
private string getFirstNchar(string instr, int firstN, bool withLink)
{
if (m_outstr == "")
{
m_outstr = instr.Clone() as string;
m_outstr = new Regex(@"(?m)<script[^>]*>(\w|\W)*?</script[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase ).Replace(m_outstr, "");
m_outstr = new Regex(@"(?m)<style[^>]*>(\w|\W)*?</style[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase ).Replace(m_outstr, "");
m_outstr = new Regex(@"(?m)<select[^>]*>(\w|\W)*?</select[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase ).Replace(m_outstr, "");
if (!withLink) m_outstr = new Regex(@"(?m)<a[^>]*>(\w|\W)*?</a[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(m_outstr, "");
Regex objReg = new System.Text.RegularExpressions.Regex("(<[^>]+?>)| ", RegexOptions.Multiline | RegexOptions.IgnoreCase);
m_outstr = objReg.Replace(m_outstr, "");
Regex objReg2 = new System.Text.RegularExpressions.Regex("(\\s)+", RegexOptions.Multiline | RegexOptions.IgnoreCase);
m_outstr = objReg2.Replace(m_outstr, " ");
}
return m_outstr.Length > firstN ? m_outstr.Substring(, firstN) : m_outstr;
} /// <summary>
/// 此私有方法返回一个IP地址对应的无符号整数
/// </summary>
/// <param name="x">IP地址</param>
/// <returns></returns>
private uint getuintFromIP(IPAddress x)
{
Byte[] bt = x.GetAddressBytes();
uint i = (uint)(bt[] * * * );
i += (uint)(bt[] * * );
i += (uint)(bt[] * );
i += (uint)(bt[]);
return i;
} #endregion #region 公有文法
/// <summary>
/// 此公有方法提取网页中一定字数的纯文本,包括链接文字
/// </summary>
/// <param name="firstN">字数</param>
/// <returns></returns>
public string getContext(int firstN)
{
return getFirstNchar(m_html, firstN, true);
} /// <summary>
/// 此公有方法提取网页中一定字数的纯文本,不包括链接文字
/// </summary>
/// <param name="firstN"></param>
/// <returns></returns>
public string getContextWithOutLink(int firstN)
{
return getFirstNchar(m_html, firstN, false);
} /// <summary>
/// 此公有方法从本网页的链接中提取一定数量的链接,该链接的URL满足某正则式
/// </summary>
/// <param name="pattern">正则式</param>
/// <param name="count">返回的链接的个数</param>
/// <returns>List<Link></returns>
public List<Link> getSpecialLinksByUrl(string pattern,int count)
{
if(m_links.Count==)getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
int cnt = ;
while (i.MoveNext() && cnt<count)
{
if (new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase ).Match(i.Current.url).Success)
{
SpecialLinks.Add(i.Current);
cnt++;
}
}
return SpecialLinks;
} /// <summary>
/// 此公有方法从本网页的链接中提取一定数量的链接,该链接的文字满足某正则式
/// </summary>
/// <param name="pattern">正则式</param>
/// <param name="count">返回的链接的个数</param>
/// <returns>List<Link></returns>
public List<Link> getSpecialLinksByText(string pattern,int count)
{
if (m_links.Count == ) getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
int cnt = ;
while (i.MoveNext() && cnt < count)
{
if (new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase ).Match(i.Current.text).Success)
{
SpecialLinks.Add(i.Current);
cnt++;
}
}
return SpecialLinks;
}
/// <summary>
/// 此公有方法获得所有链接中在一定IP范围的链接
/// </summary>
/// <param name="_ip_start">起始IP</param>
/// <param name="_ip_end">终止IP</param>
/// <returns></returns>
public List<Link> getSpecialLinksByIP(string _ip_start, string _ip_end)
{
IPAddress ip_start = IPAddress.Parse(_ip_start);
IPAddress ip_end = IPAddress.Parse(_ip_end);
if (m_links.Count == ) getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
while (i.MoveNext())
{
IPAddress ip;
try
{
ip = Dns.GetHostEntry(new Uri(i.Current.url).Host).AddressList[];
}
catch { continue; }
if(getuintFromIP(ip)>=getuintFromIP(ip_start) && getuintFromIP(ip)<=getuintFromIP(ip_end))
{
SpecialLinks.Add(i.Current);
}
}
return SpecialLinks;
} /// <summary>
/// 这公有方法提取本网页的纯文本中满足某正则式的文字
/// </summary>
/// <param name="pattern">正则式</param>
/// <returns>返回文字</returns>
public string getSpecialWords(string pattern)
{
if (m_outstr == "") getContext(Int16.MaxValue);
Regex regex = new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase );
Match mc=regex.Match(m_outstr);
if (mc.Success)
return mc.Groups[].Value;
return string.Empty;
}
#endregion #region 构造函数 private void Init(string _url)
{ try
{
m_uri = new Uri(_url);
m_links = new List<Link>();
m_html = "";
m_outstr = "";
m_title = "";
m_good = true;
if (_url.EndsWith(".rar") || _url.EndsWith(".dat") || _url.EndsWith(".msi"))
{
m_good = false;
return;
}
HttpWebRequest rqst = (HttpWebRequest)WebRequest.Create(m_uri);
rqst.AllowAutoRedirect = true;
rqst.MaximumAutomaticRedirections = ;
rqst.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
rqst.KeepAlive = true;
rqst.Timeout = ;
lock (WebPage.webcookies)
{
if (WebPage.webcookies.ContainsKey(m_uri.Host))
rqst.CookieContainer = WebPage.webcookies[m_uri.Host];
else
{
CookieContainer cc = new CookieContainer();
WebPage.webcookies[m_uri.Host] = cc;
rqst.CookieContainer = cc;
}
} HttpWebResponse rsps = (HttpWebResponse)rqst.GetResponse(); Stream sm = rsps.GetResponseStream();
if (!rsps.ContentType.ToLower().StartsWith("text/") || rsps.ContentLength > << )
{
rsps.Close();
m_good = false;
return;
}
Encoding cding = System.Text.Encoding.Default;
string contenttype=rsps.ContentType.ToLower();
int ix = contenttype.IndexOf("charset=");
if (ix != -)
{ try
{
cding = System.Text.Encoding.GetEncoding(rsps.ContentType.Substring(ix + "charset".Length + ));
}
catch
{
cding = Encoding.Default;
}
m_html = new StreamReader(sm, cding).ReadToEnd();
}
else
{
m_html = new StreamReader(sm, cding).ReadToEnd();
Regex regex = new Regex("charset=(?<cding>[^=]+)?\"",RegexOptions.IgnoreCase);
string strcding = regex.Match(m_html).Groups["cding"].Value;
try
{
cding = Encoding.GetEncoding(strcding);
}
catch{
cding = Encoding.Default;
}
byte[] bytes=Encoding.Default.GetBytes(m_html.ToCharArray());
m_html = cding.GetString(bytes);
if (m_html.Split('?').Length > )
{
m_html=Encoding.Default.GetString(bytes);
}
} m_pagesize = m_html.Length;
m_uri = rsps.ResponseUri;
rsps.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message+m_uri.ToString());
m_good = false; }
} public WebPage(string _url)
{
string uurl = "";
try
{
uurl = Uri.UnescapeDataString(_url);
_url = uurl;
}
catch { };
Regex re = new Regex("(?<h>[^\x00-\xff]+)");
Match mc = re.Match(_url);
if (mc.Success)
{
string han = mc.Groups["h"].Value;
_url = _url.Replace(han, System.Web.HttpUtility.UrlEncode(han, Encoding.GetEncoding("GB2312")));
} Init(_url);
} public WebPage(string _url, string _loginurl, string _post)
{
string uurl = "";
try
{
uurl = Uri.UnescapeDataString(_url);
_url = uurl;
}
catch { };
Regex re = new Regex("(?<h>[^\x00-\xff]+)");
Match mc = re.Match(_url);
if (mc.Success)
{
string han = mc.Groups["h"].Value;
_url = _url.Replace(han, System.Web.HttpUtility.UrlEncode(han, Encoding.GetEncoding("GB2312")));
}
if (_loginurl.Trim() == "" || _post.Trim() == "" || WebPage.webcookies.ContainsKey(new Uri(_url).Host))
{
Init(_url);
}
else
{
#region 登陆
string indata = _post;
m_post = _post;
m_loginurl = _loginurl;
byte[] bytes = Encoding.Default.GetBytes(_post);
CookieContainer myCookieContainer = new CookieContainer();
try
{ //新建一个CookieContainer来存放Cookie集合 HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(_loginurl);
//新建一个HttpWebRequest
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.AllowAutoRedirect = false;
myHttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
myHttpWebRequest.Timeout = ;
myHttpWebRequest.KeepAlive = true;
myHttpWebRequest.ContentLength = bytes.Length;
myHttpWebRequest.Method = "POST";
myHttpWebRequest.CookieContainer = myCookieContainer;
//设置HttpWebRequest的CookieContainer为刚才建立的那个myCookieContainer
Stream myRequestStream = myHttpWebRequest.GetRequestStream();
myRequestStream.Write(bytes, , bytes.Length);
myRequestStream.Close();
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse(); foreach (Cookie ck in myHttpWebResponse.Cookies)
{
myCookieContainer.Add(ck);
}
myHttpWebResponse.Close();
}
catch
{
Init(_url);
return;
} #endregion #region 登陆后再访问页面
try
{
m_uri = new Uri(_url);
m_links = new List<Link>();
m_html = "";
m_outstr = "";
m_title = "";
m_good = true;
if (_url.EndsWith(".rar") || _url.EndsWith(".dat") || _url.EndsWith(".msi"))
{
m_good = false;
return;
}
HttpWebRequest rqst = (HttpWebRequest)WebRequest.Create(m_uri);
rqst.AllowAutoRedirect = true;
rqst.MaximumAutomaticRedirections = ;
rqst.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
rqst.KeepAlive = true;
rqst.Timeout = ;
rqst.CookieContainer = myCookieContainer;
lock (WebPage.webcookies)
{
WebPage.webcookies[m_uri.Host] = myCookieContainer;
}
HttpWebResponse rsps = (HttpWebResponse)rqst.GetResponse(); Stream sm = rsps.GetResponseStream();
if (!rsps.ContentType.ToLower().StartsWith("text/") || rsps.ContentLength > << )
{
rsps.Close();
m_good = false;
return;
}
Encoding cding = System.Text.Encoding.Default;
int ix = rsps.ContentType.ToLower().IndexOf("charset=");
if (ix != -)
{
try
{
cding = System.Text.Encoding.GetEncoding(rsps.ContentType.Substring(ix + "charset".Length + ));
}
catch
{
cding = Encoding.Default;
}
} m_html = new StreamReader(sm, cding).ReadToEnd(); m_pagesize = m_html.Length;
m_uri = rsps.ResponseUri;
rsps.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message+m_uri.ToString());
m_good = false; }
#endregion
} } #endregion #region 属性 /// <summary>
/// 通过此属性可获得本网页的网址,只读
/// </summary>
public string URL
{
get
{
return m_uri.AbsoluteUri;
}
} /// <summary>
/// 通过此属性可获得本网页的标题,只读
/// </summary>
public string Title
{
get
{
if (m_title == "")
{
Regex reg = new Regex(@"(?m)<title[^>]*>(?<title>(?:\w|\W)*?)</title[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase );
Match mc = reg.Match(m_html);
if (mc.Success)
m_title= mc.Groups["title"].Value.Trim();
}
return m_title;
}
} /// <summary>
/// 此属性获得本网页的所有链接信息,只读
/// </summary>
public List<Link> Links
{
get
{
if (m_links.Count == ) getLinks();
return m_links;
}
} /// <summary>
/// 此属性返回本网页的全部纯文本信息,只读
/// </summary>
public string Context
{
get
{
if (m_outstr == "") getContext(Int16.MaxValue);
return m_outstr;
}
} /// <summary>
/// 此属性获得本网页的大小
/// </summary>
public int PageSize
{
get
{
return m_pagesize;
}
}
/// <summary>
/// 此属性获得本网页的所有站内链接
/// </summary>
public List<Link> InsiteLinks
{
get
{
return getSpecialLinksByUrl("^http://"+m_uri.Host,Int16.MaxValue);
}
} /// <summary>
/// 此属性表示本网页是否可用
/// </summary>
public bool IsGood
{
get
{
return m_good;
}
}
/// <summary>
/// 此属性表示网页的所在的网站
/// </summary>
public string Host
{
get
{
return m_uri.Host;
}
} /// <summary>
/// 此网页的登陆页所需的POST数据
/// </summary>
public string PostStr
{
get
{
return m_post;
}
}
/// <summary>
/// 此网页的登陆页
/// </summary>
public string LoginURL
{
get
{
return m_loginurl;
}
}
#endregion
} /// <summary>
/// 链接类
/// </summary>
public class Link
{
public string url; //链接网址
public string text; //链接文字
public Link(string _url, string _text)
{
url = _url;
text = _text;
}
}
C#抓取网页上的所有连接
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms; using System.Xml;
using System.Net;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions; namespace text
{
public partial class Form1 : Form
{ string strCode;
ArrayList alLinks; public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
MessageBox.Show("请输入网址");
return;
}
string strURL = textBox1.Text.ToString().Trim();
if (strURL.Substring(, ) != @"http://")
{
strURL = @"http://" + strURL;
}
MessageBox.Show("正在获取页面代码,请稍后...");
strCode = GetPageSource(strURL);
MessageBox.Show("正在提取超链接,请稍侯...");
alLinks = GetHyperLinks(strCode);
MessageBox.Show("正在写入文件,请稍侯...");
WriteToXml(strURL, alLinks);
} // 获取指定网页的HTML代码
public static string GetPageSource(string URL)
{
Uri uri = new Uri(URL); HttpWebRequest hwReq = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse hwRes = (HttpWebResponse)hwReq.GetResponse(); hwReq.Method = "Get"; hwReq.KeepAlive = false; StreamReader reader = new StreamReader(hwRes.GetResponseStream(), System.Text.Encoding.GetEncoding("GB2312")); return reader.ReadToEnd();
}
// 提取HTML代码中的网址
public static ArrayList GetHyperLinks(string htmlCode)
{
ArrayList al = new ArrayList(); string strRegex = @"http://([/w-]+/.)+[/w-]+(/[/w- ./?%&=]*)?"; Regex r = new Regex(strRegex, RegexOptions.IgnoreCase);
MatchCollection m = r.Matches(htmlCode); for (int i = ; i <= m.Count - ; i++)
{
bool rep = false;
string strNew = m[i].ToString(); // 过滤重复的URL
foreach (string str in al)
{
if (strNew == str)
{
rep = true;
break;
}
} if (!rep) al.Add(strNew);
} al.Sort(); return al;
}
// 把网址写入xml文件
static void WriteToXml(string strURL, ArrayList alHyperLinks)
{
XmlTextWriter writer = new XmlTextWriter("HyperLinks.xml", Encoding.UTF8); writer.Formatting = Formatting.Indented;
writer.WriteStartDocument(false);
writer.WriteDocType("HyperLinks", null, "urls.dtd", null);
writer.WriteComment("提取自" + strURL + "的超链接");
writer.WriteStartElement("HyperLinks");
writer.WriteStartElement("HyperLinks", null);
writer.WriteAttributeString("DateTime", DateTime.Now.ToString()); foreach (string str in alHyperLinks)
{
string title = GetDomain(str);
string body = str;
writer.WriteElementString(title, null, body);
} writer.WriteEndElement();
writer.WriteEndElement(); writer.Flush();
writer.Close();
} // 获取网址的域名后缀
static string GetDomain(string strURL)
{
string retVal; string strRegex = @"(/.com/|/.net/|/.cn/|/.org/|/.gov/)"; Regex r = new Regex(strRegex, RegexOptions.IgnoreCase);
Match m = r.Match(strURL);
retVal = m.ToString(); strRegex = @"/.|/$";
retVal = Regex.Replace(retVal, strRegex, "").ToString(); if (retVal == "")
retVal = "other"; return retVal;
}
}
}
C#抓取网站下的链接下的网页数据怎么做
1读取此网站的页面源代码
2利用正则取得所有超连接的内容
3把取得的超连接内容循环,再次操作1,2的步骤,这次2中写逻辑你想要的 数据 ---读取网页源代码---
protected void Page_Load(object sender, EventArgs e)
{
string strtemp;
strtemp = GetURLContent("http://go.microsoft.com/fwlink/?LinkId=25817", "utf-8");
//Response.ContentType = "application/x-www-form-urlencoded";
Response.Write(strtemp);
}
string GetURLContent(string url,string EncodingType)
{
string PetiResp = "";
Stream mystream;
//"http://go.microsoft.com/fwlink/?LinkId=25817"
//"utf-8"
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
req.AllowAutoRedirect = true;
System.Net.HttpWebResponse resp = (System.Net.HttpWebResponse)req.GetResponse();
if (resp.StatusCode == System.Net.HttpStatusCode.OK)
{
mystream = resp.GetResponseStream();
System.Text.Encoding encode = System.Text.Encoding.GetEncoding(EncodingType);
StreamReader readStream = new StreamReader(mystream, encode);
char[] cCont = new char[];
int count = readStream.Read(cCont, , );
while (count > )
{
// Dumps the 256 characters on a string and displays the string to the console.
String str = new String(cCont, , count);
PetiResp += str;
count = readStream.Read(cCont, , );
}
resp.Close();
return PetiResp;
}
resp.Close();
return null;
}
System.Web.UI抓取网页链接
using System;
using System.Data;
using System.Configuration;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.UI.MobileControls;
/// <summary>
/// 网页类
/// </summary>
public class WebPage
{
#region 私有成员
private Uri m_uri; //url
private List<Link> m_links; //此网页上的链接
private string m_title; //标题
private string m_html; //HTML代码
private string m_outstr; //网页可输出的纯文本
private bool m_good; //网页是否可用
private int m_pagesize; //网页的大小
private static Dictionary<string, CookieContainer> webcookies = new Dictionary<string, CookieContainer>();//存放所有网页的Cookie #endregion #region 属性 /// <summary>
/// 通过此属性可获得本网页的网址,只读
/// </summary>
public string URL
{
get
{
return m_uri.AbsoluteUri;
}
} /// <summary>
/// 通过此属性可获得本网页的标题,只读
/// </summary>
public string Title
{
get
{
if (m_title == "")
{
Regex reg = new Regex(@"(?m)<title[^>]*>(?<title>(?:\w|\W)*?)</title[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase);
Match mc = reg.Match(m_html);
if (mc.Success)
m_title = mc.Groups["title"].Value.Trim();
}
return m_title;
}
}
public string M_html
{
get
{
if (m_html == null)
{
m_html = "";
}
return m_html;
}
}
/// <summary>
/// 此属性获得本网页的所有链接信息,只读
/// </summary>
public List<Link> Links
{
get
{
if (m_links.Count == ) getLinks();
return m_links;
}
} /// <summary>
/// 此属性返回本网页的全部纯文本信息,只读
/// </summary>
public string Context
{
get
{
if (m_outstr == "") getContext(Int16.MaxValue);
return m_outstr;
}
} /// <summary>
/// 此属性获得本网页的大小
/// </summary>
public int PageSize
{
get
{
return m_pagesize;
}
}
/// <summary>
/// 此属性获得本网页的所有站内链接
/// </summary>
public List<Link> InsiteLinks
{
get
{
return getSpecialLinksByUrl("^http://" + m_uri.Host, Int16.MaxValue);
}
} /// <summary>
/// 此属性表示本网页是否可用
/// </summary>
public bool IsGood
{
get
{
return m_good;
}
}
/// <summary>
/// 此属性表示网页的所在的网站
/// </summary>
public string Host
{
get
{
return m_uri.Host;
}
}
#endregion /// <summary>
/// 从HTML代码中分析出链接信息
/// </summary>
/// <returns>List<Link></returns>
private List<Link> getLinks()
{
if (m_links.Count == )
{
Regex[] regex = new Regex[];
regex[] = new Regex(@"<a\shref\s*=""(?<URL>[^""]*).*?>(?<title>[^<]*)</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
regex[] = new Regex("<[i]*frame[^><]+src=(\"|')?(?<url>([^>\"'\\s)])+)(\"|')?[^>]*>", RegexOptions.IgnoreCase); for (int i = ; i < ; i++)
{
Match match = regex[i].Match(m_html);
while (match.Success)
{
try
{
string url = HttpUtility.UrlDecode(new Uri(m_uri, match.Groups["URL"].Value).AbsoluteUri); string text = "";
if (i == ) text = new Regex("(<[^>]+>)|(\\s)|( )|&|\"", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(match.Groups["text"].Value, ""); Link link = new Link();
link.Text = text;
link.NavigateUrl = url; m_links.Add(link);
}
catch (Exception ex) { Console.WriteLine(ex.Message); };
match = match.NextMatch();
}
}
}
return m_links;
}
/// <summary>
/// 此私有方法从一段HTML文本中提取出一定字数的纯文本
/// </summary>
/// <param name="instr">HTML代码</param>
/// <param name="firstN">提取从头数多少个字</param>
/// <param name="withLink">是否要链接里面的字</param>
/// <returns>纯文本</returns>
private string getFirstNchar(string instr, int firstN, bool withLink)
{
if (m_outstr == "")
{
m_outstr = instr.Clone() as string;
m_outstr = new Regex(@"(?m)<script[^>]*>(\w|\W)*?</script[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(m_outstr, "");
m_outstr = new Regex(@"(?m)<style[^>]*>(\w|\W)*?</style[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(m_outstr, "");
m_outstr = new Regex(@"(?m)<select[^>]*>(\w|\W)*?</select[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(m_outstr, "");
if (!withLink) m_outstr = new Regex(@"(?m)<a[^>]*>(\w|\W)*?</a[^>]*>", RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(m_outstr, "");
Regex objReg = new System.Text.RegularExpressions.Regex("(<[^>]+?>)| ", RegexOptions.Multiline | RegexOptions.IgnoreCase);
m_outstr = objReg.Replace(m_outstr, "");
Regex objReg2 = new System.Text.RegularExpressions.Regex("(\\s)+", RegexOptions.Multiline | RegexOptions.IgnoreCase);
m_outstr = objReg2.Replace(m_outstr, " "); }
return m_outstr.Length > firstN ? m_outstr.Substring(, firstN) : m_outstr;
} #region 公有文法
/// <summary>
/// 此公有方法提取网页中一定字数的纯文本,包括链接文字
/// </summary>
/// <param name="firstN">字数</param>
/// <returns></returns>
public string getContext(int firstN)
{
return getFirstNchar(m_html, firstN, true);
} /// <summary>
/// 此公有方法从本网页的链接中提取一定数量的链接,该链接的URL满足某正则式
/// </summary>
/// <param name="pattern">正则式</param>
/// <param name="count">返回的链接的个数</param>
/// <returns>List<Link></returns>
public List<Link> getSpecialLinksByUrl(string pattern, int count)
{
if (m_links.Count == ) getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
int cnt = ;
while (i.MoveNext() && cnt < count)
{
if (new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase).Match(i.Current.NavigateUrl).Success)
{
SpecialLinks.Add(i.Current);
cnt++;
}
}
return SpecialLinks;
} /// <summary>
/// 此公有方法从本网页的链接中提取一定数量的链接,该链接的文字满足某正则式
/// </summary>
/// <param name="pattern">正则式</param>
/// <param name="count">返回的链接的个数</param>
/// <returns>List<Link></returns>
public List<Link> getSpecialLinksByText(string pattern, int count)
{
if (m_links.Count == ) getLinks();
List<Link> SpecialLinks = new List<Link>();
List<Link>.Enumerator i;
i = m_links.GetEnumerator();
int cnt = ;
while (i.MoveNext() && cnt < count)
{
if (new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase).Match(i.Current.Text).Success)
{
SpecialLinks.Add(i.Current);
cnt++;
}
}
return SpecialLinks;
} /// <summary>
/// 这公有方法提取本网页的纯文本中满足某正则式的文字
/// </summary>
/// <param name="pattern">正则式</param>
/// <returns>返回文字</returns>
public string getSpecialWords(string pattern)
{
if (m_outstr == "") getContext(Int16.MaxValue);
Regex regex = new Regex(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase);
Match mc = regex.Match(m_outstr);
if (mc.Success)
return mc.Groups[].Value;
return string.Empty;
}
#endregion #region 构造函数 private void Init(string _url)
{
try
{
m_uri = new Uri(_url);
m_links = new List<Link>();
m_html = "";
m_outstr = "";
m_title = "";
m_good = true;
if (_url.EndsWith(".rar") || _url.EndsWith(".dat") || _url.EndsWith(".msi"))
{
m_good = false;
return;
}
HttpWebRequest rqst = (HttpWebRequest)WebRequest.Create(m_uri);
rqst.AllowAutoRedirect = true;
rqst.MaximumAutomaticRedirections = ;
rqst.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
rqst.KeepAlive = true;
rqst.Timeout = ;
lock (WebPage.webcookies)
{
if (WebPage.webcookies.ContainsKey(m_uri.Host))
rqst.CookieContainer = WebPage.webcookies[m_uri.Host];
else
{
CookieContainer cc = new CookieContainer();
WebPage.webcookies[m_uri.Host] = cc;
rqst.CookieContainer = cc;
}
}
HttpWebResponse rsps = (HttpWebResponse)rqst.GetResponse();
Stream sm = rsps.GetResponseStream();
if (!rsps.ContentType.ToLower().StartsWith("text/") || rsps.ContentLength > << )
{
rsps.Close();
m_good = false;
return;
}
Encoding cding = System.Text.Encoding.Default;
string contenttype = rsps.ContentType.ToLower();
int ix = contenttype.IndexOf("charset=");
if (ix != -)
{
try
{
cding = System.Text.Encoding.GetEncoding(rsps.ContentType.Substring(ix + "charset".Length + ));
}
catch
{
cding = Encoding.Default;
} //该处视情况而定 有的需要解码
//m_html = HttpUtility.HtmlDecode(new StreamReader(sm, cding).ReadToEnd());
m_html = new StreamReader(sm, cding).ReadToEnd(); }
else
{
//该处视情况而定 有的需要解码
//m_html = HttpUtility.HtmlDecode(new StreamReader(sm, cding).ReadToEnd()); m_html = new StreamReader(sm, cding).ReadToEnd();
Regex regex = new Regex("charset=(?<cding>[^=]+)?\"", RegexOptions.IgnoreCase);
string strcding = regex.Match(m_html).Groups["cding"].Value;
try
{
cding = Encoding.GetEncoding(strcding);
}
catch
{
cding = Encoding.Default;
}
byte[] bytes = Encoding.Default.GetBytes(m_html.ToCharArray());
m_html = cding.GetString(bytes);
if (m_html.Split('?').Length > )
{
m_html = Encoding.Default.GetString(bytes);
}
}
m_pagesize = m_html.Length;
m_uri = rsps.ResponseUri;
rsps.Close();
}
catch (Exception ex)
{ }
}
public WebPage(string _url)
{
string uurl = "";
try
{
uurl = Uri.UnescapeDataString(_url);
_url = uurl;
}
catch { };
Init(_url);
}
#endregion
}
WebPage webInfo = new WebPage("网址"); webInfo.Context;//不包含html标签的所有内容 webInfo.M_html;//包含html标签的内容 ...参考属性
提取网页图片链接地址代码
原计划中使用过的LZW图片压缩算法:
LZW压缩算法是一种新颖的压缩方法,由Lemple-Ziv-Welch 三人共同创造,用他们的名字命名。
它采用了一种先进的串表压缩不,将每个第一次出现的串放在一个串表中,用一个数字来表示串,压
缩文件只存贮数字,则不存贮串,从而使图象文件的压缩效率得到较大的提高。奇妙的是,不管是在
压缩还是在解压缩的过程中都能正确的建立这个串表,压缩或解压缩完成后,这个串表又被丢弃。
.基本原理
首先建立一个字符串表,把每一个第一次出现的字符串放入串表中,并用一个数字来表示,这个
数字与此字符串在串表中的位置有关,并将这个数字存入压缩文件中,如果这个字符串再次出现时,
即可用表示它的数字来代替,并将这个数字存入文件中。压缩完成后将串表丢弃。如"print" 字符串
,如果在压缩时用266表示,只要再次出现,均用266表示,并将"print"字符串存入串表中,在图象解
码时遇到数字266,即可从串表中查出266所代表的字符串"print",在解压缩时,串表可以根据压缩数
据重新生成。
.实现方法
A.初始化串表
在压缩图象信息时,首先要建立一个字符串表,用以记录每个第一次出现的字符串。一个字符串
表最少由两个字符数组构成,一个称为当前数组,一个称为前缀数组,因为在GIF文件中每个基本字符
串的长度通常为2(但它表示的实际字符串长度可达几百甚至上千),一个基本字符串由当前字符和它
前面的字符(也称前缀)构成。前缀数组中存入字符串中的首字符,当前数组存放字符串中的尾字符
,其存入位置相同,因此只要确定一个下标,就可确定它所存贮的基本字符串,所以在数据压缩时,
用下标代替基本字符串。一般串表大小为4096个字节(即2 的12次方),这意味着一个串表中最多能
存贮4096个基本字符串,在初始化时根据图象中色彩数目多少,将串表中起始位置的字节均赋以数字
,通常当前数组中的内容为该元素的序号(即下标),如第一个元素为0,第二个元素为1,第15个元
素为14 ,直到下标为色彩数目加2的元素为止。如果色彩数为256,则要初始化到第258个字节,该字
节中的数值为257。其中数字256表示清除码,数字257 为图象结束码。后面的字节存放文件中每一个
第一次出现的串。同样也要音乐会 前缀数组初始化,其中各元素的值为任意数,但一般均将其各位置
,即将开始位置的各元素初始化为0XFF,初始化的元素数目与当前数组相同,其后的元素则要存入每
一个第一次出现的字符串了。如果加大串表的长度可进一步提高压缩效率,但会降低解码速度。
B.压缩方法
了解压缩方法时,先要了解几个名词,一是字符流,二是代码流,三是当前码,四是当前前缀。
字符流是源图象文件中未经压缩的图象数据;代码流是压缩后写入GIF 文件的压缩图象数据;当前码
是从字符流中刚刚读入的字符;当前前缀是刚读入字符前面的字符。
GIF 文件在压缩时,不论图象色彩位数是多少,均要将颜色值按字节的单位放入代码流中,每个字节
均表示一种颜色。虽然在源图象文件中用一个字节表示16色、4色、2色时会出现4位或更多位的浪费(
因为用一个字节中的4位就可以表示16色),但用LZW 压缩法时可回收字节中的空闲位。在压缩时,先
从字符流中读取第一个字符作为当前前缀,再取第二个字符作为当前码,当前前缀与当前码构成第一
个基本字符串(如当前前缀为A,当前码为B则此字符串即为AB),查串表,此时肯定不会找到同样字
符串,则将此字符串写入串表,当前前缀写入前缀数组,当前码写入当前数组,并将当前前缀送入代
码流,当前码放入当前前缀,接着读取下一个字符,该字符即为当前码了,此时又形成了一个新的基
本字符串 (若当前码为C,则此基本字符串为BC),查串表,若有此串,则丢弃当前前缀中的值,用
该串在串表中的位置代码(即下标)作为当前前缀,再读取下一个字符作为当前码,形成新的基本字
符串,直到整幅图象压缩完成。由此可看出,在压缩时,前缀数组中的值就是代码流中的字符,大于
色彩数目的代码肯定表示一个字符串,而小于或等于色彩数目的代码即为色彩本身。
C.清除码
事实上压缩一幅图象时,常常要对串表进行多次初始化,往往一幅图象中出现的第一次出现的基
本字符串个数会超过4096个,在压缩过程中只要字符串的长度超过了4096,就要将当前前缀和当前码
输入代码流,并向代码流中加入一个清除码,初始化串表,继续按上述方法进行压缩。
D.结束码
当所有压缩完成后,就向代码流中输出一个图象结束码,其值为色彩数加1,在256色文件中,结
束码为257。
E.字节空间回收
在GIF文件输出的代码流中的数据,除了以数据包的形式存放之外,所有的代码均按单位存贮,样
就有效的节省了存贮空间。这如同4位彩色(16色)的图象,按字节存放时,只能利用其中的4位,另
外的4位就浪费了,可按位存贮时,每个字节就可以存放两个颜色代码了。事实上在GIF 文件中,使用
了一种可变数的存贮方法,由压缩过程可看出,串表前缀数组中各元素的值颁是有规律的,以256色的
GIF文件中,第258-511元素中值的范围是0- ,正好可用9位的二进制数表示,第512-1023元素中值
的范围是0-,正好可用10位的二进制数表示,第1024- 元素中值的范围是0-,正好用11
位的二进制数表示,第2048-4095元素中值的范围是0-,正好用12位的二进制数表示。用可变位数
存贮代码时,基础位数为图象色彩位数加1,随着代码数的增加,位数也在加大,直到位数超过为12(
此时字符串表中的字符串个数正好为2 的12次方,即4096个)。 其基本方法是:每向代码流加入一个
字符,就要判别此字符所在串在串表中的位置(即下标)是否超过2的当前位数次方,一旦超过,位数
加1。如在4位图象中,对于刚开始的代码按5位存贮,第一个字节的低5位放第一个代码,高三位为第
二个代码的低3位,第二个字节的低2位放第二个代码的高两位,依次类推。对于8位(256色)的图象
,其基础位数就为9,一个代码最小要放在两个字节。
F.压缩范围
以下为256色GIF文件编码实例,如果留心您会发现这是一种奇妙的编码方法,同时为什么在压缩
完成后不再需要串表,而且还在解码时根据代码流信息能重新创建串表。
字 符 串: ,,,,,,,,,,,,,,,…
当 前 码: ,,,,,,,,,,,,,,…
当前前缀: ,,,,,,,,,,,,,,…
当前数组: ,,, , ,,, ,,,…
数组下标: ,,,,,,,,,,…
代 码 流: ,,,,,,,,,,…
GIF文件作为一种重要的图形图象文件格式,尽管其编码规则极复杂,但其压缩效率是极高的,特
别是对某些平滑过渡的图象的图形,压缩效果更好。同时由于其在压缩过程中的对图象信息能够完整
的保存,在目前流行的电子图片及电子图书中得到了广泛的应用。 一般的html编辑器也是这样实现类似的功能,要改写html中图片的代码,想办法从html的内容中找到img的链接地址,然后在服务器端把他下载下来保存到服务器端。然后把地址给改为服务上保存图片的绝对地址,以此为参数调用图片。 一:实现对HTML中网页链接地址的提取 // AnalizeIMG.java // 主程序 import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; public class AnalizeIMG { public void p(String s)
{
System.out.println(s);
} public void analizeFile(String infile,String outfile) throws Exception
{
File file = new File(infile);
if (file == null || ! file.exists()) {
p( "File" + infile + "not exits !" );
} if ( ! file.canRead()) {
p( "File" + infile +" can't read !" ); } String strLine = null ;
FileReader frd = new FileReader(infile);
BufferedReader bufferedReader = new BufferedReader(frd);
try {
AnalizeWebParse parse = new AnalizeWebParse();
String s = parse.parse(bufferedReader); createFile(outfile,s); } catch (Exception ex) {
throw ex;
} finally {
frd.close();
bufferedReader.close();
}
} private void createFile(String filename, String content) {
FileWriter f = null ;
try {
f = new FileWriter(filename);
if (f == null || content == null ) {
return ;
} f.write(content);
f.flush();
f.close(); } catch (Exception e) { } finally {
if (f!= null ) {
try {
f.close();
} catch (Exception e) { }
}
}
}
public static void main(String arg[])
{
AnalizeIMG ana = new AnalizeIMG();
try {
ana.analizeFile("E:\\1.txt" , "E:\\out.lst");
} catch (Exception ex) {
ex.printStackTrace();
}
}
} 二:提取与下载 // AnalizeWebParse.java // 网页分析代码,
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.regex.Pattern; import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit.ParserCallback;
import javax.swing.text.html.parser.ParserDelegator;
public class AnalizeWebParse extends ParserCallback { StringBuffer sb = new StringBuffer(); boolean start =false ;
boolean finished =false ; public void p(String s)
{
System.out.println(s);
} public void handleStartTag(HTML.Tag tag, MutableAttributeSet attribs,
int pos) { if (finished == true )
{
return ;
} if (start == false ) {
if (tag == HTML.Tag.DIV) {
String cla = (String) attribs
.getAttribute(HTML.Attribute.CLASS);
if (cla == null ) {
return ;
} if (cla.indexOf("body")!= - ) {
// Start
start = true ;
}
}
}
} public void handleEndTag(HTML.Tag tag, int pos) {
if (tag == HTML.Tag.DIV && start == true && finished == false ) {
finished = true ;
} } public void handleText( char [] text, int pos) { } public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (t == HTML.Tag.IMG) {
// get a src
String src = (String) a.getAttribute(HTML.Attribute.SRC);
if (src == null ) {
return ;
} if (Pattern.matches("^(http://.+)" , src)) {
sb.append(src).append("\n");
}
}
} public String parse(BufferedReader file) throws Exception {
if (file == null )
{
return null ;
} ParserDelegator pd = new ParserDelegator();
try {
pd.parse(file, this , true );
} catch(Exception e) {
throw e;
} return sb.toString();
}
} 对于部分需要,可以提取Html内容中的所有图片信息
源码如下 protected ArrayList GetAList(string HtmlContent)
{
try
{
ArrayList arr = new ArrayList();
HtmlContent = content.Replace("\r\n","");
HtmlContent = content.Trim();
string partern = @"\<img(.*?)\>";//@"<a (.*)>.*</a> ";//"<a.*(?=Headline)(.|\n)*?</a>";//@"/<a (.*)>.*<\/\a>/ "; System.Text.RegularExpressions.Regex.Regex regex = new System.Text.RegularExpressions.rege
x(partern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.MatchCollection mc = regex.Matches(HtmlContent);
if(mc.Count <= )
return arr;
System.Text.RegularExpressions.Regex.regex2=new System.Text.RegularExpressions.Regex(@"('|""|/)?[\w_()]*(.jpg|.bmp|.gif|.png|.jpeg)",System.Text.RegularExpressions.RegexOptions.IgnoreCase); foreach(Match m in mc)
{
System.Text.RegularExpressions.MatchCollection m2 = regex2.Matches(m.ToString());
if( m2.Count > )
{
arr.Add( m2[].ToString().Substring() );
}
}
return arr; }
catch(Exception ex)
{
throw ex;
} }
三:Java字符串替换函数replace实现图片链接地址,即绝对地址向相对地址的转换。然后发送在服务器上经过压缩后的图片的相对地址,实现了WAP的图片浏览。
Public static Stringreplace (String strSource ,String strFrom,String srtTo){
//如果要替换的字串为空,则直接返回。
If (srtForm==null||strFrom.equals(''))return srtSource;
String srtDesr='';//提取的图片链接地址
Int intFromLen=srtFrom.length();
Int intPos;
While((intPos=strSource.indexof(srtFrom))!=-{
srtDest=strDext+strSource.substring(,inrPos);
strDext=srtDext+strSource;
Return strDest;
}
用C#和正则表达式截取html代码
用C#和正则表达式截【IT168技术文档】如何使用C#和正则表达式截取html代码呢,如何处理html代码中的\r\n这样的字符呢,下面我们来一起研究一下,先从截取目标开始。 一、代码说明 .以下html表示收到的3个组的信息,如果含有"unread.gif"表示未读消息,否则表示已读信息。 .截取未读消息和已读消息的条数和theUrl .要将未读信息和已读信息分开放入两个组里。 <div class="dxx_of" id="message1" onmouseover="msgOnmouseover(1)" onmouseout="msgOnmouseout(1)" /> <div class="dxx1" style="padding:15px 10px;"><img src="http://www.microsoft.com/i2/unread.gif" width="" height="" title="" /></div> <div class="dxx2"> <table class="aa" border="" cellpadding="" cellspacing="" > <colgroup> <col width="" /> </colgroup> <tbody> <tr basestyle="oRowLine2"> <td valign="top" onclick="javascript:document.location='thUrl';" > wa <div><span class='c9'>共6条会话</span><a href="thUrl" class="sl">+展开</a></span></div> <span class="c9"></span> </td> </tr> </tbody> </table> </div> <div class="c"></div> </div> <div class="dxx_of" id="message2" onmouseover="msgOnmouseover(2)" onmouseout="msgOnmouseout(2)" /> <div class="dxx1" style="padding:15px 10px;"></div> <div class="dxx2"> <table class="aa" border="" cellpadding="" cellspacing="" > <colgroup> <col width="" /> </colgroup> <tbody> <tr basestyle="oRowLine2"> <td valign="top" onclick="javascript:document.location='thUrl';" > wa <div><span class='c9'>共3条会话</span><a href="thUrl1" class="sl">+展开</a></span></div> <span class="c9"></span> </td> </tr> </tbody> </table> </div> <div class="c"></div> </div> <div class="dxx_of" id="message3" onmouseover="msgOnmouseover(3)" onmouseout="msgOnmouseout(3)" /> <div class="dxx1" style="padding:15px 10px;"></div> <div class="dxx2"> 同上很多html内容 </div> <div class="c"></div> </div> 二、官方样例 用C#和正则表达式如何对上例的代码进行截取呢,我们来看看微软官方发布的样例。 示例 System.Text.RegularExpressions.RegexOptions 。 C# class TestRegularExpressions { static void Main() { string[] sentences = { "cow over the moon", "Betsy the Cow", "cowering in the corner", "no match here" }; string sPattern = "cow"; foreach (string s in sentences) { System.Console.Write("{0,24}", s); if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase)) { System.Console.WriteLine(" (match for '{0}' found)", sPattern); } else { System.Console.WriteLine(); } } // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } /* Output: cow over the moon (match for 'cow' found) Betsy the Cow (match for 'cow' found) cowering in the corner (match for 'cow' found) no match here */ 以上代码是一个控制台应用程序,用于对数组中的字符串执行简单的不区分大小写的搜索。给定要搜索的字符串和包含搜索模式的字符串后,静态方法 Regex.IsMatch 将执行搜索。使用第三个参数指示忽略大小写。 实际可以使用以下代码来匹配。 Regex regex = new Regex("<div class=\"dxx_of\" id=\".+/>.+(?<htmlCode>.+).+<div class=\"c\"></div>");
MatchCollection matchs = regex.Matches(resultHtml); if(maths.Count>) strig html = matchs[].Groups["htmlCode"].Value; 但是正则的"."只能匹配不含\n的任何字符,可是HTML代码中有很多\r\n。 三、如何处理特殊字符 如果不重要可以用string Replace方法将 \r\n替换掉,下面这个方法可以有效的将其分为3个组。 C# class TestRegularExpressionValidation { static void Main() { string[] numbers = { "123-456-7890", "444-234-22450", "690-203-6578", "146-893-232", "146-839-2322", "4007-295-1111", "407-295-1111", "407-2-5555", }; string sPattern = "^\d{3}-\d{3}-\d{4}$"; foreach (string s in numbers) { System.Console.Write("{0,14}", s); if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern)) { System.Console.WriteLine(" - valid"); } else { System.Console.WriteLine(" - invalid"); } } // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } /* Output: 123-456-7890 - valid 444-234-22450 - invalid 690-203-6578 - valid 146-893-232 - invalid 146-839-2322 - valid 4007-295-1111 - invalid 407-295-1111 - valid 407-2-5555 - invalid */ 以上代码是一个控制台应用程序,此程序使用正则表达式验证数组中每个字符串的格式。验证要求每个字符串具有电话号码的形式,即用短划线将数字分成三组,前两组各包含三个数字,第三组包含四个数字。这是使用正则表达式 ^\d{}-\d{}-\d{}$ 完成的。取html代码
在《爬虫蜘蛛程序的制作(C#语言)》一文中,已经介绍了爬虫程序实现的基本方法,可以说,已经实现了爬虫的功能。只是它存在一个效率问题,下载速度可能很慢。这是两方面
在《爬虫/蜘蛛程序的制作(C#语言)》一文中,已经介绍了爬虫程序实现的基本方法,可以说,已经实现了爬虫的功能。只是它存在一个效率问题,下载速度可能很慢。这是两方面的原因造成的:
.分析和下载不能同步进行。在《爬虫/蜘蛛程序的制作(C#语言)》中已经介绍了爬虫程序的两个步骤:分析和下载。在单线程的程序中,两者是无法同时进行的。也就是说,分析时会造成网络空闲,分析的时间越长,下载的效率越低。反之也是一样,下载时无法同时进行分析,只有停下下载后才能进行下一步的分析。问题浮出水面,我想大家都会想到:把分析和下载用不同的线程进行,问题不就解决了吗?
.只是单线程下载。相信大家都有用过网际快车等下载资源的经历,它里面是可以设置线程数的(近年版本默认是10,曾经默认是5)。它会将文件分成与线程数相同的部分,然后每个线程下载自己的那一部分,这样下载效率就有可能提高。相信大家都有加多线程数,提升下载效率的经历。但细心的用户会发现,在带宽一定的情况下,并不是线程越多,速度越快,而是在某一点达到峰值。爬虫作为特殊的下载工具,不具备多线程的能力何以有效率可谈?爬虫在信息时代的目的,难道不是快速获取信息吗?所以,爬虫需要有多线程(可控数量)同时下载网页。
好了,认识、分析完问题,就是解决问题了:
多线程在C#中并不难实现。它有一个命名空间:System.Threading,提供了多线程的支持。
要开启一个新线程,需要以下的初始化:
ThreadStart startDownload = new ThreadStart( DownLoad );
//线程起始设置:即每个线程都执行DownLoad(),注意:DownLoad()必须为不带有参数的方法
Thread downloadThread = new Thread( startDownload ); //实例化要开启的新类
downloadThread.Start();//开启线程
由于线程起始时启动的方法不能带有参数,这就为多线程共享资源添加了麻烦。不过我们可以用类级变量(当然也可以使用其它方法,笔者认为此方法最简单易用)来解决这个问题。知道开启多线程下载的方法后,大家可能会产生几个疑问:
.如何控制线程的数量?
.如何防止多线程下载同一网页?
.如何判断线程结束?
.如何控制线程结束?
下面就这几个问题提出解决方法:
.线程数量我们可以通过for循环来实现,就如同当年初学编程的打点程序一样。
比如已知用户指定了n(它是一个int型变量)个线程吧,可以用如下方法开启五个线程。
Thread[] downloadThread;
//声名下载线程,这是C#的优势,即数组初始化时,不需要指定其长度,可以在使用时才指定。
这个声名应为类级,这样也就为其它方法控件它们提供了可能
ThreadStart startDownload = new ThreadStart( DownLoad );
//线程起始设置:即每个线程都执行DownLoad()
downloadThread = new Thread[ n ];//为线程申请资源,确定线程总数
for( int i = ; i < n; i++ )//开启指定数量的线程数
{
downloadThread[i] = new Thread( startDownload );//指定线程起始设置
downloadThread[i].Start();//逐个开启线程
}
好了,实现控制开启线程数是不是很简单啊?
.下面出现的一个问题:所有的线程都调用DonwLoad()方法,这样如何避免它们同时下载同一个网页呢?
这个问题也好解决,只要建立一下Url地址表,表中的每个地址只允许被一个线程申请即可。具体实现:
可以利用数据库,建立一个表,表中有四列,其中一列专门用于存储Url地址,另外两列分别存放地址对应的线程以及该地址被申请的次数,最后一列存放下载的内容。(当然,对应线程一列不是必要的)。当有线程申请后,将对应线程一列设定为当前线程编号,并将是否申请过一列设置为申请一次,这样,别的线程就无法申请该页。如果下载成功,则将内容存入内容列。如果不成功,内容列仍为空,作为是否再次下载的依据之一,如果反复不成功,则进程将于达到重试次数(对应该地址被申请的次数,用户可设)后,申请下一个Url地址。主要的代码如下(以VFP为例):
<建立表>
CREATE TABLE (ctablename) ( curl M , ctext M , ldowned I , threadNum I )
&&建立一个表ctablename.dbf,含有地址、文本内容、已经尝试下载次数、
线程标志(初值为-,线程标志是从0开始的整数)四个字段
<提取Url地址>
cfullname = (ctablename) + '.dbf'&&为表添加扩展名
USE (cfullname)
GO TOP
LOCATE FOR (EMPTY( ALLTRIM( ctext ) ) AND ldowned < AND
( threadNum = thisNum OR threadNum = - ) )
&&查找尚未下载成功且应下载的属于本线程权限的Url地址,thisNum是当前线程的编号,
可以通过参数传递得到
gotUrl = curl
recNum = RECNO()
IF recNum <= RECCOUNT() THEN &&如果在列表中找到这样的Url地址
UPDATE (cfullname) SET ldowned = ( ldowned + ) , threadNum =
thisNum WHERE RECNO() = recNum &&更新表,将此记录更新为已申请,即下载次数加1,
线程标志列设为本线程的编号。
<下载内容>
cfulltablename = (ctablename) + '.dbf'
USE (cfulltablename)
SET EXACT ON
LOCATE FOR curl = (csiteurl) && csiteurl是参数,为下载到的内容所对应的Url地址
recNumNow = RECNO()&&得到含有此地址的记录号
UPDATE (cfulltablename) SET ctext = (ccontent) WHERE RECNO() =
recNumNow &&插入对应地址的对应内容
<插入新地址>
ctablename = (ctablename) + '.dbf'
USE (ctablename)
GO TOP
SET EXACT ON
LOCATE FOR curl = (cnewurl) &&查找有无此地址
IF RECNO() > RECCOUNT() THEN &&如果尚无此地址
SET CARRY OFF
INSERT INTO (ctablename) ( curl , ctext , ldowned , threadNum )
VALUES ( (cnewurl) , "" , , - ) &&将主页地址添加到列表
好了,这样就解决了多线程中,线程冲突。当然,去重问题也可以在C#语言内解决,只根建立一个临时文件(文本就可以),保存所有的Url地址,差对它们设置相应的属性即可,但查找效率可能不及数据库快。
.线程结束是很难判断的,因为它总是在查找新的链接。用者认为可以假设:线程重复N次以后还是没有能申请到新的Url地址,那么可以认为它已经下载完了所有链接。主要代码如下:
string url = "";
int times = ;
while ( url == "" )//如果没有找到符合条件的记录,则不断地寻找符合条件的记录
{
url = getUrl.GetAUrl( …… );//调用GetAUrl方法,试图得到一个url值
if ( url == "" )//如果没有找到
{
times ++;//尝试次数自增
continue; //进行下一次尝试
}
if ( times > N ) //如果已经尝试够了次数,则退出进程
{
downloadThread[i].Abort; //退出进程
}
else//如果没有尝试够次数
{
Times = ; //尝试次数归零处理
}
//进行下一步针对得到的Url的处理
}
.这个问题相对简单,因为在问题一中已经建议,将线程声名为类级数组,这样就很易于控制。只要用一个for循环即可结束。代码如下:
for( int i = ; i < n; i++ )//关闭指定数量n的线程数
{
downloadThread[i].Abort();//逐个关闭线程
}
好了,一个蜘蛛程序就这样完成了,在C#面前,它的实现原来如此简单。
这里笔者还想提醒读者:笔者只是提供了一个思路及一个可以实现的解决方案,但它并不是最佳的,即使这个方案本身,也有好多可以改进的地方,留给读者思考。
最后说明一下我所使用的环境:
winXP sp2 Pro
VFP 9.0
Visual Studio .net中文企业版
正则表达式过滤html标签对
public string DealHtml(string str)
{
//str = Regex.Replace(str, @"\<(img)[^>]*>|<\/(img)>", "", RegexOptions.IgnoreCase);
str = Regex.Replace(str, @"\<(table|tbody|tr|td|th)[^>]*>|<\/(table|tbody|tr|td|th)>", "", RegexOptions.IgnoreCase);
str = Regex.Replace(str, @"\<(div|blockquote|fieldset|legend)[^>]*>|<\/(div|blockquote|fieldset|legend)>", "", RegexOptions.IgnoreCase);
//str = Regex.Replace(str, @"\<(font|i|u|h[1-9]|s)[^>]*>|<\/(font|i|u|h[1-9]|s)>", "", RegexOptions.IgnoreCase);
//str = Regex.Replace(str, @"\<(style|strong)[^>]*>|<\/(style|strong)>", "", RegexOptions.IgnoreCase);
//str = Regex.Replace(str, @"\<a[^>]*>|<\/a>", "", RegexOptions.IgnoreCase);
//str = Regex.Replace(str, @"\<(meta|iframe|frame|span|tbody|layer)[^>]*>|<\/(iframe|frame|meta|span|tbody|layer)>", "", RegexOptions.IgnoreCase);
str = Regex.Replace(str, @"\<br[^>]*", "", RegexOptions.IgnoreCase);
str = str.Replace("<br>", "");
return str;
}
正则表达式过滤(.net)
Regex re = new Regex("<a[^>]+href=[^>]+>[^<]*</a>");//这个可以过滤掉所有的超链接的内容
Regex re = new Regex("<a.*?>|</a>");//过滤超链接中的<a ....>xxx</a>标签,标签中xxx内容保留 public string checkStr(string html)
{
System.Text.RegularExpressions.Regex regex1 = new System.Text.RegularExpressions.Regex(@"<script[/s/S]+</script *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex2 = new System.Text.RegularExpressions.Regex(@" href *= *[/s/S]*script *:", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex3 = new System.Text.RegularExpressions.Regex(@" no[/s/S]*=", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex4 = new System.Text.RegularExpressions.Regex(@"<iframe[/s/S]+</iframe *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex5 = new System.Text.RegularExpressions.Regex(@"<frameset[/s/S]+</frameset *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex6 = new System.Text.RegularExpressions.Regex(@"/<img[^/>]+/>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex7 = new System.Text.RegularExpressions.Regex(@"</p>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex8 = new System.Text.RegularExpressions.Regex(@"<p>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.Regex regex9 = new System.Text.RegularExpressions.Regex(@"<[^>]*>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
html = regex1.Replace(html, ""); //过滤<script></script>标记
html = regex2.Replace(html, ""); //过滤href=Javascript: (<A>) 属性
html = regex3.Replace(html, " _disibledevent="); //过滤其它控件的on...事件
html = regex4.Replace(html, ""); //过滤iframe
html = regex5.Replace(html, ""); //过滤frameset
html = regex6.Replace(html, ""); //过滤frameset
html = regex7.Replace(html, ""); //过滤frameset
html = regex8.Replace(html, ""); //过滤frameset
html = regex9.Replace(html, "");
html = html.Replace(" ", "");
html = html.Replace("</strong>", "");
html = html.Replace("<strong>", "");
return html;
}
TempContent 表示包含有html的字符串;
TempContent = System.Text.RegularExpressions.Regex.Replace(TempContent,"<[^>]+>","");至少一个
TempContent = System.Text.RegularExpressions.Regex.Replace(TempContent,"<[^>]*>","");任意个
C#获取网页源码,自动判断网页字符集编码
方法一:
-- ::
C#获取网页源码,自动判断网页字符集编码
最近开始打算重新学习C#,并且尝试做点C#练习。而最让我感兴趣的还是和网站相关的东西。那就做个获取网站源码,采集文章的功能吧。
根据网上的资料,使用WebClient来获取源码,很容易就能得到网页源码。但是,发现,不同网站的网页字符编码就不一样的,如何自动分别不同网站的字符编码,而正确解释网页中的汉字呢。在C#里,提供了丰富的工具类库,可以轻松的转码。但是,却发现不能自动获取网站上的字符编码而自动正确的解释源码,而导致汉字显示乱码。以前我也用JAVA做过这样的功能,获取网站源码,同样,在JAVA的各种获取网站源码的类库里,也不能自动根据网页字符编码自动正确解释编码,只能我们自己手动来做了。
我的解决办法是先采用系统默认的编码从stream里得到源码,再使用正则表达式获取源码中的[获取网页字符编码描述信息],这个信息,一般来说,网页里都会有的,在网页源码的<head>里,类似这样的代码:<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />(其实不一样完全这样,有些不规范的,没有双引号,或者最后面没有/闭合,所以,正则表达式得考虑周全些),从这个代码里来获取编码信息,再判断系统当前的默认编码是否和这个一样,如果不同,再使用网页中取到的编码来重新从stream里解释取得网页源码。
源码如下:(下面这段代码是一个完整的获取网页源码,且自动正确解释汉字)
[CODE=csharp] using System.Net; using System.IO; using System.Text.RegularExpressions; private string getHtml(string url, string charSet)//url是要访问的网站地址,charSet是目标网页的编码,如果传入的是null或者"",那就自动分析网页的编码 { WebClient myWebClient = new WebClient(); //创建WebClient实例myWebClient // 需要注意的: //有的网页可能下不下来,有种种原因比如需要cookie,编码问题等等 //这是就要具体问题具体分析比如在头部加入cookie // webclient.Headers.Add("Cookie", cookie); //这样可能需要一些重载方法。根据需要写就可以了 //获取或设置用于对向 Internet 资源的请求进行身份验证的网络凭据。 myWebClient.Credentials = CredentialCache.DefaultCredentials; //如果服务器要验证用户名,密码 //NetworkCredential mycred = new NetworkCredential(struser, strpassword); //myWebClient.Credentials = mycred; //从资源下载数据并返回字节数组。(加@是因为网址中间有"/"符号) byte[] myDataBuffer = myWebClient.DownloadData(url); string strWebData = Encoding.Default.GetString(myDataBuffer); //获取网页字符编码描述信息 Match charSetMatch = Regex.Match(strWebData, "
这样就可以了,不信你试试,对了,如果你有什么更方便的方法请告诉我哦~~~~
这段代码里有很多注释,如果你也想做个网页采集功能,可以学一下,嘿嘿。
方法二:
用asp.net c# HttpWebRequest获取网页源代码
该方法需要传递目标网页的编码方式,比如System.Text.Encoding.Default或者System.Text.Encoding.UTF8
如果哪位高手知道如何自动判断目标页面的编码格式,请在评论中告知。谢谢! /// <summary>
/// 获取源代码
/// </summary>
/// <param name="url"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string GetPage(string url, Encoding encoding)
{
HttpWebRequest request = null;
HttpWebResponse response = null;
StreamReader reader = null;
try
{
request = (HttpWebRequest)WebRequest.Create(url);
request.UserAgent = "www.svnhost.cn";
request.Timeout = ;
request.AllowAutoRedirect = false;
response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK && response.ContentLength < * )
{
reader = new StreamReader(response.GetResponseStream(), encoding);
string html = reader.ReadToEnd();
return html;
}
}
catch
{
}
finally
{
if (response != null)
{
response.Close();
response = null;
}
if (reader != null)
reader.Close();
if (request != null)
request = null;
}
return string.Empty;
}
C# code
C# code
string str = "<div class=\"cl1\">sdfsdfsdf</div>";
Regex re = new Regex(@"(?is)(?<=>[^>]).*?(?=<[^<])"); foreach (Match ma in re.Matches(str))
{
Respo…… C# code
string str = "<div class=\"cl1\">sdfsdfsdf</div>";
Regex re = new Regex(@"(?is)(?<=>[^>]).*?(?=<[^<])"); foreach (Match ma in re.Matches(str))
{
Response.Write(ma.Value+ "<br/>");
} 对我有用[]丢个板砖[]引用举报管理TOP
精华推荐:【散分】公布一些常用的WebServices,希望对大家的应用有帮助~ soonfei
(soonfei)
等 级:
#3楼 得分:10回复于:-- ::
如果是特定的id或者calss
C# code
Regex re = new Regex(@"(?is)<div\s(id|class)="".*?"">(.*?)</div>"); 对我有用[]丢个板砖[]引用举报管理TOP
精华推荐:Share Some Methods about Linq to Entry dalmeeme
(无机战士)
等 级:
#4楼 得分:10回复于:-- ::
C# code
string s = @"<div class=""cl1"">sdfsdfsdf</div>";
MatchCollection matches = Regex.Matches(s, @"(?is)<div[^>]*?>(.*?)</div>");
foreach (Match match in matches)
Response.Write(match.Groups[].Value + "<br/>");
Regex regex = new Regex("<div class=\"dxx_of\" id=\".+/>.+(?<htmlCode>.+).+<div class=\"c\"></div>"); MatchCollection matchs = regex.Matches(resultHtml); if(maths.Count>) strig html = matchs[].Groups["htmlCode"].Value;
匹配divdiv的正则表达式
<div id="all">(.+?)</div>
匹配<div></div>的正则表达式
<input type="button" value="GetDiv" onclick="getDivContent();">
<script>
function getDivContent()
{
var div_str = 'dsadasd<div id="idname">csdfaer2342rwqer2?$@#$@#Dswweef</div>231231';
var re = /<div id="idname">[\s|\S]*?<\/div>/;
alert(div_str.match(re));
}
</script>
var re = /<div id="idname">[\s|\S]*?<\/div>/;
建立正则表达式
构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。
可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式。对 JScript 而言,分隔符为一对正斜杠 (/) 字符。例如:
/expression/
对 VBScript 而言,则采用一对引号 ("") 来确定正则表达式的边界。例如:
"expression"
在上面所示的两个示例中,正则表达式模式 (expression) 均存储在RegExp 对象的Pattern 属性中。
正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。 特殊字符
有不少元字符在试图对其进行匹配时需要进行特殊的处理。要匹配这些特殊字符,必须首先将这些字符转义,也就是在前面使用一个反斜杠 ()。下表给出了这些特殊字符及其含义:
特殊字符 说明
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ' ' 或 ' '。要匹配 $ 字符本身,请使用 $。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
. 匹配除换行符 之外的任何单字符。要匹配 .,请使用 。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 [。
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。
将下一个字符标记为或特殊字符、或原义字符、或后向引用、或八进制转义符。例如, 'n' 匹配字符 'n'。' ' 匹配换行符。序列 '\' 匹配 "",而 '(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 ^。
{ 标记限定符表达式的开始。要匹配 {,请使用 {。
| 指明两项之间的一个选择。要匹配 |,请使用 |。 下表给出了各种限定符及其含义的说明:
字符 描述
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {,}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 match 方法
使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回。
stringObj.match(rgExp)
参数
stringObj
必选项。对其进行查找的 String 对象或字符串文字。
rgExp
必选项。为包含正则表达式模式和可用标志的正则表达式对象。也可以是包含正则表达式模式和可用标志的变量名或字符串文字。
说明
如果 match 方法没有找到匹配,返回 null。如果找到匹配返回一个数组并且更新全局 RegExp 对象的属性以反映匹配结果。
match 方法返回的数组有三个属性:input、index和lastIndex。Input 属性包含整个的被查找字符串。Index 属性包含了在整个被查找字符串中匹配的子字符串的位置。LastIndex 属性包含了最后一次匹配中最后一个字符的下一个位置。
如果没有设置全局标志 (g),数组的0元素包含整个匹配,而第 到 n 元素包含了匹配中曾出现过的任一个子匹配。这相当于没有设置全局标志的 exec 方法。如果设置了全局标志,元素0到n中包含所有匹配。
示例
下面的示例演示了match 方法的用法:
function MatchDemo(){
var r, re; // 声明变量。
var s = "The rain in Spain falls mainly in the plain";
re = /ain/i; // 创建正则表达式模式。
r = s.match(re); // 尝试匹配搜索字符串。
return(r); // 返回第一次出现 "ain" 的地方。
}
本示例说明带 g 标志设置的 match 方法的用法。
function MatchDemo(){
var r, re; // 声明变量。
var s = "The rain in Spain falls mainly in the plain";
re = /ain/ig; // 创建正则表达式模式。
r = s.match(re); // 尝试去匹配搜索字符串。
return(r); // 返回的数组包含了所有 "ain"
// 出现的四个匹配。