用Java编程实现“网络蜘蛛”
副标题#e#
简介
“网络蜘蛛”可能说“网络爬虫”,是一种能会见网站并跟踪链接的措施,通过它,可快速地画出一个网站所包括的网页舆图信息。本文主要报告如何利用Java编程来构建一个“蜘蛛”,我们会先以一个可复用的蜘蛛类包装一个根基的“蜘蛛”,并在示例措施中演示如何建设一个特定的“蜘蛛”来扫描相关网站并找出死链接。
Java语言在此很是适合构建一个“蜘蛛”措施,其内建了对HTTP协议的支持,通过它可以传输大部门的网页信息;其还内建了一个HTML理会器,正是这两个原因使Java语言成为本文构建“蜘蛛”措施的首选。
利用“蜘蛛”
文章后头例1的示例措施,将会扫描一个网站,并寻找死链接。利用这个措施时需先输入一个URL并单击“Begin”按钮,措施开始之后,“Begin”按钮会酿成“Cancel”按钮。在措施扫描网站期间,会在“Cancel”按钮之下显示进度,且在查抄当前网页时,也会显示相关正常链接与死链接的数目,死链接将显示在措施底部的转动文本框中。单击“Cancel”按钮会遏制扫描进程,之后可以输入一个新的URL;假如期间没有单击“Cancel”,措施将会一直运行直到查找完所有网页,从此,“Cancel”按钮会再次变回“Begin”,暗示措施已遏制。
下面将演示示例措施是如何与可复用“Spider”类交互的,示例措施包括在例1的CheckLinks类中,这个类实现了ISpiderReportable接口,如例2所示,正是通过这个接口,蜘蛛类才气与示例措施相交互。在这个接口中,界说了三个要领:第一个要领是“spiderFoundURL”,它在每次措施定位一个URL时被挪用,假如要领返回true,暗示措施应继承执行下去并找出个中的链接;第二个要领是“spiderURLError”,它在每次措施检测URL导致错误时被挪用(如“404 页面未找到”);第三个要领是“spiderFoundEMail”,它在每次发明电子邮件地点时被挪用。有了这三个要领,Spider类就能把相关信息反馈给建设它的措施了。
在begin要领被挪用后,“蜘蛛”就开始事情了;为答允措施重绘其用户界面,“蜘蛛”是作为一个单独的线程启动的。点击“Begin”按钮会开始这个靠山线程,当靠山线程运行之后,又会挪用“CheckLinks”类的run要领,而run要领是由Spider工具实例化时启动的,如下所示:
spider = new Spider(this);
spider.clear();
base = new URL(url.getText());
spider.addURL(base);
spider.begin();
首先,一个新的Spider工具被实例化,在此,需要通报一个“ISpiderReportable”工具给Spider工具的结构函数,因为“CheckLinks”类实现了“ISpiderReportable”接口,只需简朴地把它作为当前工具(可由要害字this暗示)通报给结构函数即可;其次,在措施中维护了一个其会见过的URL列表,而“clear”要领的挪用则是为了确保措施开始时URL列表为空,措施开始运行之前必需添加一个URL到它的待处理惩罚列表中,此时用户输入的URL则是添加到列表中的第一个,措施就由扫描这个网页开始,并找到与这个起始URL相链接的其他页面;最后,挪用“begin”要领开始运行“蜘蛛”,这个要领直到“蜘蛛”事情完毕或用户打消才会返回。
当“蜘蛛”运行时,可以挪用由“ISpiderReportable”接话柄现的三个要领来陈诉措施当前状态,措施的大部门事情都是由“spiderFoundURL”要领来完成的,当“蜘蛛”发明一个新的URL时,它首先查抄其是否有效,假如这个URL导致一个错误,就会把它看成一个死链接;假如链接有效,就会继承查抄它是否在一个差异的处事器上,假如链接在同一处事器上,“spiderFoundURL”返回true,暗示“蜘蛛”应继承跟踪这个URL并找出其他链接,假如链接在别的的处事器上,就不会扫描是否尚有其他链接,因为这会导致“蜘蛛”不绝地欣赏Internet,寻找更多、更多的网站,所以,示例措施只会查找用户指定网站上的链接。
结构Spider类
前面已经讲了如何利用Spider类,请看例3中的代码。利用Spider类及“ISpiderReportable”接口能利便地为某一措施添加“蜘蛛”成果,下面继承讲授Spider类是奈何事情的。
Spider类必需保持对其会见过的URL的跟踪,这样做的目标是为了确保“蜘蛛”不会会见同一URL一次以上;进一步来说,“蜘蛛”必需把URL分成三组,第一组存储在“workloadWaiting”属性中,包括了一个未处理惩罚的URL列表,“蜘蛛”要会见的第一个URL也存在个中;第二组存储在“workloadProcessed”中,它是“蜘蛛”已经处理惩罚过且无需再次会见的URL;第三组存储在“workloadError”中,包括了产生错误的URL。
#p#分页标题#e#
Begin要领包括了Spider类的主轮回,其一直反复遍历“workloadWaiting”,并处理惩罚个中的每一个页面,虽然我们也想到了,在这些页面被处理惩罚时,很大概有其他的URL添加到“workloadWaiting”中,所以,begin要领一直继承此进程,直到挪用Spider类的cancel要领,或“workloadWaiting”中已不再剩有URL。这个进程如下:
cancel = false;
while ( !getWorkloadWaiting().isEmpty() && !cancel ) {
Object list[] = getWorkloadWaiting().toArray();
for ( int i=0; (i
processURL((URL)list[i]);
}
当上述代码遍历“workloadWaiting”时,它把每个需处理惩罚的URL都通报给“processURL”要领,而这个要领才是真正读取并理会URL中HTML信息的。
读取并理会HTML
Java同时支持会见URL内容及理会HTML,而这正是“processURL”要领要做的。在Java中读取URL内容相对还较量简朴,下面就是“processURL”要领实现此成果的代码:
URLConnection connection = url.openConnection();
if ( (connection.getContentType()!=null) &&
!connection.getContentType().toLowerCase()
.startsWith("text/") ) {
getWorkloadWaiting().remove(url);
getWorkloadProcessed().add(url);
log("Not processing because content type is: " +
connection.getContentType() );
return;
}
#p#副标题#e#
首先,为每个通报进来的变量url中存储的URL结构一个“URLConnection”工具,因为网站上会有多种范例的文档,而“蜘蛛”只对那些包括HTML,尤其是基于文本的文档感乐趣。前述代码是为了确保文档内容以“text/”打头,假如文档范例为非文本,会从期待区移除此URL,并把它添加到已处理惩罚区,这也是为了担保不会再次会见此URL。
在对特定URL成立毗连之后,接下来就要理会其内容了。下面的代码打开了URL毗连,并读取内容:
InputStream is = connection.getInputStream();
Reader r = new InputStreamReader(is);
此刻,我们有了一个Reader工具,可以用它来读取此URL的内容,对本文中的“蜘蛛”来说,只需简朴地把其内容通报给HTML理会器就可以了。本例中利用的HTML理会器为Swing HTML理会器,其由Java内置,但由于Java对HTML理会的支持力度不足,所以必需重载一个类来实现对HTML理会器的会见,这就是为什么我们要挪用“HTMLEditorKit”类中的“getParser”要领。但不幸的是,Sun公司把这个要领置为protected,独一的办理步伐就是建设本身的类并重载“getParser”要领,并把它置为public,这由“HTMLParse”类来实现,请看例4:
import javax.swing.text.html.*;
public class HTMLParse extends HTMLEditorKit {
public HTMLEditorKit.Parser getParser()
{
return super.getParser();
}
}
这个类用在Spider类的“processURL”要领中,我们也会看到,Reader工具会用于读取通报到“HTMLEditorKit.Parser”中网页的内容:
HTMLEditorKit.Parser parse = new HTMLParse().getParser();
parse.parse(r,new Parser(url),true);
请寄望,这里又结构了一个新的Parser类,这个Parser类是一个Spider类中的内嵌类,并且照旧一个回调类,它包括了对应于每种HTML tag将要挪用的特定要领。在本文中,我们只需体贴两类回调函数,它们别离对应一个简朴tag(即不带竣事tag的tag,如
)和一个开始tag,这两类回调函数名为“handleSimpleTag”和“handleStartTag”。因为每种的处理惩罚进程都是一样的,所以“handleStartTag”要领仅是简朴地挪用“handleSimpleTag”,而“handleSimpleTag”则会认真从文档中取出超链接,这些超链接将会用于定位“蜘蛛”要会见的其他页面。在当前tag被理会时,“handleSimpleTag”会查抄是否存在一个“href”或超文本引用:
String href = (String)a.getAttribute(HTML.Attribute.HREF);
if( (href==null) && (t==HTML.Tag.FRAME) )
href = (String)a.getAttribute(HTML.Attribute.SRC);
if ( href==null )
return;
假如不存在“href”属性,会继承查抄当前tag是否为一个Frame,Frame会利用一个“src”属性指向其他页面,一个典范的超链接凡是为以下形式:
Click Here
#p#分页标题#e#
上面链接中的“href”属性指向其链接到的页面,可是“linkedpage.html”不是一个地点,它只是指定了这个Web处事器上一个页面上的某处,这称为相对URL,相对URL必需被理会为绝对URL,而这由以下代码完成:
URL url = new URL(base,str);
这又会结构一个URL,str为相对URL,base为这个URL上的页面,这种形式的URL类结构函数可结构一个绝对URL。在URL变为正确的绝对形式之后,通过查抄它是否在期待区,来确认此URL是否已经被处理惩罚过。假如此URL没有被处理惩罚过,它会添加到期待区,之后,它会像其他URL一样被处理惩罚。
例1:查找死链接(ChcekLinks.java)
import java.awt.*;
import javax.swing.*;
import java.net.*;
import java.io.*;
public class CheckLinks extends javax.swing.JFrame implements
Runnable,ISpiderReportable {
public CheckLinks()
{
//{{INIT_CONTROLS
setTitle("找到死链接");
getContentPane().setLayout(null);
setSize(405,288);
setVisible(false);
label1.setText("输入一个URL:");
getContentPane().add(label1);
label1.setBounds(12,12,84,12);
begin.setText("Begin");
begin.setActionCommand("Begin");
getContentPane().add(begin);
begin.setBounds(12,36,84,24);
getContentPane().add(url);
url.setBounds(108,36,288,24);
errorScroll.setAutoscrolls(true);
errorScroll.setHorizontalScrollBarPolicy(javax.swing.
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
errorScroll.setVerticalScrollBarPolicy(javax.swing.
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
errorScroll.setOpaque(true);
getContentPane().add(errorScroll);
errorScroll.setBounds(12,120,384,156);
errors.setEditable(false);
errorScroll.getViewport().add(errors);
errors.setBounds(0,0,366,138);
current.setText("当前处理惩罚进度:");
getContentPane().add(current);
current.setBounds(12,72,384,12);
goodLinksLabel.setText("正常链接:0");
getContentPane().add(goodLinksLabel);
goodLinksLabel.setBounds(12,96,192,12);
badLinksLabel.setText("死链接:0");
getContentPane().add(badLinksLabel);
badLinksLabel.setBounds(216,96,96,12);
/