摘要
    网络爬虫是一种自动搜集互联网信息的程序。通过网络爬虫不仅能够为搜索引擎采集网络信息,而且可以作为定向信息采集器,定向采集某些网站下的特定信息,如招聘信息,租房信息等。
    本文通过JAVA实现了一个基于广度优先算法的多线程爬虫程序。本论文阐述了网络爬虫实现中一些主要问题:为何使用广度优先的爬行策略,以及如何实现广度优先爬行;为何要使用多线程,以及如何实现多线程;系统实现过程中的数据存储;网页信息解析等。
    通过实现这一爬虫程序,可以搜集某一站点的URLs,并将搜集到的URLs存入数据库。
  【关键字】网络爬虫;JAVA;广度优先;多线程。

ABSTRACT
  SPIDER is a program which can auto collect informations from internet. SPIDER ca
n collect data for search engines, also can be a Directional information collector, collects specifically informations from some web sites, such as HR informations,  this paper, use JAVA implements a breadth-first algorithm multi-thread SPDIER. This paper expatiates some major problems of SPIDER: why to use breadth-first crawling strategy, and  collect URLs from one web site, and store URLs into database.
  KEY WORDSPIDER; JAVA; Breadth First Search; multi-threads.
第一章 引言    1
第二章 相关技术介绍    2
2.1 JAVA线程    2
2.1.1 线程概述    2
2.1.2  JAVA线程模型    2
2.1.3 创建线程    3
2.1.4 JAVA中的线程的生命周期    4
2.1.5 JAVA线程的结束方式    4
2.1.6 多线程同步    5
2.2 URL消重    5
2.2.1 URL消重的意义    5
2.2.2 网络爬虫URL去重储存库设计    5
2.2.3 LRU算法实现URL消重    7
2.3 URL类访问网络    8
2.4 爬行策略浅析    8
2.4.1宽度或深度优先搜索策略    8
2.4.2 聚焦搜索策略    9
2.4.3基于内容评价的搜索策略    9
2.4.4 基于链接结构评价的搜索策略    10
2.4.5 基于巩固学习的聚焦搜索    11
2.4.6 基于语境图的聚焦搜索    11
第三章 系统需求分析及模块设计    13
3.1 系统需求分析    13
3.2 SPIDER体系结构    13
3.3 各主要功能模块(类)设计    14
3.4 SPIDER工作过程    14
第四章 系统分析与设计    16
4.1 SPIDER构造分析    16
4.2 爬行策略分析    17
4.3 URL抽取,解析和保存    18
4.3.1 URL抽取    18
4.3.2 URL解析    19
4.3.3 URL保存    19
第五章 系统实现    21
5.1 实现工具    21
5.2 爬虫工作    21
5.3 URL解析    22
5.4 URL队列管理    24
5.4.1 URL消重处理    24
5.4.2 URL等待队列维护    26
5.4.3 爬虫可以干什么数据库设计    27
第六章 系统测试    29
第七章 结论    32
参考文献    33
致谢    34
外文资料原文    35
译文    51

第一章 引言
    随着互联网的飞速发展,网络上的信息呈爆炸式增长。这使得人们在网上到所需的信息越来越困难,这种情况下搜索引擎应运而生。搜索引擎搜集互联网上数以亿计的网页,并为每个词建立索引。在建立搜索引擎的过程中,搜集网页是非常重要的一个环节。爬虫程序就是用来搜集网页的程序。以何种策略偏历互联网上的网页,也成了爬虫程序主要的研究方向。现在比较流行的搜索引擎,比如google,百度,它们爬虫程序的技术内幕一般都不公开。目前几种比较常用的爬虫实现策略:广度优先的爬虫程序,Repetitive爬虫程序,定义爬行爬虫程序,深层次爬行爬虫程序。此外, 还有根据概率论进行可用Web页的数量估算, 用于评估互联网Web规模的抽样爬虫程序; 采用爬行深度、页面导入链接量分析等方法, 限制从程序下载不相关的Web页的选择性爬行程序等等。
    爬虫程序是一个自动获取网页的程序。它为搜索引擎从互联网上下载网页,是搜索引擎的重要组成部分。爬虫程序的实现策略,运行效率直接影响搜索引擎的搜索结果。不同的搜索引擎,会根据对搜索结果的不同需求,选择最合适的爬行策略来搜集互联网上的信息。高效,优秀的爬虫程序可以使人们在互联网上寻到更及时,更准确的信息。
实现网络爬虫的重点和难点有:多线程的实现;对临界资源的分配;遍历web图的遍历策略选择和实现;存储数据结构的选择和实现。
    本文通过JAVA语言实现一个基于广度优先偏历算法的多线程爬虫程序。通过实现此爬虫程序可以定点搜集某一站点的URLs,如果需要搜集其他信息,可以在解析URLs的同时,解析获取相应信息。
