World Wide Web,缩写WWW、W3或者Web,是一个因特网的相互连接的超文本文档。使用Web浏览器,可以查看一个文档,以及跟随超链接查看其它文档。这里,我们将开发一个简单的程序,可以跟随超链接来自动遍历Web。这类程序通常称为Web爬虫。为简单起见,我们的程序跟随以http://开始的超链接。
在写程序之前有必要了解一下什么是URL(Uniform Resource Location),即Web上的文件提供的唯一地址,可以叫做统一资源定位器,知道了这个便可以从Web上访问数据。
为了读取一个文件,首先要使用java.net.URL
类的构造方法,为该文件创建一个URL对象。例如,下面给出的语句为http://www.google.com/index.html
创建一个URL对象。
try {
URL url1=new URL("http://www.google.com/index.html");
}
catch (MalformedURLException ex) {
ex.printStackTrace();
}
创建一个URL对象后,可以使用URL类中定义的openStream()方法来打开输入流和用输入流创建Scanner对象。
Scanner input=new Scaner(url.openStream());
现在可以从输入流读取数据了,如同从本地文件中读取数据一样。
如图给出了一个遍历Web的例子。从一个包含了三个分别名为URL1,URL2,URL3的网址的页面开始,跟随URL2将到达一个包含两个名为URL21和URL22的网址的页面,跟随URL3将到达一个包含名为URL31、URL32、URL33、URL44的网址的页面。可以继续跟随新的超链接对Web进行遍历。这个过程可以一直进行下去,但是我们将在遍历了100个页面后退出程序。
程序跟随URL来遍历Web。为保证每一个URL只被遍历一次,程序包含两个网址的列表。一个列表保存将被遍历的网址,另一个保存已经被遍历的网址。程序的算法描述如下:
将起始URL添加到名为listOfPendingURLs的列表中;
当listOfPendingURLs不为空并且listOfTraversedURLs的长度<=100 {
从listOfPendingURLs移除一个URL;
如果该URL不在listOfTraversedURLs中 {
将其添加到listOfTraversedURLs中;
显示该URL;
读取该URL的页面,并且对该页面中包含的每个URL进行如下操作 {
如果不在listOfPendingURLs中,则将其添加到listOfPendingURLs中;
}
}
}
实现该算法的完整程序如下:
package com.company;
import java.util.Scanner;
import java.util.ArrayList;
public class WebCrawler {
public static void main(String[] args) {
java.util.Scanner input=new java.util.Scanner(System.in);
System.out.print("Enter a URL: ");
String url=input.nextLine();
craweler(url);
}
public static void craweler(String startingURL) {
ArrayList<String> listOfPendingURLs=new ArrayList<>();
ArrayList<String> listOfTraversedURLs=new ArrayList<>();
//将起始的URL添加到listOfPendingURLs,然后通过一个while循环重复处理listOfPendingURLs中每一个URL
listOfPendingURLs.add(startingURL);
while(!listOfPendingURLs.isEmpty()&&listOfTraversedURLs.size()<=100) {
//将列表中第一个RUL去除,如果该RUL没有被处理过则对其进行处理
String urlString=listOfPendingURLs.remove(0);
if(!listOfTraversedURLs.contains(urlString)) {
listOfTraversedURLs.add(urlString);
System.out.println("Crawl"+urlString);
//程序使用foreach循环,将页面中的每个不存在listOfTraversedURLs中的URL添加到listOfPendingURLs中
for(String s:getSubURLs(urlString)) {
if(!listOfTraversedURLs.contains(s)) listOfPendingURLs.add(s);
}
}
}
}
public static ArrayList<String> getSubURLs(String urlString) {
//该方法为每个给定的URL返回一个URL列表
ArrayList<String> list=new ArrayList<>();
try {
java.net.URL url=new java.net.URL(urlString);
Scanner input=new Scanner(url.openStream());
int current=0;
while(input.hasNext()) {
String line=input.nextLine();//从Web读取每一行
current=line.indexOf("http:",current);//寻找该行中的URL
while(current>0) {
int endIndex=line.indexOf("\"",current);//假设URL以引号"结束
if(endIndex>0) {
list.add(line.substring(current,endIndex));//一行中可能包含多个URL,
current=line.indexOf("http:",endIndex);//方法继续寻找下一个URL
}
else current=-1;//如果该行中没有发现URL,curr设为-1
}
}
}
catch (Exception ex) {
System.out.println("Error: "+ex.getMessage());
}
return list;//页面中包含的URL以一个列表的形式返回
}
}
运行结果如下: