基于c++搜索引擎网络爬虫设计与实现

获取项目源文件,学习交流联系Q:1415736481,可指导毕设,课设

网络中的资源非常丰富,但是如何有效的搜索信息却是一件困难的事情。建立搜索引擎就是解决这个问题的最好方法。

本文首先详细介绍了基于英特网的搜索引擎的系统结构,然后具体阐述了如何设计并实现搜索引擎的搜索器——网络爬虫。

多线程网络爬虫程序是从指定的Web页面中按照宽度优先算法进行解析、搜索,并把搜索到的每条URL进行抓取、保存并且以URL为新的入口在互联网上进行不断的爬行的自动执行后台程序。

网络爬虫主要应用socket套接字技术、正则表达式、HTTP协议、windows网络编程技术等相关技术,以C++语言作为实现语言,并在VC6.0下调试通过。

在网络爬虫的设计与实现的章节中除了详细的阐述技术核心外还结合了多线程网络爬虫的实现代码来说明,易于理解。本网络爬虫是一个能够在后台运行的以配置文件来作为初始URL,以宽度优先算法向下爬行,保存目标URL的网络程序,能够执行普通用户网络搜索任务。

 

关键词 搜索引擎;网络爬虫;URL搜索器;多线程

Design and Realization of Search Engine Network Spider

Abstract

The resource of network is very rich, but how to search the effective information is a difficult task. The establishment of a search engine is the best way to solve this problem.

This paper first introduces the internet-based search engine structure, and then illustrates how to implement search engine ----­­­­­network spiders.

The multi-thread network spider procedure is from the Web page which assigns according to the width priority algorithm connection for analysis and search, and each URL is snatched and preserved, and make the result URL as the new source entrance unceasing crawling on internet to carry out the backgoud automatically.

My paper of network spider mainly applies to the socket technology, the regular expression, the HTTP agreement, the windows network programming technology and other correlation technique, and taking C++ language as implemented language, and passes under VC6.0 debugging.

In the chapter of the spider design and implementation, besides a detailed exposition of the core technology in conjunction with the multi-threaded network spider to illustrate the realization of the code, it is easy to understand. This network spiders is initial URL based on configuration files which can operate on background,using width priority algorithm to crawl down, preserving network programme of target URL.

 

Keywords  Internet search engine; Network spider; URL search programme; Multithreaded

 

目录

摘要... I

Abstract II

 

第1章 绪论... 1

1.1 课题背景... 1

1.2 搜索引擎的历史和分类... 2

1.2.1 搜索引擎的历史... 2

1.2.2 搜索引擎的分类... 2

1.3 搜索引擎的发展趋势... 3

1.4 搜索引擎的组成部分... 4

1.5 课题研究的主要内容... 4

第2章 网络爬虫的技术要点分析... 6

2.1 网络爬虫Spider工作原理... 6

2.1.1 Spider 的概念... 6

2.1.2 网络爬虫抓取内容分析... 6

2.2 HTTP协议... 7

2.2.1 HTTP协议的请求... 7

2.2.2 HTTP协议的响应... 8

2.2.3 HTTP的消息报头... 8

2.3 SOCKET套接字... 10

2.3.1 什么是SOCKET套接字... 10

2.3.2 SOCKET各函数分析... 10

2.4 正则表达式... 14

2.4.1 正则表达式应用分析... 14

2.4.2 正则表达式的元字符分析... 15

2.5 本章总结... 15

第3章 网络爬虫系统模型的分析和概要设计... 16

3.1 网络爬虫模型分析... 16

3.1.1 单线程爬虫模型分析... 16

3.1.2 多线程爬虫模型分析... 16

3.1.3 爬虫集群模型分析... 17

3.2 网络爬虫的搜索策略的分析与设计... 17

3.3 网络爬虫主要性能评价指标分析... 20

3.4 本论文中网络爬虫的概要设计... 20

第4章 网络爬虫的详细设计与实现... 23

4.1 网络爬虫总体设计... 23

4.2 Socket功能模块的设计与实现... 24

4.2.1 Socket功能模块的设计... 24

4.2.2 Socket功能模块的具体实现... 24

4.2.3 Socket模块中各功能函数模块中的调用关系设计... 31

4.3 HTTP 功能模块的设计与实现... 33

4.3.1 HTTP协议与URL. 33

4.3.2 依照HTTP协议设计send()函数发送信息... 33

4.4 正则表达式过滤模块的设计与实现... 35

4.4.1 URL正则表达式的定义与实现... 35

4.4.2 开源正则表达式引擎DEELX的应用... 37

4.4.3 MatchResult 类的调用和设计... 38

4.5 URL保存模块的设计与实现... 39

4.6 宽度搜索模块的设计与实现... 39

4.7 文件存储模块的设计与实现... 41

4.8 多线程的设计与实现... 42

4.9 运行显示结果... 42

4.9.1 输入... 42

4.9.2 显示... 43

4.9.3 结果... 43

4.10 本章总结... 44

结论... 45

致谢... 46

参考文献... 47

附录... 48

 

  1. 绪论
    1.  课题背景

面对浩瀚的网络资源,搜索引擎为所有网上冲浪的用户提供了一个入口,毫不夸张的说,所有的用户都可以从搜索出发到达自己想去的网上任何一个地方。因此它也成为除了电子邮件以外最多人使用的网上服务。

什么是搜索引擎?它是如何工作的?搜索引擎一词在国内外因特网领域被广泛使用,然而他的含义却不尽相同。在美国搜索引擎通常指的是基于因特网的搜索引擎,他们通过网络机器人程序收集上千万到几亿个网页,并且每一个词都被搜索引擎索引,也就是我们说的全文检索。著名的因特网搜索引擎包括First Search、Google、HotBot等。在中国,搜索引擎通常指基于网站目录的搜索服务或是特定网站的搜索服务,本人这里研究的是基于因特网的搜索技术。

广义的搜索引擎泛指网络(尤其是万维网)上提供信息检索服务的工具或系统,即在因特网上或通过因特网响应用户的搜索请求,返回相应查询结果的信息技术和系统.

狭义的搜索引擎主要指利用网络自动搜索软件或人工方式,对万维网信息资源进行采集,分析与标引,并将索引信息组织成数据库,以网站形式为网络用户提供检索服务的一类信息服务系统.

概括的说:搜索引擎就是WWW网络环境中的一套信息检索系统。它通常有两种不同的工作方式:一种是分类目录型的检索,把因特网中的资源收集起来,由其提供的资源的类型不同而分成不同的目录,再一层层地进行分类,人们要找自己想要的信息可按他们的分类一层层进入,就能最后到达目的地,找到自己想要的信息;另一种是基于关键词(Keyword)的检索,这种方式用户可以用逻辑组合方式输入各种关键词,搜索引擎计算机根据这些关键词寻找用户所需资源的地址,然后根据一定的规则反馈给用户包含此关键字词信息的所有网址和指向这些网址的链接。
  搜索引擎其实也就是一个网站,只不过该网站专门为你提供信息“检索”服务,它使用特有的程序把INTERNET上的所有信息归类以帮助人们在浩如烟海的信息海洋中搜寻到自己所需要的信息。随着因特网信息按几 何级数增长,这些搜索引擎利用其内部的一个spider程序,自动搜索网站每一页的开始,并把每一页上代表超级链接的所有词汇放入一个数据库,供用户来查询。

    1.  搜索引擎的历史和分类

搜索引擎的历史。1990年以前,没有任何人能搜索互联网。所有搜索引擎的祖先,是1990年由 Montreal的McGill University学生Alan Emtage、Peter Deutsch、Bill Wheelan发明的Archie。后来,程序员们开发出了一个名叫“spider”(爬虫)的“Robot”(机器人)程序,它能自动以人类无法达到的速度不断重复地在网络上检索信息。这种行为很像一只爬虫在INTERNET这张巨大的信息网上爬来爬去,因此,spider程序便由此而来。世界上第一个Spider程序,是MIT Matthew Gray的World wide Web Wanderer,用于追踪互联网发展规模。刚开始它只用来统计互联网上的服务器数量,后来发展为也能够捕获网址(URL)[1]。

      1.  搜索引擎的历史

搜索引擎技术伴随着WWW的发展是引人注目的。搜索引擎大约经历了三代的更新发展:

第一代搜索引擎出现于1994年。这类搜索引擎一般都索引少于1,000,000个网页,极少重新搜集网页并去刷新索引。而且其检索速度非常慢,一般都要等 待10秒甚至更长的时间。在实现技术上也基本沿用较为成熟的IR(Information Retrieval)、网络、数据库等技术,相当于利用一些已有技术实现的一个WWW上的应用。在1994年3月到4月,网络爬虫(Spider)World Web Worm (WWW)平均每天承受大约1500次查询。

大约在1996年出现的第二代搜索引擎系统大多采用分布式方案(多个微型计算机协同工作)来提高数据规模、响应速度和用户数量,它们一般都保持一个大约5千万网页个的索引数据库,每天能够响应1千万次 用户检索请求。1997年11月,当时最先进的几个搜索引擎号称能建立从2百万到1亿的网页索引。Altavista搜索引擎声称他们每天大概要承受2千万次查询。

2000年搜索引擎2000年大会上,按照Google公司总裁Larry Page的演讲,Google正在用3千台运行Linux系统的个人电脑在搜集Web上的网页,而且以每天30台的速度向这个微机集群里添加电脑,以保持与网络的发展相同步。每台微机运行多个爬虫程序搜集网页的峰值速度是每秒100个网页,平均速度是每秒48.5个网页,一天可以搜集超过4百万个网页。

      1.  搜索引擎的分类

搜索引擎按其工作方式主要可分为三种,分别是全文搜索引擎(Full Text Search Engine)、目录索引类搜索引擎(Search Index/Directory)和元搜索引擎(Meta Search Engine)。

全文搜索引擎是名副其实的搜索引擎,国外具代表性的有Google、Fast/AllTheWeb、AltaVista、Inktomi、 Teoma、WiseNut等,国内著名的有百度(Baidu)。它们都是通过从互联网上提取的各个网站的信息(以网页文字为主)而建立的数据库中,检索 与用户查询条件匹配的相关记录,然后按一定的排列顺序将结果返回给用户,因此他们是真正的搜索引擎。

目录索引虽然有搜索功能,但在严格意义上算不上是真正的搜索引擎,仅仅是按目录分类的网站链接列表而已。用户完全可以不用进行关键词(Keywords) 查询,仅靠分类目录也可找到需要的信息。目录索引中最具代表性的莫过于大名鼎鼎的Yahoo雅虎。其他著名的还有Open Directory Project(DMOZ)、LookSmart、About等。国内的搜狐、新浪、网易搜索也都属于这一类[2]。

元搜索引擎在接受用户查询请求时,同时在其他多个引擎上进行搜索,并将结果返回给用户。著名的元搜索引擎有InfoSpace、Dogpile、Vivisimo等(元搜索引擎列表),中文元搜索引擎中具代表性的有搜星搜索引擎。在搜索结果排列方面,有的直接按来源引擎排列搜索结果,如Dogpile,有的则按自定的规则将结果重新排列组合,如Vivisimo。

    1.  搜索引擎的发展趋势

搜索引擎经过几年的发展和摸索,越来越贴近人们的需求,搜索引擎的技术也得到了很大的发展。搜索引擎的最新技术发展包括以下几个方面:

为了提高搜索引擎对用户检索提问的理解,就必须有一个好的检索提问语言,为了克服关键词检索和目录查询的缺点,现在已经出现了自然语言智能答询。用户可以输入简单的疑问句,比如“how can kill virus of computer?”。搜索引擎在对提问进行结构和内容的分析之后,或直接给出提问的答案,或引导用户从几个可选择的问题中进行再选择。自然语言的优势在于,一是使网络交流更加人性化,二是使查询变得更加方便、直接、有效。

1.基于链接评价的搜索引擎

基于链接评价的搜索引擎的优秀代表是Googel,它独创的“链接评价体系”是基于这样一种认识,一个网页的重要性取决于它被其它网页链接的数量,特别是一些已经被认定是“重要”的网页的链接数量。