第二章 相关技术介绍
2.1 JAVA线程
2.1.1 线程概述
    几乎每种操作系统都支持线程的概念进程就是在某种程度上相互隔离的,独立运行的程序。一般来说,这些操作系统都支持多进程操作。所谓多进程,就是让系统(好像)同时运行多个程序。比如,我在Microsoft Word编写本论文的时候,我还打开了一个mp3播放器来播放音乐,偶尔的,我还会再编辑Word的同时让我的机器执行一个打印任务,而且我还喜欢通过IE从网上下载一个Flash动画。对于我来说,这些操作都是同步进行的,我不需要等一首歌曲放完了再来编辑我的论文。看起来,它们都同时在我的机器上给我工作。
    事实的真相是,对于一个CPU而言,它在某一个时间点上,只能执行一个程序。CPU
断的在这些程序之间“跳跃”执行。那么,为什么我们看不出任何的中断现象呢?这是因为,相对于我们的感觉,它的速度实在太快了。我们人的感知时间可能以秒来计算。而对于CPU而言,它的时间是以毫秒来计算的,从我们肉眼看来,它们就是一个连续的动作。
    因此,虽然我们看到的都是一些同步的操作,但实际上,对于计算机而言,它在某个时间点上只能执行一个程序,除非你的计算机是多CPU的。
    多线程(Multi-Thread)扩展了多进程(multi-Process)操作的概念,将任务的划分下降到了程序级别,使得各个程序似乎可以在同一个时间内执行多个任务。每个任务称为一个线程,能够同时运行多个线程的程序称为多线程程序。
    多线程和多进程有什么区别呢?对于进程来说,每个进程都有自己的一组完整的变量,而线程则共享相同的数据。
2.1.2  JAVA线程模型
    我们知道,计算机程序得以执行的三个要素是:CPU,程序代码,可存取的数据。在JAVA语言中,多线程的机制是通过虚拟CPU来实现的。可以形象的理解为,在一个JAVA
序内部虚拟了多台计算机,每台计算机对应一个线程,有自己的CPU,可以获取所需的代码和数据,因此能独立执行任务,相互间还可以共用代码和数据。JAVA的线程是通过java.lang.Thread类来实现的,它内部实现了虚拟CPU的功能,能够接收和处理传递给它的代码和数据,并提供了独立的运行控制功能。
    我们知道,每个JAVA应用程序都至少有一个线程,这就是所谓的主线程。它由JVM创建并调用JAVA应用程序的main()方法。
    JVM还通常会创建一些其他的线程,不过,这些线程对我们而言通常都是不可见的。比如,用于自动垃圾收集的线程,对象终止或者其他的JVM处理任务相关的线程。
2.1.3 创建线程
2.1.3.1 创建线程方式一
    JAVA中创建线程的一种方式是通过Thread来实现的。Thread有很多个构造器来创建一个线程(Thread)实例:
    Thread();创建一个线程。
    Thread(Runnable target);创建一个线程,并指定一个目标。
    Thread(Runnable target,String name);创建一个名为name的目标为target的线程。
    Thread(String name);创建一个名为name的线程。
    Thread(ThreadGroup group,Runnable target);创建一个隶属于group线程组,目标为target的线程。
    通常,我们可以将一个类继承Thread,然后,覆盖Thread中的run()方法,这样让这个类本身也就成了线程。
    每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。
    使用start()方法,线程进入Runnable状态,它将线程调度器注册这个线程。调用start()方法并不一定马上会执行这个线程,正如上面所说,它只是进入Runnble而不是Running
2.1.3.2 创建线程方式二
    通过实现Runnable接口并实现接口中定义的唯一方法run(),可以创建一个线程。在使用Runnable接口时,不能直接创建所需类的对象并运行它,而是必须从Thread类的一个实例内部运行它。
    从上面两种创建线程的方法可以看出,如果继承Thread类,则这个类本身可以调用start方法,也就是说将这个继承了Thread的类当作目标对象;而如果实现Runnable接口,则这个类必须被当作其他线程的目标对象。
2.1.4 JAVA中的线程的生命周期
    JAVA的线程从产生到消失,可分为5种状态:新建(New),可运行(Runnable),运行(Running),阻塞(Blocked)以及死亡(Dead)。其中,Running状态并非属于JAVA规范中定义的线程状态,也就是说,在JAVA规范中,并没有将运行(Running)状态真正的设置为一个状态,它属于可运行状态的一种。
    当使用new来新建一个线程时,它处于New状态,这个时候,线程并未进行任何操作。
    然后,调用线程的start()方法,来向线程调度程序(通常是JVM或操作系统)注册一个线程,这个时候,这个线程一切就绪,就等待CPU时间了。
    线程调度程序根据调度策略来调度不同的线程,调用线程的run方法给已经注册的各个线程以执行的机会,被调度执行的线程进入运行(Running)状态。当线程的run方法运行完毕,线程将被抛弃,进入死亡状态。你不能调用restart方法来重新开始一个处于死亡状态的线程,但是,你可以调用处于死亡状态的线程对象的各个方法。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。