用C#开发蜘蛛程序Word格式.docx
- 文档编号:22434127
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:20
- 大小:59.35KB
用C#开发蜘蛛程序Word格式.docx
《用C#开发蜘蛛程序Word格式.docx》由会员分享,可在线阅读,更多相关《用C#开发蜘蛛程序Word格式.docx(20页珍藏版)》请在冰豆网上搜索。
既然要将这个文件作为队列使用,那么就需要对这个文件进行追加行和从文件开始部分读取数据操作。
让我们首先来实现向文件中追加行操作。
实现代码如下:
向文件中追加行的实现代码
//
这两个变量为类全局变量
private
FileStream
fsw;
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
StreamWriter(fsw);
}
关闭写文件流
CloseWriteFile()
(fsr
!
null)
fsw.Close();
向文件中追加一行字符串
WriteLine(string
s)
sw.WriteLine(s);
sw.Flush();
刷新写入缓冲区,使这一行对于读文件流可见
}
在实现上述的代码时要注意,在创建FileStream对象时,必须使用FileShare.ReadWrite,否则这个文件无法被两个或两个以上的Stream打开,也就是说下面要介绍的读文件流将无法操作这个被写文件流打开的文件。
从文件中读取行的实现代码如下:
从文件中读取行的实现代码
//
fsr;
StreamReader
sr;
创建用于读取文件行的文件流和StreamWriter对象
OpenReadFile(string
如果文件不存在,首先创建这个文件
fsr
FileMode.OpenOrCreate,
FileAccess.Read,
FileShare.ReadWrite);
sr
StreamReader(fsr);
关闭读文件流
CloseReadFile()
if(fsr
fsr.Close();
从文件中读取一行
string
ReadLine()
if(sr.EndOfStream)
如果文件流指针已经指向文件尾部,返回null
return
null;
sr.ReadLine();
除了上述的读写文件的代码外,FileIO还提供了一个IsEof方法用来判断文件流指针是否位于文件尾部。
IsEof方法的实现代码如下如下:
IsEof方法的实现代码
用于判断文件流指针是否位于文件尾部
bool
IsEof()
sr.EndOfStream;
FileIO类不仅仅用于对下载队列的读写。
在后面我们还会讲到,网络蜘蛛通过多线程下载网络资源时,每一个线程将自己下载的网络资源保存在属于自己的一个目录中。
每个这样的目录都有一个index.txt文件,这个文件保存了当前目录的网络资源的URL。
向index.txt文件中追加URL也用到了FileIO(index.txt不需要读取,只需要不断地追加行)。
关注此文的读者还看过:
∙·
用C#编程实现读写Binary
无废话C#设计模式之十一:
Composite
无废话C#设计模式之十二:
Bridge
无废话C#设计模式之十三:
Decorator
C#访问修饰符
四、线程类的实现
要想使网络蜘蛛在有限的硬件环境下尽可能地提高下载速度。
最廉价和快捷的方法就是使用多线程。
在.netframework2.0中提供了丰富的线程功能。
其中的核心线程类是Thread。
一般可使用如下的代码创建并运行一个线程:
在C#中使用线程的演示代码
private
fun()
线程要执行的代码
testThread()
Thread
thread;
thread
Thread(fun);
创建一个Thread对象,并将fun设为线程运行的方法
thread.Start();
运行一个线程
虽然上面的代码比较简单地创建并运行了一个线程,但是这段代码看起来仍然不够透明,也就是客户端在调用线程时仍然需要显式地使用Thread类。
下面我们来实现一个用于创建线程的MyThread类。
C#中的任何类只需要继承这个类,就可以自动变成一个线程类。
MyThread类的代码如下:
MyThread类的实现代码
任何C#类继承MyThread后,就会自动变成一个线程类
class
MyThread
Thread
MyThread()
Thread(run);
创建Thread对象
用于运行线程代码的方法,MyThread的子类必须覆盖这个方法
virtual
run()
start()
开始运行线程,也就是开始执行run方法
使当前线程休眠millisecondsTimeout毫秒
sleep(int
millisecondsTimeout)
Thread.Sleep(millisecondsTimeout);
我们可参照如下的代码使用MyThread类:
测试的ThreadClass类的代码
class
ThreadClass
:
override
要执行的线程代码
测试ThreadClass类
testThreadClass()
ThreadClass
tc
ThreadClass();
tc.start();
开始运行线程,也就是执行run方法
各位读者可以看看,上面的代码是不是要比直接使用Thread类更方便、直观、易用,还有些面向对象的感觉!
五、用多线程下载网络资源
一般来说,网络蜘蛛都是使用多线程来下载网络资源的。
至于如何使用多线程来下载,各个版本的网络蜘蛛不尽相同。
为了方便和容易理解,本文所讨论的网络蜘蛛采用了每一个线程负责将网络资源下载到一个属于自己的目录中,也就是说,每一个线程对应一个目录。
而在当前目录中下载的网络资源达到一定的数目后(如5000),这个线程就会再建立一个新目录,并从0开始计数继续下载网络资源。
在本节中将介绍一个用于下载网络资源的线程类DownLoadThread。
这个类的主要功能就是从下载队列中获得一定数量的URL,并进行下载和分析。
在DownLoadThread类中涉及到很多其他重要的类,这些类将在后面的部分介绍。
在这里我们先看一下DownLoadThread类的实现代码。
DownLoadThread类的代码
DownLoadThread
ParseResource类用于下载和分析网络资源
ParseResource
pr
ParseResource();
int
currentCount
0;
当前下载目录中的网页数
用于向每个线程目录中的index.txt中写当前目录的URL
FileIO
fileIO
FileIO();
path;
当前的下载目录(后面带“\"
)
string[]
patterns;
线程不下载符合patterns中的正则表达式的URL
stop
false;
stop为true,线程退出
threadID;
当前线程的threadID,用于区分其他的线程
DownLoadThread(string[]
patterns)
pr.findUrl
+=
findUrl;
为findUrl事件赋一个方法
this.patterns
这是一个事件方法,每获得一个URL时发生
findUrl(string
url)
Common.addUrl(url);
将获得的URL加到下载队列中
openFile()
打开下载目录中的index.txt文件
fileIO.CloseWriteFile();
fileIO.OpenWriteFile(path
+
Common.indexFile);
run()
线程运行方法
LinkedList
urls
LinkedList();
path
Common.getDir();
获得下载目录
openFile();
while
stop)
当下载队列中没有URL时,进行循环等待
&
urls.Count
==
0)
Common.getUrls(urls,
20);
从下载队列中获得20个url
(urls.Count
0)
如果未获得url
通知系统当前线程已处于等待状态,
如果所有的线程都处于等待状态,
说明所有的网络资源都被下载完了
Common.threadWait(threadID);
sleep(5000);
当前线程休眠5秒
StringBuilder
sb
StringBuilder();
foreach
(string
url
in
urls)
循环对这20个url进行循环下载分析
(stop)
break;
如果当前下载目录的资源文件数大于等于最大文件数目时,
建立一个新目录,并继续下载
(currentCount
>
Common.maxCount)
currentCount
目录
每个下载资源文件名使用5位的顺序号保存(没有扩展名),
如00001、00002。
下面的语句是格式化文件名
string
s
string.Format("
{0:
D5}"
1);
sb.Remove(0,
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代码中标签的href属性值。
状态和状态之间会根据从html文件中读入的字符进行切换。
下面是状态之间切换的描述。
状态0:
读入'
<
'
字符后切换到状态1,读入其他的字符,状态不变。
状态1:
a'
或'
A'
,切换到状态2,读入其他的字符,切换到状态0。
状态2:
读入空格或制表符(\t),切换到状态3,读入其他的字符,切换到状态0。
状态3:
,成功获得一个,读入其他的字符,状态不变。
为了更容易说明问题。
在本文给出的网络蜘蛛中只提取了html代码中中的href属性中的url。
本文中所采用的分析方法是分步进行提取href。
首先将html代码中的标签整个提出来。
不包括和前面的字符,如comprg中只提取,而comprg将被忽略,因为这里并没有url。
本文使用了一个状态机来的提取,这个状态机分为五个状态(0至4)。
第一个状态是初始态,最后一个状态为终止态,如果到达最后一个状态,说明已经成功获得了一个
状态机如图1所示。
图1
最后一个双环的状态是最终态。
下面让我们来看看获得的实现代码。
getA方法的实现
获得html中的
getA()
char[]
buffer
char[1024];
int
state
String
a
;
sr.EndOfStream)
n
sr.Read(buffer,
0,
buffer.Length);
for
(int
i
n;
i++)
switch
(state)
case
0:
状态0
(buffer[i]
)
读入的是'
a
buffer[i];
state
1;
切换到状态1
break;
1:
状态1
||
buffer[i]
读入是'
2;
切换到状态2
else
切换到状态0
2:
状态2
\t'
读入的是空格或'
3;
3:
状态3
,已经成功获得一个
getUrl(getHref(a));
获得中的href属性的值
(url
(findUrl
findUrl(url);
引发发现url的事件
在获得一个后,重新切换到状态0
在getA方法中除了切换到状态0外,其他的状态切换都将已经读入的字符赋给String变量a,如果最后发现变量a中的字符串不可能是后,就将a清空,并切换到状态0后重新读入字符。
在getA方法中使用了一个重要的方法getHref来从中获得href部分。
getHref方法的实现如下:
getHref方法的实现
从中获得Href
String
getHref(string
a)
p
@"
href\s*=\s*('
[^'
]*'
|"
[^"
]*"
|\S+\s+)"
获得Href的正则表达式
MatchCollection
matches
Regex.Matches(a,
p,
RegexOptions.IgnoreCase
|
RegexOptions.ExplicitCapture);
(Match
nextMatch
matches)
nextMatch.Value;
返回href
throw
e;
在getHref方法中使用了正则表达式从中获得href。
在中正确的href属性格式有三种情况,这三种情况的主要区别是url两边的符号,如单引号、双引号或没有符号。
这三种情况如下所示:
情况1:
comprg
情况2:
情况3:
getHref方法中的p存储了用于过滤这三种情况的href,也就是说,使用正则表达式可以获得上述三种情况的href如下:
从情况1获得得的href:
href="
http:
//www.co
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 开发 蜘蛛 程序
![提示](https://static.bdocx.com/images/bang_tan.gif)