2.基于访问大众性的搜索引擎
    基于访问大众性的搜索引擎的代表是direct hit,它的基本理念是多数人选择访问的网站就是最重要的网站。根据以前成千上万的网络用户在检索结果中实际所挑选并访问的网站和他们在这些网站上花费的时间来统计确定有关网站的重要性排名,并以此来确定哪些网站最符合用户的检索要求。因此具有典型的趋众性特点。这种评价体制与基于链接评价的搜索引擎有着同样的缺点。
    3.去掉检索结果中附加的多余信息

有调查指出,过多的附加信息加重了用户的信息负担,为了去掉这些过多的附加信息,可以采用用户定制、内容过滤等检索技术。

确定搜索引擎信息搜集范围,提高搜索引擎的针对性

    1.  搜索引擎的组成部分

一个搜索引擎大致由三部分组成。第一部分是搜索器,也就是上面提到的Spider程序。它定期的自动爬到各个网站上,把网页抓下来,并顺着上面的链接,象爬虫一样爬开去,持续不断的抓取网页。第二部分是索引器,它把爬虫程序抓来网页进行分析,按照关键词句进行索引,并存入服务器的数据库中。

第三部分是面向用户的检索器,它接收用户提交的查询字串,在索引数据库中查询,并将结果反馈给用户。

因此,准确的说,当我们利用搜索引擎搜索信息时,并不是真正在网上进行搜索,而是在检索那个由爬虫程序自动建立起来的庞大的数据库。由于各大搜索引擎的数据库的自动更新周期是不同的,从几天到几周甚至一个月都有可能,因此,选择合适的搜索引擎显得尤为重要。同时,有时也可能遇到用搜索引擎搜索到的网页无法打开的情况,了解了它的工作原理,这一点也就不会大惊小怪了[3]。

    1.  课题研究的主要内容

    本论文主要研究搜索引擎的搜索器(Spider程序)的设计与实现,详细介绍Spider程序的概念及技术要点,算法介绍,并用C++语言实现简单的可在后台自动运行的爬虫程序

网络爬虫程序,是一种专业的Bot程序。用于查找大量的Web页面。它从一个简单Web页面上开始执行,然后通过其超链接在访问其他页面,如此反复理论上可以扫描互联网上的所有页面。基于因特网的搜索引擎是Spider的最早应用。例如搜索巨头Google公司,就利用网络机器人程序来遍历Web站点,以创建并维护这些大型数据库。网络机器人还可以通过扫描Web站点的主页来得到这个站点的文件清单和层次机构。还可以扫描出中断的超链接和拼写错误等。

然而Internet是建立在很多相关协议基础上的,而更复杂的协议又建立在系统层协议之上。Web就是建立在HTTP ( Hypertext Transfer Protocol ) 协议基础上,而HTTP又是建立在TCP/IP ( Transmission Control Protocol / Internet Protocol ) 协议之上,它同时也是一种Socket协议。

所以网络爬虫本质上是一种基于Socket的网络程序。

本论文中网络爬虫的关键技术如下:SOCKET套接字技术,HTTP协议技术,正则表达式技术。以多线程网络爬虫模型为设计基础,应用SOCKET套接字相关函数按照HTTP协议向源URL发送请求,并以正则表达式过滤所接受到的网页标签,得到目标URL,再将目标URL重新作为源URL进行新的一轮向下爬行搜索,按照宽度优先算法向下爬行,从而完成整个Network Spider的爬行工作。

总的来说本论文的网络爬虫是一个能够在后台运行的以配置文件来作为初始URL,以宽度优先算法向下爬行,保存目标URL的网络爬虫程序。

  1. 网络爬虫的技术要点分析
    1.  网络爬虫Spider工作原理
      1.  Spider 的概念

搜索引擎一直专注于提升用户的体验度,其用户体验度则反映在三个方面: 准、全、快 。用专业术语讲是:查准率、查全率和搜索速度(即搜索耗时)。其中最易达到的是搜索速度,因为对于搜索耗时在1秒以下的系统来说,访问者很难辨别其快慢 了,更何况还有网络速度的影响。因此,对搜索引擎的评价就集中在了前两者:准、全。中文搜索引擎的“准”,需要保证搜索的前几十条结果都和搜索词十分相关,这需由“分词技术”和“排序技术”来决定。

中文搜索引擎的“全”则需保证不遗漏某些重要的结果,而且能找到最新的网页,这需要搜索引擎有一个强大的网页收集器,一般称为“网络爬虫”,也有叫“网页机器人”。这就是本论文所研究的目标。

网络爬虫即Network Spider,是一个很形象的名字。把互联网比喻成一个爬虫网,那么Spider就是在网上爬来爬去的爬虫。网络爬虫是通过网页的链接地址来寻找网页,从网站某一个页面(通常是首页)开始,读取网页的内容,找到在网页中的其它链接地址,然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页都抓取完为止。如果把整个互联网当成一个网站,那么网络爬虫就可以用这个原理把互联网上所有的网页都抓取下来。

对于搜索引擎来说,要抓取互联网上所有的网页几乎是不可能的,从目前公布的数据来看,容量最大的搜索引擎也不过是抓取了整个网页数量的百分之四十左右。这其中的原因一方面是抓取技术的瓶颈,无法遍历所有的网页,有许多网页无法从其它网页的链接中找到;另一个原因是存储技术和处理技术的问题,如果按照每个页 面的平均大小为20K计算(包含图片),100亿网页的容量是100×2000G字节,即使能够存储,下载也存在问题(按照一台机器每秒下载20K计算, 需要340台机器不停的下载一年时间,才能把所有网页下载完毕)。同时,由于数据量太大,在提供搜索时也会有效率方面的影响。因此,许多搜索引擎的网络爬虫只是抓取那些重要的网页,而在抓取的时候评价重要性主要的依据是某个网页的链接深度。

      1.  网络爬虫抓取内容分析

1.静态网页抓取内容分析:

爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。

2.动态网页抓取内容分析:

分析动态网页参数,按照一定规章,“拼”出所有要被抓取内容URL,只抓取这些特定范围内动态网页。

3.特殊内容抓取内容分析:

比如RSS、XML数据,情况特殊需特殊处理。如新闻的滚动新闻页面,需要爬虫不停地监控扫描,发现新内容马上就进行抓取。

4.文件对象抓取内容分析:

图片,MP3、Flash、视频等文件的抓取,都要特殊处理。比如说:图片抓取出来后,要知道图片文件类型、图片文件的大小、图片的像素大小,还要转换出来缩略图。

    1.  HTTP协议
      1.  HTTP协议的请求

http请求由三部分组成,分别是:请求行、消息报头、请求正文

请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF 

其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。

请求方法(所有方法全为大写)有多种,请求方法解释表如表2-1所示。

表2-1 请求方法表

GET

请求获取Request-URI所标识的资源

POST

在Request-URI所标识的资源后附加新的数据

HEAD

请求获取由Request-URI所标识的资源的响应消息报头

PUT

请求服务器存储一个资源,并用Request-URI作为其标识

DELETE

请求服务器删除Request-URI所标识的资源

TRACE

请求服务器回送收到的请求信息,主要用于测试或诊断

应用举例:

GET方法:在浏览器的地址栏中输入网址的方式访问网页时,浏览器采用GET方法向服务器获取资源。

POST方法要求被请求服务器接受附在请求后面的数据,常用于提交表单。

HEAD方法与GET方法几乎是一样的,对于HEAD请求的回应部分来说,它的HTTP头部中包含的信息与通过GET请求所得到的信息是相同的。利用这个方法,不必传输整个资源内容,就可以得到Request-URI所标识的资源的信息。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新。

在本论文中网络爬虫的http请求部分将用GET方法。

      1.  HTTP协议的响应

HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文

状态行格式如下:

HTTP-Version Status-Code Reason-Phrase CRLF。

其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:

1xx:指示信息--表示请求已接收,继续处理

2xx:成功--表示请求已被成功接收、理解、接受

3xx:重定向--要完成请求必须进行更进一步的操作

4xx:客户端错误--请求有语法错误或请求无法实现

5xx:服务器端错误--服务器未能实现合法的请求

响应报头与响应正文略。

      1.  HTTP的消息报头

HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。

HTTP消息报头包括普通报头、请求报头、响应报头、实体报头。

每一个报头域都是由名字+“:”+空格+值 组成,消息报头域的名字是大小写无关的。

1.普通报头

在普通报头中,有少数报头域用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息。

请求时的缓存指令包括:no-cache(用于指示请求或响应消息不能缓存)、no-store、max-age、max-stale、min-fresh、only-if-cached;

响应时的缓存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage.

2.请求报头

请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。

常用的请求报头包括:

Accept:

Accept请求报头域用于指定客户端接受哪些类型的信息。

Accept-Charset:

Accept-Charset请求报头域用于指定客户端接受的字符集。

Accept-Encoding:

Accept-Encoding请求报头域类似于Accept,但是它是用于指定可接受的内容编码。eg:Accept-Encoding:gzip.deflate.如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。

Accept-Language:

Accept-Language请求报头域类似于Accept,但是它是用于指定一种自然语言。

Authorization:

Authorization请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。

Host(发送请求时,该报头域是必需的):

Host请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的。

3.响应报头

响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。

常用的响应报头包括:

Location:

Location响应报头域用于重定向接受者到一个新的位置。Location响应报头域常用在更换域名的时候。

Server:

Server响应报头域包含了服务器用来处理请求的软件信息。与User-Agent请求报头域是相对应的。

4.实体报头

请求和响应消息都可以传送一个实体。一个实体由实体报头域和实体正文组成,但并不是说实体报头域和实体正文要在一起发送,可以只发送实体报头域。实体报头定义了关于实体正文和请求所标识的资源的元信息。

常用的实体报头包括:

Content-Encoding:

Content-Encoding实体报头域被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。Content-Encoding这样用于记录文档的压缩方法。

Content-Language:

Content-Language实体报头域描述了资源所用的自然语言。没有设置该域则认为实体内容将提供给所有的语言阅读者。

Content-Length:

Content-Length实体报头域用于指明实体正文的长度,以字节方式存储的十进制数字来表示。

Content-Type:

Content-Type实体报头域用语指明发送给接收者的实体正文的媒体类型。

Expires:

Expires实体报头域给出响应过期的日期和时间[6]。

    1.  SOCKET套接字
      1.  什么是SOCKET套接字

简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。

区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目标IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。 Socket原意是“插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。

      1.  SOCKET各函数分析

1.WSAStartup函数

int WSAStartup

(

WORD wVersionRequested,

LPWSADATA lpWSAData

);

使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中 高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操 作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的 Socket库中的其它Socket函数了。该函数执行成功后返回0。

2.WSACleanup函数

int WSACleanup (void);

应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

3.socket函数
SOCKET socket

(
int af,
int type,
int protocol

);

应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该 参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数 指定应用程序所使用的通信协议。该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表,但是套接字数据结构都是在操作系统的内核缓冲里。

4.closesocket函数
    int closesocket( SOCKET s );

closesocket函数用来关闭一个描述符为s套接字。由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作 系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符 指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接 字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1。
    closesocke函数如果执行成功就返回0,否则返回SOCKET_ERROR。

5.send函数

int send

(
SOCKET s,
const char FAR *buf,
int len,
int flags

);

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用 send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际 要发送的数据的字节数;第四个参数一般置0。这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区 的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的 发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发 送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里。

6.recv函数

int recv

(

SOCKET s,

char FAR *buf,

int len,

int flags
    );

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓 冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。这里只描述同步Socket的recv函数的执行流 程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么 recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收 缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中。

7.bind函数

int bind

(
SOCKET s,
const struct sockaddr FAR *name,
int namelen

);

当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址 和一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构。

8.listen函数

int listen( SOCKET s, int backlog );
    服务程序可以调用listen函数使其流套接字s处于监听状态。处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR。

9.accept函数

SOCKET accept

(
        SOCKET s,
        struct sockaddr FAR *addr,
        int FAR *addrlen
    );
    服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接 字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。

10.connect函数

int connect

(

SOCKET s,

const struct sockaddr FAR *name,

int namelen
    );

客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR[7]。

    1.  正则表达式

网络爬虫程序抓取到网页之后,需要将HTML页面的所有URL连接提取出来,这就需要应用正则表达式来区分页面中的其他标签。

简单的说,正则表达式是一种可以用于模式匹配和替换的强有力的工具。他可以让用户通过使用一系列的特殊字符构建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据比较对象中是否包含匹配模式,执行相应的程序。

举例来说,正则表达式的一个最为普遍的应用就是用于验证用户在线输入的邮件地址的格式是否正确。如果通过正则表达式验证用户邮件地址的格式正确,用户所填写的表单信息将会被正常处理;反之,如果用户输入的邮件地址与正则表达的模式不匹配,将会弹出提示信息,要求 用户重新输入正确的邮件地址。由此可见正则表达式在WEB应用的逻辑判断中具有举足轻重的作用。

在本论文的网络爬虫的实现部分将应用正则表达式来判断URL。

      1.  正则表达式应用分析

正则表达式简称RE(regular expressions),就是用某种模式去匹配一类字符串的公式。

正则表达式被各种文本编辑软件、类库(例如Rogue Wave的tools.h++)、脚本工具(象awk/grep/sed)广泛的支持,而且像Microsoft的Visual C++这种交互式IDE也开始支持它了。正则表达式由一些普通字符和一些元字符(metacharacters组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,在最简单的情况下,一个正则表达式看上去就是一个普通的查找串。

例如,正则表达式"testing"中没有包含任何元字符,它可以匹配"testing"和"123testing"等字符串,但是不能匹配"Testing"。

      1.  正则表达式的元字符分析

常用正则表达式的元字符解释如表2-2所示。

表2-2 正则表达式的元字符

元字符

描述

.

匹配任何单个字符。 

$

匹配行结束符。

^

匹配一行的开始。

*

匹配0或多个正好在它之前的那个字符。

\

引用符将列出的元字符当作普通的字符来匹配。

[ ] 
[c1-c2]
[^c1-c2]

匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配ratrotrut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,

\< \>

匹配词(word)的开始(\<)和结束(\>)。

\( \)

将 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1\9 的符号来引用。

|

将两个匹配条件进行逻辑“或”(Or)运算。

+

匹配1或多个正好在它之前的那个字符。

?

匹配0或1个正好在它之前的那个字符。

\{i\}
\{i,j\}

匹配指定数目的字符,这些字符是在它之前的表达式定义的。

    1.  本章总结

本论文中网络爬虫的设计,应用了HTTP协议,SOCKET套接字编程,以及正则表达式的相关技术。采用多线程网络爬虫模型和宽度优先的搜索策略。已达到抓取网页、提取目标URL及在网络上不断爬行的网络爬虫的功能。

  1. 网络爬虫系统模型的分析和概要设计
    1.  网络爬虫模型分析
      1.  单线程爬虫模型分析

首先建立URL任务列表,即初始URL种子,由URL任务列表向DNS Cache发送请求,DNS Cache发送URL列表中的初始URL 种子到DNS,在由DNS按照http协议发送到互联网上按照一定算法和排序方式搜索页面,然后对页面按照一定算法进行分析,并提取相关URL,最后将所得URL返回任务列表,并以新的URL作为种子再次发送到DNS Cache中,进行新的页面抓取和URL 提取。从而使网络爬虫进行循环运行。(DNS DNS 是域名系统(Domain Name System) 的缩写,该系统用于命名组织到域层次结构中的计算机和网络服务。也就是进行域名解析的服务器;Cache高速缓冲存储器一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问。单线程网络爬虫模型示意图如图3-1所示。

图3-1单线程网络爬虫模型图

      1.  多线程爬虫模型分析

URL任务列表,即初始URL种子,把初始种子保存在临界区中,然后利用多线程技术,每一个线程发送一个请求,多个爬虫线程在互联网上同时爬行,按照一定搜索算法抓取网页并提取URL返回到临届区中,临届区再将所得URL传回URL列表作为新的种子开始新的一轮多线程的搜索抓取网页和提取URL,从而使整个爬虫程序循环运行下去。多线程网络爬虫示意图如图3-2所示。

图3-2多线程网络爬虫示意图

      1.  爬虫集群模型分析

URL任务列表,将初始URL种子传给Spider管理器,Spider管理器将每一个种子一一分配给一个Spider程序,每一个Spider程序作为一个单独的进程在互联网上爬行抓取网页并且按照相关算法提取URL并将其传回Spider管理器,Spider管理器按照一定算法将所得URL进行消重,有机排列,得到目标URL,再将目标URL传回URL列表作为新的URL种子开始进行新的一轮新的多线程的搜索抓取网页和提取URL。从而使整个爬虫程序循环运行下去。爬虫集群示意图如3-3所示。

图3-3 爬虫集群示意图

单线程模式的工作效率、搜索速度较低;集群模式占用资源较多,功能强大但实现较为复杂。而多线程模型速度快,效率较高,而占用资源相对集成模型少。所以在本论文中我使用多线程模式构造网络爬虫程序。

    1.  网络爬虫的搜索策略的分析与设计

1.IP地址搜索策略分析

先赋予爬虫一个起始的IP地址,然后根据IP地址递增的方式搜索本IP地址段后的每一个WWW地址中的文档,它完全不考虑各文档中指向其它Web 站点的超级链接地址。优点是搜索全面,能够发现那些没被其它文档引用的新文档的信息源;缺点是不适合大规模搜索。

2.深度优先搜索策略分析

深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。优点是能遍历一个Web 站点或深层嵌套的文档集合;缺点是因为Web结构相当深,,有可能造成一旦进去,再也出不来的情况发生。这就是所谓的爬虫的陷入(trapped)问题。如图3-4 所示,如果按照深度优先搜索算法则搜索顺序为A-F-G;E-H-I … …。

图3-4网页搜索示例图

3.广度优先搜索策略分析

广度优先又称为宽度优先,在广度优先搜索中,先搜索完一个Web 页面中所有的超级链接,然后再继续搜索下一层, 直到底层为止。例如,一个HTML 文件中有三个超链,选择其中之一并处理相应的HTML文件,然后不再选择第二个HTML文件中的任何超链, 而是返回并选择第二个超链,处理相应的HTML文件,再返回,选择第三个超链并处理相应的HTML文件。一旦一层上的所有超链都己被选择过,就可以开始在刚才处理过的HIML 文件中搜索其余的超链。这就保证了对浅层的首先处理。当遇到一个无穷尽的深层分支时,不会导致陷进WWW 中的深层文档中出现出不来的情况发生。宽度优先搜索策略还有一个优点,即它能在两个HTML文件之间找到最短路径。宽度优先搜索策略通常是实现爬虫的最佳策略,因为它容易实现,而且具备大多数期望的功能。但是如果要遍历一个指定的站点或者深层嵌套的HTML文件集,用宽度优先搜索策略则需要花费比较长的时间才能到达深层的HTML文件。综合考虑以上几种策略和国内信息系统搜索信息的特点,国内一般采用以宽度优先搜索策略为主、其他搜索策略为辅的搜索策略。对于某些不被引用的或很少被引用的HTML文件,宽度优先搜索策略可能会遗漏这些孤立的信息源,可以用其他搜索策略作为它的补充。

本文实现的网络爬虫程序也是以广度优先作为搜索策略。如果图3-4按照广度优先顺序搜索,则搜索顺序为A—B、C、D、E、F—H、G—I。

 

由于不可能抓取所有的网页,有些网络爬虫对一些不太重要的网站,设置了访问的层数。例如,在上图中,A为起始网页,属于0层,B、C、D、E、F属于第1 层,G、H属于第2层,I属于第3层。如果网络爬虫设置的访问层数为2的话,网页I是不会被访问到的。这也让有些网站上一部分网页能够在搜索引擎上搜索到,另外一部分不能被搜索到。

4.专业搜索引擎的爬虫策略分析

目前,专业搜索引擎网络爬虫通常采用“最好优先”原则访问WEB,即为快速、有效地获得更多的与主题相关的页面(简称“回报”),每次选择“最有价值”的链接进行访问。由于链接包含于页面之中,而通常具有较高价值的页面包含的链接也具有较高的价值,因而对链接价值的评价有时也转换为对页面价值的评价。与通用搜索引擎不同,专业搜索引擎服务于特定人群,其索引内容只限于特定主题或专门领域,因此无需对整个WEB进行遍历,只需选择与主题相关页面进行访问。通用搜索引擎搜索示意图如图3-5 所示。白框代表与主题无关的页面,黑框代表与主体相关的页面,黑箭头代表访问顺序。

图3-5 通用搜索引擎搜索顺序示意图

专业搜索引擎搜索示意图如图3-6所示。白框代表与主题无关的页面,黑框代表与主体相关的页面,黑箭头代表访问顺序,白箭头代表无关访问。

图3-6 专业搜索引擎搜索示意图

    1.  网络爬虫主要性能评价指标分析

对于任何需要大规模抽取网络数据信息的网络爬虫程序,都应该考虑如下几个问题。

1.灵活性:

任何运行于复杂网络环境的网络爬虫,都需要对平台软硬件资源、网络性能具有良好自适应能力,以及对不同性能需求有相应的调节能力。

2.健壮性:

爬虫系统需要有很强的容错能力,主要包括处理不规范的HTML 代码以及各类异常,应对服务器短和客户端的异常行为(如跳转)、选择适合的传输协议(HTTP/HTTPS)等,以求将损失减到最低。

3.可维护和可配置性

系统要有良好的人机接口能够监控系统运行状况与进程统计并保存目标URL,系统中止运行等。

    1.  本论文中网络爬虫的概要设计

本网络爬虫的开发目的,通过网络爬虫技术一个自动提取网页的程序,实现搜索引擎从自己想要访问的网上下载网页,再根据已下载的网页上继续访问其它的网页,并将其下载直到满足用户的需求。

根据现实中不同用户的实际上的各种需求,本项目简单实现网络中的搜索引擎,本网络爬虫需要达到如下几个目标:

1.设计基于多线程的网络爬虫,通过socket套接字实现客户端对服务器的访问,客户端向服务器发送自己设定好请求。如图3-7所示。

 

 

 

 

 

 

URL配置文件

 

 

 

 

 

 

URL配置文件列表

 

 

 

 

 

 

 

 

 

 

 

 

 

互联网

 

线程1搜索元URL如www.baidu.com

 

线程2搜索元URL如www.baidu.com

 

线程N

 

图3-7 多线程网络爬虫概要设计图模型

2.通过 http将Web服务器上协议站点的网页代码提取出来。

3.根据一定的正则表达式提取出客户端所需要的信息。

4.广度优先搜索可从网页中某个链接出发,访问该链接网页上的所有链接,访问完成后,再通过递归算法实现下一层的访问。

网络爬虫工作流程图如图3-8所示。

一般情况下,基于Web的全文搜索引擎均由页面搜集器、页面索引器、页面检索器等三个主要部分组成,如图3-9所示。

基于对用例图的分析,其中页面搜集器和页面索引器是搜索引擎最为核心的模块,也是本项目的核心关键,结合本项目的自身的特点,故将本项目设计是一个基于后台运行的项目,是一个功能很强大的程序,它会根据预先设定的网址去查询对应的网页,根据该网页中的链接继续去访问。网络爬虫访问页面的过程就是对网上信息遍历的过程,从而满足客户的需求。

本论文网络爬虫的实现,能方便客户掌握搜索工作的进度把握,确保搜索引擎的质量,同时,还省去了人为下载的繁琐,节省了时间,使搜索更加自动化,人性化。

  本网络爬虫最终将设计成一个能够自动读写配置文件并且在后台自动执行的网络爬虫程序。

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

图3-8 网络爬虫工作流程图

图3-9 搜索引擎组成示意图

 

 

 

  1. 网络爬虫的详细设计与实现
    1.  网络爬虫总体设计

根据本网络爬虫的概要设计本网络爬虫是一个自动提取网页的程序,实现搜索引擎从自己想要访问的网上下载网页,再根据已下载的网页上继续访问其它的网页,并将其下载直到满足用户的需求。

根据现实中不同用户的实际上的各种需求,本项目简单实现网络中的搜索引擎,本网络爬虫的基本流程如下:

Main函数流程图如图4-1所示。

图4-1网络爬虫main函数流程图

1.设计基于多线程的网络爬虫,该爬虫程序通过socket套接字实现客户端对服务器的访问,客户端向服务器发送自己设定好请求。

2.通过 http将Web服务器上协议站点的网页代码提取出来。

3.根据一定的正则表达式提取出客户端所需要的信息。

4.宽度优先搜索可从网页中某个链接出发,访问该链接网页上的所有链接,访问完成后,再通过递归算法实现下一层的访问。

总的来说爬虫程序从配置文件中读出URL任务列表,即初始URL种子,把初始种子保存在临界区中,然后利用多线程技术,每一个线程发送一个请求,多个爬虫线程在互联网上同时爬行,按照广度搜索运算法搜索抓取网页并提取URL返回到临届区中,通过正则表达式过滤所得的URL保存在临届区再将所得URL传回URL列表作为新的种子应用递归算法开始新的一轮多线程的搜索抓取网页和提取URL,从而使整个爬虫程序循环运行下去。

    1.  Socket功能模块的设计与实现
      1.  Socket功能模块的设计

Socket原意是 “插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通 过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。

Socket功能模块是网络爬虫依赖的背景知识,存在于知识管理系统的结构中,客户通过socket套接字与服务端建立起连接。客户端与服务器建立连接如图4-2所示。

 

客户端创建一个进行网络通信的套接字

 

客户端用connect函数与服务器建立连接

 

端用send函数向服

 

 

客户端用recv函数接收数据

 

 

 
 

 

 

 

 

 

 

 

 

        图4-2 socket 功能模块流程图

      1.  Socket功能模块的具体实现

为了能够实现Socket功能,本论文设计了Socket类,在Csocket类中共设计有5个函数,类的结构如图4-3所示。

图4-3 Csocket类图

        1.  SOCKET Create()函数的设计与实现

SOCKET Create(int af, int type, int protocol) 函数主要功能是应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。Create函数声明及说明如表4-1所示。

表4-1 Create( )函数的声明及说明

函数声明

返回值类型

说明

SOCKET Create(int af, int type, int protocol)

SOCKET

应用程序调用Create函数来创建一个能够进行网络通信的套接字

功能:使用前创建一个新的套接字
格式:SOCKET Create(int af, int type, int protocol)
参数:

af: 通信发生的区域
type: 要建立的套接字类型
procotol: 使用的特定协议

在应用Create创建SOCKET连接时要应用WSAStartup( )函数和WSACleanup( )函数来创建和验证版本号。

使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。

应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

Create()函数在论文中的具体调用图实现如图4-4所示。

图4-4 Create( )函数在论文中的具体调用图

Create ( )函数接口如表4-2所示。

表4-2 Create( )函数接口表

 

函数名

Create( );

功能

创建一个套接字

记述形式

SOCKET Create( int af, int type, int protocol );

参数

 

类型

变量名

I/O

说明

int

af

I

所创建套接字的地址标识

int

type

I

套接字的类型

int

protocol

I

用来表示地址的特定参数

返回值

类型

SOCKET

说明

NULL

WSAStartup( )调用错误

详细说明

创建一个套接字用于进行与服务器的连接接收发送操作

        1.  CConnect( )函数的设计与实现

客户程序调用connect函数来使客户与监听于name所指定的计算机的特定端口上的服务Socket进行连接。如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR。

int CConnect(SOCKET client, string dest)函数的主要功能是客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。CConnect函数声明及说明如表4-3所示。

表4-3 CConnect( )函数声明及说明

函数声明

返回值类

int CConnect

(SOCKET client, string dest)

int

客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接

功能:完成连接工作

格式:int CConnect(SOCKET client, string dest)

参数:

client:是由socket()调用返回的并且未作连接的套接字描述符。

CConnect函数接口说明如表4-4所示。

 

表4-4 CConnect( )函数接口表

 

函数名

CConnect();

功能概要

连接到服务器

记述形式

Int CConnect( SOCKET client, string dest );

参数

 

类型

变量名

I/O

说明

SOCKET

Client

I

多创建的套接字

String

dest

O

 

返回值

类型

int

说明

-1

连接不成功,输出connect error

0

连接成功

详细说明

1.通过创建的套接字与服务器进行连接;

2.参数为创建的套接字的名字和连入服务器的名字。

注意事项

所需要输入的服务器地址必须小于50字符。

对应用实现中的说明如下:

在应用connect连接服务器与客户端时,要调用sockaddr_in地址解析函数

地址结构说明:
struct sockaddr_in
{
    short sin_family;        //AF_INET
    u_short sin_port;        //16位端口号,网络字节顺序
    struct in_addr sin_addr;   //32位IP地址,网络字节顺序
    char sin_zero[8];        //保留
}

定义参数result;

result = connect(client, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

说明:

client:所创建的socket连接。

(SOCKADDR*)&addrSrv:主机的地址。

sizeof(SOCKADDR):地址的长度。

然后对参数result建立判断,如果返回值为0,则终止socket套接字创建调用WSACleanup()函数解除与Socket库的绑定并且释放Socket库所占用的系统资源。

        1.  CClose( )函数模块的设计与实现

void CClose(SOCKET client)用来关闭套接字。CClose( )函数说明及声明如表4-5所示。

 

表4-5 CClose( )函数声明及说明表

函数声明

返回值类

void CClose(SOCKET client)

void

客户程序调用CCloset函数来关闭Socket进行连接

功能:关闭套接字client
格式:void CClose(SOCKET client);

CClose函数在论文中的应用如下:

void Csocket::CClose(SOCKET client)

{

          closesocket(client);

          WSACleanup( );

}

说明:

调用closesocket()函数关闭socket连接;调用WSACleanup( )函数释放socket所占用的资源。

CClose( )函数接口如表4-6所示。

表4-6 CClose( )函数接口表

 

函数名

CClose();

 

功能概要

关闭套接字

 

记述形式

void CClose( SOCKET client );

 

参数

 

 

类型

变量名

I/O

说明

 

SOCKET

Client

I

所创建的套接字

 

 

返回值

类型

int

说明

 

0

成功关闭套接字client

详细说明

关闭创建的套接字

 

                 
        1.  CSend( )函数模块的设计与实现

CSend(SOCKET client, const char* sendbuf, int len) 函数的主要功能是程序用send函数向服求。CSend( )函数声明及说明如表4-7所示。

       表4-7 CSend( )函数声明及说明表

函数声明

返回值类

intCSend(SOCKET client, constchar*sendbuf,int len)

int

程序用send函数向服

说明:

功能:数据的发送
格式:int CSend(SOCKET client, const char* sendbuf, int len);

参数:

Sendbuf:指向存有传输数据的缓冲区的指针;

len:发送数据的长度。

CSend( )函数的接口如表4-8所示。

表4-8 CSend( )函数接口表

 

函数名

CSend();

功能概要

发送数据到服务器

记述形式

Int Csend( SOCKET client, const char* sendbuf, int len )

参数

 

类型

变量名

I/O

说明

SOCKET

Client

I

所创建的套接字

const  char *

sendbuf

O

保存http的请求包

int

len

O

所发送的数据长度

返回值

类型

int

说明

 

发送的字符数

详细说明

向服务器发送由用户输入的目标地址的数据

CSend函数在本论文中的实现如下所示:

int Csocket::CSend(SOCKET client, const char* sendbuf, int len)

{

             int result;

             result = send (client , sendbuf , len , 0);

             return result;

}

说明:

CSend(SOCKET client, const char* sendbuf, int len)向服务器发送数据请求。设立参数result,返回发送请求。

        1.  CRecv( )函数模块的设计与实现

void CRecv(SOCKET client, FILE* fp)函数的功能是接受服务器所返回的数据信息。函数CRecv( )声明及说明如表4-9所示。

表4-9 CRecv( )函数声明及说明表

函数声明

返回值类型

说明

voidCRecv(SOCKET client, FILE* fp)

void

户应程序都用CRecv函数从TCP连接的另一端接收数据

说明:

功能:数据的接受
格式:void CRecv(SOCKET client, FILE* fp)

参数:

Client:所创建的socket套接字链接;

Fp:创建用来保存目标URL的文本文件。

函数CRecv( )接口如表4-10所示:

表4-10 CRecv( )函数接口表

 

函数名

CRecv();

功能概要

接收由服务器发送过来的数据

记述形式

void CRecv( SOCKET client, FILE* fp );

参数

 

类型

变量名

I/O

说明

SOCKET

Client

I

客户端所创建的套接字

FILE*

fp

O

保存接受到的目标URL

返回值

类型

void

说明

-1

没有接收到数据或者数据不符合要求

 

如果数据正常则可以正常过滤并保存

详细说明

1.该函数的目的是接收服务器发送过来的数据,并储存在字符数组中;同时将接收到的数据与正则表达式进行匹配,将匹配结果存储到文件当中。

2.fp为目标文件名。

      1.  Socket模块中各功能函数模块中的调用关系设计

面向连接的套接字系统调用总体流程如图4-5所示。

在面向连接的套接字的系统调用中,服务器方调用顺序如下

1.应用socket( )函数建立套接字并返回套接字号。

2.应用bind( )函数绑定本地地址。

3.调用listen( )函数监听是否有客户方向服务器发出连接请求。

4.调用accept( )接收连接,并等待客户端的连接。

5.建立accept( )返回得到新的套接字。

6.接收客户端的connect连接并调用send( )\recv( )函数在套接字ns上读写数据,直到完成交换。

7.调用closesocket( )函数,关闭套接字ns。

 

 

 

 

 

 

Socket()建立流示套接字,返回套接字号s

 

Bind()套接字s与本地地址相连

 

listen( )函数监听是否有客户方向服务器发出连接请求

 

建立连接,accept()返回得到新的套接字如ns

 

accept( )接收连接,并等待客户端的连接

 

recv()/send()在套接字ns上读写数据,直到完成交换。

 

closesocket(),关闭套接字ns

 

closesocket(),关闭最初套接字s,服务结束。

 

Socket()建立流示套接字,返回套接字号s

 

Connect(),将套接字s与远程主机连接。

 

send()/recv()在套接字上读写数据,直到完成交换。

 

closesocket(),关闭套接字s,服务结束TCP对话。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

图4-5 面向连接的套接字系统调用总体流程图

8.循环执行第三步调用listen( )函数监听客户端是否有connect请求。循环执行整个程序。

客户段的函数调用顺序如下:

1.客户端程序调用socket( )函数建立流式套接字s。

2.调用connect( )函数建立与服务器主机的连接。

3.调用send( )函数和recv( )函数,在套接字s上写/读数据。

4.调用closesocket( ),关闭套接字,结束TCP对话。

本论文的网路爬虫程序是一个在后台运行的客户端程序,调用流程按照照客户端的调用顺序。

    1.  HTTP 功能模块的设计与实现
      1.  HTTP协议与URL

HTTP(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。

HTTP URL (URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息)的格式如下:

http://host[":"port][abs_path]

http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用缺省端口80;abs_path指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮用户完成。

例如:

1.输入:www.nit.edu.cn

浏览器自动转换成:http://www.nit.edu.cn/

2.输入:192.168.0.116

浏览器自动转换成:http:192.168.0.116:8080/index.jsp

      1.  依照HTTP协议设计send()函数发送信息

网络爬虫按照HTTP协议,构造send()函数的发送请求数据。

定义一个char型数组sendBuf用来保存请求信息。具体实现如下:

char sendBuf[200];

然后按照HTTP协议构造请求信息。采用GET请求方法发送请求,一般的网站为了应负网络爬虫的搜索爬行,会制作出一个网站地图一般命名为sitemap.jsp。

网站地图的作用是

1.提供文本连接到站点上主要的页面,根据网站大小,网页数目的多少甚至可以连接到所有的页面。

2.为每个连接提供一个简短的介绍,用以提示访问者这部分内容是关于哪一方面的。

3.在网站地图和文本超连接里面提及最主要的关键词语,帮助搜索引擎识别所连接页面主题是关于哪一方面的。

4.帮助引擎索引一些动态页面,由于一些页面将是动态产生的,如果不是用户行为调用将不会显示出来,可以将此连接放在网站地图上,以帮助引擎索引重要的动态页面。

5.为了使网站地图吸引网络爬虫的访问,一定要在连接后面写上一定的描述性语句并使用此连接的关键词进行简单描述,但不能过渡的使用关键词,否则会适得其反。

6.因为一般爬虫只抓前三级页面,所以网站地图越平越好,这里指的是逻辑路径,而不是物理路径。

总之网站地图能够起到当用户查询到网站时能让用户一目了然的看清楚网站的实质作用,帮助用户更快的查询到所需要的信息。同时为搜索引擎提供绿色通道,使引擎程序迅速收录主要网页,得到较好的排名,让用户更容易找到这个网站。对于网络爬虫程序而言,网站地图是网站给其规定的查找规则。所以,本网络爬虫也主要以请求查询目标网站的网站地图为主要请求内容。同时,以HTTP/1.1作为HTTP-Version参数的内容,即采用HTTP 1.1版本。具体实现如下:

GET /sitemap.jsp HTTP/1.1

如果没有sitemap.jsp也可以用index.jsp代替。对于不同的网站有不同的效率。

HTTP请求包(GET、POST等请求方法)由三个部分构成,分别是:方法-URI-协议/版本,请求头,请求正文。所以除了定义方法-URL-协议版本外,还要定义请求头和请求正文,才能组成一个完整的HTTP请求包,使源URL的服务器能够明白这个网络爬虫的请求,从而给出正确的反馈,使这个网络爬虫能够顺顺当当的爬下去。Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。具体实现如下:

Host:localhost

Connection普通报头域允许发送指定连接的选项。例如指定连接是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接。本网络爬虫为了减少给服务器带来的负担,所以选用close选项。具体实现如下:

Connection:close

User-Agent也是请求报头域的一部分但不是必需部分我们上网登陆论坛的时候,往往会看到一些欢迎信息,其中列出了你的操作系统的名称和版本,你所使用的浏览器的名称和版本,这往往让很多人感到很神奇,实际上,服务器应用程序就是从User-Agent这个请求报头域中获取到这些信息。User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。不过,这个报头域不是必需的,如我们自己编写一个浏览器,不使用User-Agent请求报头域,那么服务器端就无法得知我们的信息了。

具体实现如下:

User-agent:MyApp/0.1

Accept-Language请求报头域类似于Accept,但是它是用于指定一种自然语言。如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。

具体实现如下:

Accept-language:zh-cn

Accept请求报头域用于指定客户端接受哪些类型的信息。比如Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。

具体实现如下:

Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF)

使用Accept请求报头域可以使我们自由选择网络爬虫希望抓取的URL类型。

如果把请求包中所有内容都加入到sendbuf[200]中会显得混乱并且很不易于维护和修改,为了养成良好的格式习惯,我在这里调用strcpy()函数和strcat()函数完成请求报的连接。是的格式规范整洁,易于维护,提高程序健壮性。

整体send()函数发送HTTP请求包如下:

strcpy(sendBuf , "GET /sitemap.jsp HTTP/1.1\r\n");

strcat(sendBuf , "Host:localhost\r\n");

strcat(sendBuf , "Connection:close\r\n");

strcat(sendBuf , "User-agent:MyApp/0.1\r\n");

strcat(sendBuf , "Accept-language:zh-cn\r\n\r\n");

    1.  正则表达式过滤模块的设计与实现
      1.  URL正则表达式的定义与实现

当recv()函数接受到源URL服务器反馈回来的应答报包后将接受到源URL页面的整个标签,这时候就需要用正则表达式里提取用户所要URL标签。

在可以匹配一个字符串到正则表达式之前,必须先建立正则表达式。开始的时候,正则表达式的语法有点古怪,表达式中的每一个短语代表某个类型的搜索特征。

搜索一个字符串的头部,用^;

搜索字符串尾部,用$;

任意的单个字符

搜索任意字符,用点(.);

为了告诉正则表达式引擎一个字符可能存在,也可以重复,用*字符;

为了告诉正则表达式引擎一个字符必须存在,也可以重复不止一次,用+字符;

也可以通过在表达式后用?字符来告诉正则表达式引擎匹配没有空白或者一个空白;

相关的模式可以在方括号里分在一起。很容易用[a-z]和[A-Z]指定只有一个小写字母或者一列大写字母以搜索字符串的一部分存在。

本论文的网络爬虫为了能够在源URL 页面下的所有的URL下爬行,要首先定义一个能够满足URL的正则表达式。检验一个标准的URL.一个标准的URL(没有端口号),有三个部分构成:[协议]://[域名]

首先从匹配URL的协议部分开始,并且让它只能用http或者ftp.我们可以用下面的正则表达式做到这点,如下:

^(http|ftp)

^字符特指字符串的头部,利用圆括号把http和ftp围住,且用“或者”符号(|)将它们分开,用来告诉正则表达式引擎http和ftp两者之一必须在字符串的开头。

一个域名通常由www.somesite.com构成,但是可以随意选择要不要www部分。为了实现起来简单一些,本论文只允许.com,.net,和.org的域名是在考虑之中的。最好这样对正则表达式中的域名部分表示如下:

(www.)?.+.(com|net|org)$

在本论文的网络爬虫程序中,为了更加准确的过滤URL定义了两个正则表达式,用来实现对页面标签的过滤,具体实现如下:

"\"[hH][tT]{2}[pP]\\:/{2}.*?(\")" 

用来获取如http://www.neusoft.com/...这样的URL。

"\"[hH][tT]{2}[pP]\\:/{2}.*?(?=((/)|(\")|(\\:)))" 

用来获取如www.neusoft.com这样的URL。

当然,每个人定义URL的正则表达式由于对于正则表达式的理解和低定义目标的不同而都不相同,本论文中定义的URL一定不是最全面有效的,但它可以获取所得页面上的URL标签,满足程序的可用性。

 

 

 

正则表达式流程图如图4-6所示。

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

图4-6 正则表达式流程图

      1.  开源正则表达式引擎DEELX的应用

为了能够在C++环境下使用正则表达式,本论文的爬虫程序需要调用正则表达式引擎来完成正则表达式的匹配调用。

DEELX 是一个在 C++ 环境下的与 Perl 兼容的正则表达式引擎。是 RegExLab 开展的一个研究开发项目。

基本特点:

1.支持与 Perl 兼容的正则表达式语法。

2.支持 IGNORECASE, SINGLELINE, MULTILINE 等常见匹配模式。

3.兼容性强,能在vc6,vc7,vc8,gcc,TurboC++ 等大多数C++环境编    译。

4.支持命名分组,条件表达式,递归表达式等多种高级特性。

与GRETA、boost 等开源正则表达式驱动相比,DEELX 独到之处:

1.完全使用模版库编写,支持char, wchar_t, int 等以及其他基类型版本。

2.全部代码位于一个头文件(.h)中,比任何引擎都使用简单和方 便。

3.支持从右向左匹配模式,可从文本结束位置向前搜索匹配。

4.可防止零长度子匹配循环无限次而产生的死循环。

DEELX开源正则表达式的使用方法:

DEELX 的移植和使用非常简单,DEELX 的所有代码只有一个头文件(deelx.h),include 该头文件即可。不需要为 DEELX 创建 project,也不需要添加任何 cpp 或者静态库 lib 文件。运行时,也不依赖专门的动态库。由于deelx.h 已经直接包含到项目中,因此不会存在Runtime Library 与主项目不同的问题,也不用担心会产生连接错误的问题。

      1.  MatchResult 类的调用和设计

MatchResult 类用来记录匹配结果。

MatchResult 对象中记录了所匹配到的字符串在整个文本中的位置,以及各个捕获组的位置。

要获取命名分组捕获的字符串,先通过 CRegexpT 类中的 GetNamedGroupNumber 属性获取分组编号,然后通过 MatchResult 对象获取捕获组信息。

成员方法

IsMatched 属性
  判断是否匹配成功,返回非 0 值表示匹配成功。

GetStart 属性
  获取匹配到的子字符串开始位置,如果未匹配成功则返回一个负值。

GetEnd 属性
  获取匹配到的子字符串结束位置,如果未匹配成功则返回一个负值。

GetGroupStart 属性
  获取指定分组的开始位置,如果该分组未捕获或者未匹配成功,则返 回一个负值。

GetGroupEnd 属性
  获取指定分组的结束位置,如果该分组未捕获或者未匹配成功,则返回一个负值。

MaxGroupNumber 属性
  返回表达式中最大的分组编号。

MatchResult类声明如图4-7所示。

图4-7 MatchResult类图

  为了校验以上定义的两个URL正则表达式,需要两次调用IsMatched()方法,具体实现如下:

说明:

定义myURL.push_back()来保存过滤后的目标URL。

MatchResult regex_first.Match( CContext )函数的主要功能是实现将设定好的字符串与网页上的代码进行匹配。函数MatchResult regex_first.Match( )声明及说明如表4-11所示。

表4-11 MatchResult regex_first.Match( )函数声明及说明表

函数声明

返回值类型

说明

MatchResult regex_first.Match( CContext )

MatchResult

将设定好的字符串与网页上的代码进行匹配用于校验http://www.neusoft.com/这样的URL

MatchResult regex_ second.Match函数声明及说明如表4-12所示。

表4-12 MatchResult regex_ second.Match( )函数声明及说明表

函数声明

返回值类型

说明

MatchResult         

regex_ second.Match()

MatchResult

将设定好的字符串与网页上的代码进行匹配用于校验www.neusoft.com这样的URL。

    1.  URL保存模块的设计与实现

定义vector容器类myURL 如下:

vector myURL

vector myURL用来保存网络爬虫所抓取下来的URL链接。

具体应用如下:

vectortemp;

Csocket sock;

SOCKET csock = sock.Create( AF_INET , SOCK_STREAM , 0 );

string myURL;

myURL = SpiderAddr->dest;

connresult = sock.CConnect( csock , myURL );

    1.  宽度搜索模块的设计与实现

在宽度优先搜索中,先搜索完一个Web 页面中所有的超级链接,然后再继续搜索下一层, 直到底层为止。例如,一个HTML 文件中有三个超链,选择其中之一并处理相应的HTML文件,然后不再选择第二个HTML文件中的任何超链, 而是返回并选择第二个超链,处理相应的HTML文件,再返回,选择第三个超链并处理相应的HTML文件。一旦一层上的所有超链都己被选择过,就可以开始在刚才处理过的HIML 文件中搜索其余的超链。这就保证了对浅层的首先处理。当遇到一个无穷尽的深层分支时,不会导致陷进WWW 中的深层文档中出现出不来的情况发生。宽度优先搜索策略还有一个优点,即它能在两个HTML文件之间找到最短路径。宽度优先搜索策略通常是实现爬虫的最佳策略,因为它容易实现,而且具备大多数期望的功能。但是如果要遍历一个指定的站点或者深层嵌套的HTML文件集,用宽度优先搜索策略则需要花费比较长的时间才能到达深层的HTML文件。综合考虑以上几种策略和国内信息导航系统搜索信息的特点,国内一般采用以宽度优先搜索策略为主、线性搜索策略为辅的搜索策 略。对于某些不被引用的或很少被引用的HTML文件,宽度优先搜索策略可能会遗漏这些孤立的信息源,可以用线性搜索策略作为它的补充。广度搜索策略应用流程如图4-8所示。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

图4-8 宽度搜索策略应用流程图

爬的深度是多少呢?如果我们只有一台服务器做网络爬虫,宽度优先算法效率统计如表4-13所示。

表4-13 宽度优先算法效率统计表

网页深度

网页个数

网页重要程度

0

1

10

1

20

8

2

600

5

3

2000

2

4

大约6000

一般无法计算

可见将深度设计为小于等于3层,符合本论文中网络爬虫的最高效率。设计深度获取函数Getdepth()具体接口如表4-13所示。

表4-13 深度获取函数Getdepth()接口表

 

函数名

Getdepth();

功能概要

输入搜索深度

记述形式

int Getdepth();

类型

变量名

I/O

说明

int

 

I

获取所要搜索的页面深度

返回值

类型

int

说明

-1

不能正确获取层数数值

0-4

正确的层数数值

详细说明

获得搜索深度并将它返回。

以递归来实现宽度搜索如表4-14所示。其中Spider( )函数用于递归调用实现循环。

表4-14 Spider( )函数接口表

 

函数名

Spider();

功能概要

调用一个spider,完成创建socket,连接网络,发送数据和接收数据的功能。

记述形式

void Spider( void*p );

类型

变量名

I/O

说明

Void*

p

I

指向一个数据结构

详细说明

该函数调用一个spider函数实现递归调用循环。

    1.  文件存储模块的设计与实现

本论文的网络爬虫程序为后台运行程序,为了保存搜索结果,需要调用fopen()函数来讲myURL容器中的目标URL保存成TXT格式的文本文件来保存在源程序所在目录中。

具体实现如下:

fp = fopen( fileName , "w" );参数“w”表示打开空TXT文本并向其写入。

FILE *fp;

static int fileNo = 0;

char fileName[128] = {0};

sprintf( fileName, "RecvURL%d.txt", fileNo++ );

fp = fopen( fileName , "w" );

说明:

fileNo:生成文件的序列号;

filename:生成文件的名字;

“w”:打开一个空的文本文件并且向其写入文件。

    1.  多线程的设计与实现

为了使网络爬虫的效率大幅提高,需要设计多线程函数,具体实现如下:

void thread(void *p)

{

              Spider(p);

              fclose( ((addr_t)p)->fp );

              free(p);

}

说明:

每一个线程调用一个爬虫程序,以实现不同源URL同时进行搜索。

调用_beginthread()函数创建线程:

void *p = SpiderAddr;

_beginthread( thread , 0 , p);

调用GetCurrentThreadId()函数随机获取生成线程的ID号以区别不同的线程。函数thread( )接口表如下表所示:

表4-15 thread( )函数接口表

 

函数名

thread( );

功能概要

创建多线程

记述形式

void thread( void* p )

参数

 

类型

变量名

I/O

说明

void*

p

I

指向一个数据结构

详细说明

将Spider ( p ),free ( p )和_endthread()三个函数封装在一个函数体中。

    1.  运行显示结果
      1.  输入

提示用户输入搜索深度以及需要搜索的网页地址如图4-9所示。

图4-9 网络爬虫输入显示图

      1.  显示

将搜索到的信息与相应的正则表达式进行匹配,匹配后的信息打印在屏幕上并储存在文件当中。如图4-10所示。

图4-10 结果显示图

      1.  结果

根据用户输入的网址的个数生成相应的TXT文件,文件内存储的内容即获取到的并与相应正则表达式匹配的URL。如下图所示:打开几个线程就会有几个文本文件用来保存搜索结果。如果打开两个线程就会有两个线程的.txt格式的保存文件,从0开始计数。如图4-11所示。

图4-11 保存结果图

 

    1.  本章总结

在本论文的网络爬虫程序的实现中,共设计了Socket模块,HTTP协议模块,正则表达式校验模块三个主要模块以及宽度搜索模块,文件存储模块两个辅助模块。

在SOCKET模块中又包含了如下内容:

SOCKET Create(int af, int type, int protocol);

int CConnect(SOCKET client, string dest);

void CClose(SOCKET client);

int CSend(SOCKET client, const char* sendbuf, int len);

void CRecv(SOCKET client, FILE* fp);

五个模块以实现创建、连接、关闭、发送、接收五个功能。

    在HTTP协议模块中按照HTTP协议,写了send()函数的请求包。

在正则表达式校验模块中,应用开源正则表达式引擎DEELX来校验网页标签中的目标URL。

在宽度搜索模块以宽度优先算法实现网页搜索。

各个模块共同实现了能够在后台运行的多线程网络爬虫程序。他可以以配置文件中的URL作为源URL,以宽度优先算法作为搜索算法,用正则表达式模块作为URL标签的验证模块,从源URL所在主页中提取所有的URL并向下层搜索,返回目标URL存储于文本文件中。

 

结论

本论文的完成网络爬虫的设计与实现,是一个在后台运行的多线程网络爬虫程序。它可以以配置文件中的URL作为源URL,以宽度优先算法作为搜索算法,用正则表达式模块作为URL标签的验证模块,从源URL所在主页中提取所有的URL并向下层搜索,返回目标URL存储于文本文件中。

本论文的爬虫程序可以从配置文件中读出URL任务列表,即初始URL种子,把初始种子保存在临界区中,然后利用多线程技术,每一个线程发送一个请求,多个爬虫线程在互联网上同时爬行,按照广度搜索运算法搜索抓取网页并提取URL返回到临届区中,通过正则表达式过滤所得的URL保存在临届区再将所得URL传回URL列表作为新的种子应用递归算法开始新的一轮多线程的搜索抓取网页和提取URL,从而使整个爬虫程序循环运行下去。是一个可以在后台自动运行的网络机器人。能够实现用户检索并保存目标URL的功能要求,并符合灵活性、健壮性和可维护性的性能指标要求。能够胜任简单的检索保存任务。

主要技术要点包括:socket套接字技术、http网络协议、正则表达式、文件读写和多线程编程技术。

socket套接字用来建立网络爬虫与源URL的连接,发送请求并接受所返回的信息;http网络协议用来定义CSend()函数的发送内容,使网络爬虫符合协议规范,能够正确执行功能;正则表达式用来过滤CRecv()函数所返回的信息数据,提取目标URL;文件读写技术用于初始配置文件的建立以及目标URL文件的保存;多线程编程技术用来建立多线程网络爬虫结构使网络爬虫的运行效率大幅提高。

主要检索算法为:广度优先算法(又称为宽度优先算法)。

广度优先又称为宽度优先,在广度优先搜索中,先搜索完一个Web 页面中所有的超级链接,然后再继续搜索下一层, 直到底层为止。这种优先搜索算法保证了对浅层的首先处理。当遇到一个无穷尽的深层分支时,不会导致陷进WWW 中的深层文档中出现出不来的情况发生。宽度优先搜索策略还有一个优点,即它能在两个HTML文件之间找到最短路径。宽度优先搜索策略通常是实现爬虫的最佳策略,因为它容易实现,而且具备大多数期望的功能。

致谢

本论文是在我的导师杨慧晶老师和魏佳老师的亲切关怀和悉心指导下完成的。魏老师和杨老师严肃的科学态度,严谨的治学精神,精益求精的工作作风,深深地感染和激励着我。魏老师不仅在学业上给我以精心指导,同时还在思想、生活上给我以无微不至的关怀,在此谨向魏老师致以诚挚的谢意和崇高的敬意。

同时,我的导师杨老师也给予了我巨大的帮助和鼓励。不管是在企业中的研发实习阶段还是在学校中的论文编写时期,杨老师都给予了我极大的指导和帮助,她细心认真地指导我解决了一个又一个的问题,给予了我莫大的帮助。在这里我要向杨老师致以衷心的感谢。

我还要感谢在一起愉快的度过毕业论文小组的同学们,正是由于你们的帮助和支持,我才能克服一个一个的困难和疑惑,直至本文的顺利完成。

论文的完成标志着四年的本科时代即将结束,也意味着,新的生活又将开始了。从开始进入课题到论文的顺利完成,可敬的师长、亲爱的同学、朋友给了我无私的帮助,在这里请接受我诚挚的谢意!最后我还要感谢含辛茹苦培养我长大的父母,谢谢你们!

最后,再次对关心、帮助我的老师和同学表示衷心地感谢!

参考文献

1   David S. Doermann, Ehud Rivlin and Isaac Weiss, Logo Recognition Using Geometric Invariants. Document Analysis and Recognition. Proceedings of the Second International Conference on, 20-22 1993: 4~34

2  Jie Zou, Nagy G.. Evaluation of model-based interactive flower recognition,Pattern Recognition. 2004. ICPR 2004. Proceedings of the 17th International Conference on,Volume 2, 23-26 Aug. 2004 Page(s): 311~314

3   Abo-Zaid. A simple invariant neural network for 2-D image recognition.  Thirteenth National Radio Science Conference, 1996. NRSC '96 : 18~21

4   Junhong Li, Quan Pan,Hongcai Zhang, Peiling Cui. Image recognition using Radon transform. Intelligent Transportation Systems, 2003. Proceedings. 2003 IEEE: 995~1000

5   Tuan D. Pham. Recognition of Trademarks with Spatial Statistics and Neural Learning. Proceedings of the 2002 IEEE International Conference on Artificial Intelligence Systems , 2002: 89~144

6  Fang G Nicholascr. Collaborative Project Management Architecture. Proceeding of the 36th Hawaii International Conference on System Sciences,2002: 234~240

7   林卉,赵尝胜,舒宁.基于Canny算子的边缘检测及评价.黑龙江工程学院报, 2003.17(2):36~45

8   胡洪涛,常佳.基于网络的信息获取技术浅析.福建电脑,2006.2:3~38

9   周立柱,林菱.聚焦爬虫技术研究综述.计算机应用,2005.9:48~52

10  Gary R.Wright W.Richard Stevens .TCP-IP协议详解卷3.机械工业出版社,2002 .1.:1~307

11  Anthony Jones,Jim Ohlund.Windows网络编程.机械工业出版社,2000.3:37~42

12  施炜,李铮.Windows Sockets 规范及应用-Windows网络编程接口.机械工业出版社,2002 年1月:45~48

13 Friedl,J.E.F.精通正则表达式(第3版).电子工业出版社,2007.07:30~445

 

附录

英文文献

Software Project Management Maturity Assessment Model to Assess

Software Project Management Practices

1Shukor Sanim Bin Mohd Fauzi, 2Nuraminah Bt Ramli

1Faculty of Information Technology and Quantitative Sciences, Universiti Teknologi Mara,

Perlis Campus, 02600 Arau, Perlis, Malaysia

[email protected]

2Faculty of Information and Communication Technology

Universiti Pendidikan Sultan Idris, 35900 Tanjong Malim, Perak, Malaysia

[email protected]

 

Abstract

This paper presents a model to assess Software Project Management (SPM) practices using our Software Project Management Maturity Assessment (SPMMA) model. We discuss the model procedure, assessment inputs and outputs, the assessment questionnaires, team selection associated with the SPMMA. We have tested our model in a mid-size IT company. One pilot project conferred with ISO 9001 certification has been selected in order to perform the assessment. For this version of assessment, it only focuses on the Project Management practices which comprise Project Planning, Project Monitoring and Control and Risk Management process areas. The paper also discuss about strength and weaknesses of the current process in the pilot project.

Keywords: Software Process Maturity Assessment, Project Management, Project Planning, Project Monitoring and Control, Risk Management

1. Introduction

Project management is an important part in software development organization (SDO). Without proper project management, it can lead to the failure of the project. Many software projects are faced with a common situation: They fail in developing the required functionality within schedule and planned budget; the

results often lack the required quality. Thus, during the last years several companies have started initiatives to improve their software development. These initiatives mostly focus on improving the software processes and the technology used during software development. One area often underestimated but crucial for every software development project is project management.

[1]

The SPMMA has been carried out at one of the mid-size IT company (called ABC for confidentiality reason). One pilot project has been selected. The project comprise of 40 staffs including Head of Project, Project Manager, Project Leader, System Analyst, Programmer, Business Analyst and other personnel.

All of the staffs has involved in this program. Two modules have been selected for SPMMA program. ABC company has involved in most of the government’s big project. Almost every project involves with the development of the system. They also provide other services such as supplying hardware and software.

 

In ABC company, planning, project requirements and development were mostly handled on the fly. The practitioners in ABC company have their own mentality and in reality, they are facing difficulties totag along the guideline introduced. SPMMA is very important to ABC company in order to provide structured framework for project management practices. Other than that, from the SPMMA, it can helps ABC company to measure the strength and weaknesses, develop action plan for software project management. Both process can lead to the development of software process improvement initiative for software project management practices.

This paper presents a model to assess Software Project Management (SPM) practices using our Software Project Management Maturity Assessment (SPMMA) model. We have used the Capability Maturity Model Integration (CMMI) [2] and Software Process Improvement and Capability Determination (SPICE) [3] [4] assessment models to guide the development of SPMMA.

2. SPMMA Components

We have identified a set of component for the SPMMA as shown in Figure 1 below:

i. The assessment inputs (questionnaires, interview question, checklist and assessment plan)

ii. SPMMA training and team selection criteria.

iii. SPMMA Procedure

iv. The assessment preliminary findings

v. The assessment outputs (Strengths and weaknesses, action plan, assessment record and SPPMA level)

SPMMA assessment procedure - Consists of a eries of steps that guide an assessment team in carrying out the assessment. The principle goals are ensure the assessment is executed with efficient, guide the assessment team as how to organize the

assessment, collect, and analyze assessment data. Others, SPMMA procedure also guide the assessors in developing action plan for the assessment.

SPMMA Team Selection – The assessment team consists of a team leader, organizational unit coordinator, librarian, facilitator, timekeeper, process area mini team and observer.

SPMMA Inputs - There are four (4) types of assessment instruments which had been produced. There were assessment plan, questionnaires, interview question and checklist. The assessment plan consists of the scope of the assessment, the participants for the interview session, the facilities need to perform the

assessment, the schedule of the assessment and other related aspect on the on-site assessment.

The questionnaires had been governed based on the specific practices in each process areas. It had been used as an additional instrument to gather data

indirectly from the practitioners in the project. The questions were organized in groups of process areas such as project planning and configuration management. The respondents need to select four (4) possible answers for each question. There are ‘Yes’, ‘No’, ‘Does Not Apply’ and ‘Don’t Know’.

The examples of the question for Risk Management process area questionnaires are:

i. Are risks contingency activities planned?

ii. Does the project conduct meetings to identify common causes of defects?

iii. Once identified, are common causes of risks prioritized and systematically eliminated?

iv. Does the project follow a written organizational policy for risks management activities?

v. Do members of the software engineering group and other software-related groups receive required training to perform their risks prevention activities?

vi. Are measurements used to determine the status of risk prevention activities (e.g.the time and cost for identifying risks and the number of action items proposed,

open, and completed)?

vii. Are the activities and work products for risk prevention subjected to SQA review and audit?

Besides the questionnaires, the interview question was used directly as an instrument to conduct the interview. Each personnel had been asked a question on the roles and responsibilities in project, the process which had been done in their daily task, the problems arise in implementing the process and also the recommendation on the process.

The sample questions for the interview session are:

i. Please tell me about yourself and your experience as it relates to this project?

ii. Please describe your role and responsibilities on the project?

iii. How do you know what you are supposed to be working on?

iv. What training have you had for your job?

v. Are you involved with any of the estimating and planning of the software project?

vi. If there were anything you could change about your project, what would it be?

vii. Do you hold periodic reviews of the project? How do you know what format to follow?

The checklist had been prepared based on the specific goals in each process areas. It had been used to list the findings from the assessment. The checklist would be filled up by the assessment team.

The primary purpose of the interviews is to give the assessment team a better understanding of the project management practices and the project managers’ perceptions of strengths and weaknesses in the process. Other than that, we and the assessment team also have to review all the related project management documentations.

Preliminary Findings - The preliminary findings represent the assessment team’s view of the software process currently faces the organization. The preliminary findings are based on the questionnaires responses, discussion with the assessment participants in the interview session and discussions among the assessment team members. From the checklist we and the assessment team have the idea on which practices is not really follow by the project. The mapping process is quite difficult for the reason that there is a lot of information that should be cover. The preliminary findings represent the starting point for

formulating recommendations. The assessment team should have achieve a team consensus for the assessment findings and complete the preliminary findings presentation.

To validate the assessment findings, it has been presented to the head of the project, project managers and also project leader. The presentation session serve the purpose of getting the project managers’ commitment to undertake specific improvement initiatives. After the findings presentation, the head of project and the assessment team leader hold an executivemeeting to discuss next steps. The purpose of the meeting is to confirm the time for the recommendations presentation and final report, to discuss the importance of forming the action plan team and to develop the action plan, and address any questions or concerns of management.

Outputs - The assessment team prepare a final report of the assessment findings and recommendations. The purpose of the final report is to document the assessment findings, describe the key findings, and make specific recommendations to the project based on the findings. Besides, the assessment report also consist the characterization for each process areas assessed. We use the formal characterization in SCAMPI [5] such as Fully Implemented (FI), Largely Implemented (LI), Partially Implemented (PI) and Not Implemented (NI) as the SPMMA Level.

Following the recommendations, the assessed project have to prepare an action plan that specifies how, when and by whom each recommendations to be implemented. Each action that addresses a

recommendation is call an action task. The purpose of the action plan is to identify and plan the work effort for each action task, identify responsibility and resources, and provide a mechanism for tracking and reporting on task status.

3. SPMMA Model Test Result

As mentioned in first chapter, we have test the SPMMA model in a mid-size IT company. We will give an overview of the result achieved from performing the assessment using SPMMA model. To help us in performing the assessment, we have used CMMI Generic Goals [2] to help us in measuring the strength and weakness of the current process. Table 1 show the result obtained in SPMMA.

From the mapping process in Table 1, we have used the formal characterization from SCAMPI. The intent of this characterization is very effective in order to summarize the assessment team’s judgment. Table 2 shows the characterization for the process areas assessed.

We have made some recommendation for the ABC company. For example, the ABC company should establish proper risk identification and contingency list. Besides it also should be well documented in a Risk Management documentation.

4. Future Plans

We are planning to develop our own 5-level structure and our own characterization indicator. Besides, we are also planning to use the SPMMA model to assess other software engineering practices. Next, we will develop a tool to aid the assessment team.

 

5. Conclusion

From our experience in implementing Software Process Maturity Assessment (SPMMA), it is believed that SPMMA can be a tool to measure the level or maturity of the software project management practices in an organization. From our experience, SPMMA can be an alternative to formal assessment because it is not too complicated rather than formal assessment such as

SCAMPI. We have identified three main elements to make sure SPMMA can be implemented successfully. There are model, measurement and also full commitment from the top management. Based on theresult, it can help ABC company to improve their project management practices. Of course, improving task will take a lot of time and effort from the ABC company. But, from that also, it can help ABC company to produce better products for their customers.

 

6. Reference

[1] Mandl-Striegnitz P, Lichter H. A Case Study on Project Management in Industry – Experiences and Conclusions. Proceedings of the European Software Measurement Conference (FESMA), 06. - 08. May 1998, Antwerp, Belgium, 1998, pp 305 – 313

[2] CMMI Product Development Team (2000). CMMI for Systems Engineering/Software Engineering, Version 1. 02, . CMU/SEI-2000-TR-018. Pittsburgh, PA:Software Engineering Institute.

[3] ISO/IECJTC1/WG10, “SPICE Products,” Technical Report, Type 2, June 1995.

[4] Dorling. A. , (1993) SPICE: Software Process Improvement and Capability dEtermination. Software Quality Journal 2, 209-224.

[5] Software Engineering Institute, 2001 . Standard CMMI Appraisal Method for Process Improvement (SCAMPI, Version 1. 1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

中文译文

用软件项目管理完备估价模型评定软件项目管理实践

Shukor Sanim Bin Mohd Fauzi, Nuraminah Bt Ramli

Faculty of Information Technology and Quantitative Sciences,

Universiti Teknologi Mara, Perlis Campus,

02600 Arau, Perlis, Malaysia

[email protected]

Faculty of Information and Communication Technology

Universiti Pendidikan Sultan Idris, 35900 Tanjong Malim Perak, Malaysia

[email protected]

 

摘要

    这篇文章展示了一个利用我们的软件项目管理完备估价(SPMMA)模型评定软件项目管理(SPM)实践.我们讨论模型过程,估价输入和输出,估价调查表,SPMMA相关的队伍挑选.我们已经在一个中型IT公司中测试过了我们的模型.为了完成估价,选择了一个通过了ISO 9001认证的领航员项目.对于这个估价版本,只专注于包含项目计划,项目监测,控制和风险管理过程范围的项目管理实践.本文也讨论了领航员项目当前进程的强度和弱点.

    关键字: 软件过程完备估价,项目管理,项目计划,项目监控和控制,风险管理.

 

1. 介绍

在软件开发组织(SDO)中,项目管理是一个重要的部分.缺乏了适当的项目管理,会导致项目的失败.很多软件项目面临着一个共同的情形:他们在日程表和计划了的成本之内,在开发必须的功能性时失败了;结果常常缺乏必须的质量.这样,在这几年有些公司开始主动改进他们的软件开发.这些主动主要集中在改进软件过程和软件开发中用到的技术.在软件开发项目中一个重要的但经常被忽视的领域是软件管理.

SPMMA在一家中型IT公司(由于机密性的原因我们称为ABC)被实现了. 我们选择了一个领航员项目.这个项目由40个人员组成,包括项目领袖,项目经理,项目领导,系统分析员,程序员,商业分析员和其他职员,所有这些人员都参与了这个程序.SPMMA程序中的2个模块被选定.ABC公司致力于很多政府的大项目.参与了几乎每个项目的系统开发.他们也提供其他诸如提供硬件和软件的服务.

在ABC公司,计划,项目需求和开发很多都是在飞行中完成的.ABC公司的从业人员有他们自己的想法.事实上,他们在顺着方针介绍往下走时碰到了困难.SPMMA为ABC公司在项目管理实践中提供结构框架很重要.另外,SPMMA可以帮助ABC公司测量强度和缺点,并为软件项目管理开发行动计划.这2点在软件项目管理实践中都能将软件过程开发主动改进.

这篇文章展示了一个利用我们的软件项目管理完备估价(SPMMA)模型做成的模型评定软件项目管理(SPM)的实践.我们用了CMMI[2],软件过程改进和容量测定(SPICE)[3][4]估价模型来领导SPMMA的开发.

2. SPMMA组成部分

    在下面的图1中,我们已经为SPMMA鉴别了一套组成部分:

  1. 估价输入(调查表,调查问题,清单和估价计划)

  2. SPMMA训练和小组选择标准.

  3. SPMMA程序

  4. 估价初步的发现.

  5. 估价输出(强度和缺点,行动方案,估价记录和SPPMA级别)

    SPMMA估价程序--由一些连续的步骤组成,指导一个估价小组完成估价.原则目标是保证估价有效地完成,指导估价小组怎样组织估价,收集,分析估价数据.另外,SPMMA程序也指导估价员为估价开发行动计划.

    SPMMA小组选择--估价小组由一个小组组长,组织协调者,图书管理员,促进人员,计时员,过程袖珍小组和观察员.

    SPMMA输入--总共有4种类型的估价工具被提出来.估价计划,调查表,调查问题和清单.估价计划包括估价范围,会议会见的参与者,完成估价需要的设备.估价日程表和其他现场估价相关的方面.

    每个过程中,参与者基于特定的实践被管理着.这被用来当成一个额外的工具间接从参与项目的参与者那里收集数据.每个过程的问题都被组织为一组.例如项目计划和结构管理.回答者对每个问题选择可能的四个(4)答案:”是”,”否”,”不确定”,”不知道”.

    一个关于风险管理过程的调查表的例子:

  1. 风险的偶然行为计划了吗?
  2. 项目管理会议确定了普遍的缺陷的原因了吗?
  3. 一旦确定了,把这些普遍的缺点优先级设定了吗?系统地排除了吗?
  4. 项目遵循了组织对于风险管理行为的方针了吗?
  5. 软件工程团队的成员和其他有关团队需要为完成他们的风险预防行为进行练习吗?
  6. 有一个测量方法被用来确定风险预防行为状态(例: 确定风险的时间和成本和行为项目提议,开展和完成的数量)?
  7. 风险管理主题的行为和工作产品经过SQA回顾和审核了吗?

除了调查表,调查问题也被直接用做一个工具引导调查.每个项目中担任角色和有责任的人员被问了一个问题,问题包括他们每天任务的完成过程,执行过程遇到的问题和对该过程的建议.

调查会议问题的例子为:

  1. 请说一下你和你关于这个项目的经验.
  2. 请描述一下你在这个项目中担任的角色和责任.
  3. 你是怎么知道你将会进行怎样的预想的工作?
  4. 针对你的工作,你进行过什么训练?
  5. 你以前从事过对软件项目的估计和计划吗?
  6. 如果你觉得你的项目需要改进,你觉得是哪些地方?
  7. 你对你的项目进行过周期性的重审吗?你怎么知道你应该遵循哪种格式?

清单在每个过程针对特定的目标被准备好了.它被用来列出对估价的发现.清单是由估价小组填写的.

调查首要的目的是让估价小组对于项目管理实践和项目管理过程的强度和缺点有一个更好的认识.此外,估价小组和我们也不得不重新审查所有关于项目管理的文件.

初步的发现--初步的发现表现出估价小组对于软件过程现在面对的组织的观点.初步的发现是基于调查表结果,在调查会议中与估价参与者的讨论和估价小组成员的讨论的.从列表中我们和估价小组有一个想法,实践不真的遵循项目. 由于很多应该被覆盖的信息的原因,映射过程相当困难.初步的发现显示出明确地叙述建议的起始点.估价小组对于估价发现和初步的发现总结陈述应该有一致的意见.

为证实估价发现,它被反映给项目领袖,项目经理和项目领导了.陈述会议基于获得项目经理们的同意进行特定的主动改进的目的.

在发现陈述之后,项目领导和估价小组组长针对下一个步骤举行了一个行政会议.会议的目的是确定建议陈述和最终报告的时间.讨论形成行动方案小组的重要性,开发行动计划和从事任何问题和关注管理.

输出--估价团队对估价发现和建议准备一个最终报告.这个最终报告的目的是证明估价发现,描述关键的发现和作出基于该发现的关于该项目的明确的建议.此外,估价报告也包含了对每个过程的评定的描述.我们用SCAMPI[5]那种形式的描述.比如在SPMMA级别上的充分地实行(FI),主要地实行(LI),部分地实行(PI)和没有实行(NI).

根据建议,评定过的项目不得不准备一个行动计划以指定每一个建议是怎样,什么时候,被谁执行的.每个从事于该建议的行动称为行动任务.行动计划的目的是确定和计划每一个行动任务的工作成果,确定责任和资源,提供跟踪和报告任务状态机制的.

 

3. SPMMA模型测试结果

    就象第一节说过的那样,我们已经在一个中等规模的IT公司测试过了SPMMA模型.我们可以给出一个用SPMMA模型执行估价的结果的总体的概括.为了帮助我们执行估价,我们运用了CMMI一般目标[2]帮助我们测量当前过程的强度和缺点.表格1显示出由SPMMA获得的结果.

    在表格1绘制过程中,我们运用了SCAMPI的正式的描述.用这种描述的意图是为了有效地总结估价小组的判断.表格2给出过程评定的描述.

    我们给ABC公司作出了一些建议.例如,ABC公司应该建立适当的风险鉴定和偶然的列表.此外还应该在一个风险管理文件中好好证明它.

 

4. 以后的计划.

    我们计划开发我们自己的5级结构和我们自己的描述指示器.此外,我们也在计划用SPMMA模型评定其他软件工程实践.下一步,我们将设计一个工具来帮助估价小组.

 

5. 总结

    从我们在实现软件过程完备估价(SPMMA)的经验中,有理由相信SPMMA可以被用作一个工具来测量一个组织软件项目管理实践的级别或成熟度.通过我们的经验,SPMMA可以成为一个可供选择的正式的估价.因为他比起其他的正式的估价如SCAMPI来说并不复杂.我们确定了三个主要的因素来确保SPMMA可以成功地实现.有顶部管理的模型,测量方法和完整的委托事项.基于这些原因,它可以帮助ABC公司实现他们的项目管理实践.当然,ABC公司改进任务将花费很多时间和努力.但是,也正是这样一来,它可以帮助ABC公司为他们的顾客生产更好的产品.

 

6. 参考资料

[1] Mandl-Striegnitz P, Lichter H. A Case Study on Project Management in Industry – Experiences and Conclusions. Proceedings of the European Software Measurement Conference (FESMA), 06. - 08. May 1998, Antwerp, Belgium, 1998, pp 305 – 313

[2] CMMI Product Development Team (2000). CMMI for Systems Engineering/Software Engineering, Version 1. 02, . CMU/SEI-2000-TR-018. Pittsburgh, PA:Software Engineering Institute.

[3] ISO/IECJTC1/WG10, “SPICE Products,” Technical Report, Type 2, June 1995.

[4] Dorling. A. , (1993) SPICE: Software Process Improvement and Capability dEtermination. Software Quality Journal 2, 209-224.

[5] Software Engineering Institute, 2001 . Standard CMMI Appraisal Method for Process Improvement

 

 

 

 

 

 

 

源代码(部分)不含deelx.hstdafx.cppstdafx.h

/*****************/

/*Socket.h

/*****************/

#ifndef _CSOCKET_H_

#define _CSOCKET_H_

#pragma once

 

#include "stdafx.h"

#include "deelx.h"

 

class Csocket

{

public:

       Csocket(void);

       SOCKET Create(int af, int type, int protocol);

       int CConnect(SOCKET client, string dest);

       void CClose(SOCKET client);

       int CSend(SOCKET client, const char* sendbuf, int len);

       void CRecv(SOCKET client, FILE* fp);

public:

       ~Csocket(void);

      

private:

       int af;

       int type;

       int protocol;

public:

       vector myURL;

};

 

#endif

 

 

 

 

 

 

 

 

 

 

/*****************/

/*Socket.cpp

/*****************/

#include "Csocket.h"

 

Csocket::Csocket(void)

{

      

}

Csocket::~Csocket(void)

{

 

}

 

SOCKET Csocket::Create(int af, int type, int protocol)

{

       WORD wVersionRequested;

       WSADATA wsaData;

       int err;

      

       wVersionRequested = MAKEWORD( 2, 2 );

      

       err = WSAStartup( wVersionRequested, &wsaData );

       if ( err != 0 )

       {

              cout << "Error: " << WSAGetLastError() << endl;

              return NULL;

       }                                     

      

       if ( LOBYTE( wsaData.wVersion ) != 2 ||

             

        HIBYTE( wsaData.wVersion ) != 2 )

       {  

              cout << "Error: " << WSAGetLastError() << endl;

              WSACleanup( );

              return NULL;

       }

 

       return socket(af, type, protocol);

}

 

int Csocket::CConnect(SOCKET client, string dest)

{    

       SOCKADDR_IN addrSrv;

 

       char a[50] = " ";

       for(int i=0; i

              a[i] = dest[i];

       LPHOSTENT lphost;

 

       int port ;

       port = 8080;

       lphost = gethostbyname(a);

       addrSrv.sin_addr.S_un.S_addr = ((LPIN_ADDR)lphost->h_addr)->S_un.S_addr;

       addrSrv.sin_port = htons(port);

       addrSrv.sin_family = AF_INET;

 

       int result;

       result = connect(client, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

 

       if (0 != result)

       {

              cout << "Connect error! " << GetLastError() << endl;

              WSACleanup();

       }

 

       return result;

}

 

void Csocket::CClose(SOCKET client)

{

       closesocket(client);

       WSACleanup();

}

 

int Csocket::CSend(SOCKET client, const char* sendbuf, int len)

{

       int result;

       result = send (client , sendbuf , len , 0);

       return result;

}

 

void Csocket::CRecv(SOCKET client, FILE* fp)

{

       char recvBuf[200];

 

       static CRegexpT regex_first("\"[hH][tT]{2}[pP]\\:/{2}.*?(\")");

       // get the URL like "http://www.neusoft.com/..."

       static CRegexpT regex_second("\"[hH][tT]{2}[pP]\\:/{2}.*?(?=((/)|(\")|(\\:)))");

       // get the URL like "www.neusoft.com"

 

       int recvresult = 0;

       int i = 0;

      

       string temp;

      

       do

       {

              memset(recvBuf,0x00,sizeof(recvBuf));

              recvresult = recv(client, recvBuf, 200, 0);

 

              MatchResult result_first = regex_first.Match(recvBuf);

 

              while( result_first.IsMatched() )

              {

                     char resu[200] = " ";

                     for (i=0; i

                            resu[i] = recvBuf[result_first.GetStart()+i];

 

                     fputs(resu, fp);

                     fprintf(fp, "\n");

 

                     cout << resu << endl;

 

                     result_first = regex_first.Match(recvBuf, result_first.GetEnd());

              }

 

              MatchResult result_second = regex_second.Match(recvBuf);

 

              while( result_second.IsMatched())

              {

                     char resu[200] = " ";

                     for (i=0; i

                            resu[i] = recvBuf[result_second.GetStart()+i+8];

             

                     temp = resu;

                     myURL.push_back(temp);

 

                     result_second = regex_second.Match(recvBuf, result_second.GetEnd());

              }

 

       }while (recvresult != 0);

       cout << "Received over!" << endl;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/********************/

/*Sipder.h

/********************/

#pragma once

 

#ifndef _SPIDER_H

#define _SPIDER_H

 

#include "stdafx.h"

#include "Csocket.h"

 

typedef struct addr

{

       char *dest;

       FILE *fp;

       int depth;

}*addr_t;

 

void Spider(void* p);

 

#endif

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/*******************/

/*Spider.cpp

/*******************/

#include "Spider.h"

 

void Spider(void* p)

{

 

       addr_t SpiderAddr;

       SpiderAddr = (addr_t)malloc(sizeof(addr));

       memset( SpiderAddr , 0*00 , sizeof(addr) );

       SpiderAddr = (addr_t)p;

      

       int connresult;

       char sendBuf[200];

       strcpy(sendBuf , "GET /sitemap.jsp HTTP/1.1\r\n");

       strcat(sendBuf , "Host:localhost\r\n");

       strcat(sendBuf , "Connection:close\r\n");

       strcat(sendBuf , "User-agent:MyApp/0.1\r\n");

       strcat(sendBuf , "Accept-language:zh-cn\r\n\r\n");

 

       vectortemp;

       Csocket sock;

       SOCKET csock = sock.Create( AF_INET , SOCK_STREAM , 0 );

       string myURL;

       myURL = SpiderAddr->dest;

       connresult = sock.CConnect( csock , myURL );

      

       if (0!=connresult)

              return;

 

       int sendresult;

      

       sendresult = sock.CSend( csock , sendBuf , strlen(sendBuf) );

      

       if (sendBuf<0)

       {

              cout << "Send Error!" << endl;

              return ;

       }

 

       cout << sendresult << "Date Send!" << endl;

       cout << "Thread ID = " << GetCurrentThreadId() << endl;

 

      

       sock.CRecv( csock , SpiderAddr->fp );

       temp = sock.myURL;

       sock.CClose(csock);

 

       while( SpiderAddr->depth > 0 )

       {

              SpiderAddr->depth--;

              for ( int i = 0 ; i < temp.size() ; i++ )

              {

                     addr_t subSpiderAddr;

                     subSpiderAddr = ( addr_t )malloc(sizeof(addr));

                     memset( subSpiderAddr , 0*00 , sizeof(addr) );

                     subSpiderAddr->depth = SpiderAddr->depth;

                     subSpiderAddr->dest = temp[i].begin();

                     subSpiderAddr->fp = SpiderAddr->fp;

 

                     void *p = subSpiderAddr;

                     Spider(p);

              }

       }

 

       /***********************/

       Sleep(1000L);

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/******************/

/*main.cpp

/******************/

 

#include "stdafx.h"

#include "Spider.h"

 

bool wait = false;

 

 

 

void Stop(void *dummy)

{

       char cut;

       cout << "Input a '!' to stop the Spider " << endl;

       cin >> cut;

       if( '!'==cut )

              wait = true;

}

 

int Getdepth()

{

       int depth;

      

       while(1)

       {

              char temp = '0';

              cout << " Please input the depth to dig : (0-3) default:0 " << endl;

              cin >> temp;

              if ( ((temp-48) > -1) && ((temp-48) < 4) )

              {

                     depth = temp - 48;

                     break;

              }

 

              cout << " Wrong input , please input again! " <

       }

 

       return depth;

}

 

 

void thread(void *p)

{

       Spider(p);

       fclose( ((addr_t)p)->fp );

       free(p);

 

}

 

int main()

{

       string source;

//     source = "www.neusoft.com";

       int depth = 0;

       depth = Getdepth();

       while(!wait)

       {

              cout << "Input the URL (like:www.baidu.com)" << endl;

              cin >> source;

             

              if( source.size() > 50 )

              {

                     cout << " Wrong input ! Please input again ! " << endl;

                     continue;

              }

 

              FILE *fp;

              static int fileNo = 0;

              char fileName[128] = {0};

              sprintf( fileName, "RecvURL%d.txt", fileNo++ );

             

              fp = fopen( fileName , "w" );

              /**************/

              addr_t SpiderAddr;

              SpiderAddr = (addr_t)malloc(sizeof(addr));

              memset( SpiderAddr , 0*00 , sizeof(addr));

              SpiderAddr->depth = depth;

              SpiderAddr->dest = source.begin();

              SpiderAddr->fp = fp;

              void *p = SpiderAddr;

              _beginthread( thread , 0 , p)

              Sleep(1000L);

       }

       return 0;      

}

你可能感兴趣的:(基于c++搜索引擎网络爬虫设计与实现)