The wind of freedom blows
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
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以一個列表的形式返回
}
}
執行結果如下: