クローラーについて言えば、Java に付属の URLConnection を使用すると、いくつかの基本的なページ クローリング機能を実現できますが、リダイレクト処理や HTML タグの削除などのより高度な機能については、URLConnection を使用するだけでは十分ではありません。
ここでは、サードパーティの jar パッケージ HttpClient を使用できます。
次に、HttpClient を使用して、Baidu にクロールするデモを簡単に作成します。
java.io.FileOutputStreamをインポートします。
java.io.InputStreamをインポートします。
java.io.OutputStreamをインポートします。
org.apache.commons.httpclient.HttpClientをインポートします。
org.apache.commons.httpclient.HttpStatus をインポートします。
org.apache.commons.httpclient.methods.GetMethod をインポートします。
/**
*
* @author CallMeWhy
*
*/
パブリッククラス Spider {
プライベート静的 HttpClient httpClient = new HttpClient();
/**
* @パラメータパス
※対象ページへのリンク
* @return 対象ページが正常にダウンロードされたかどうかを示すブール値を返します。
* @throwsException
* Web ページ ストリームの読み取り時またはローカル ファイル ストリームの書き込み時の IO 例外
*/
public static boolean downloadPage(String path) throws Exception {
//入力ストリームと出力ストリームを定義する
InputStream 入力 = null;
OutputStream 出力 = null;
// 投稿メソッドを取得する
GetMethod getMethod = 新しい GetMethod(パス);
//実行してステータスコードを返す
int statusCode = httpClient.executeMethod(getMethod);
// ステータスコードを処理する
// 簡単のため、戻り値 200 のステータス コードのみが処理されます
if (statusCode == HttpStatus.SC_OK) {
入力 = getMethod.getResponseBodyAsStream();
// URLからファイル名を取得する
文字列ファイル名 = path.substring(path.lastIndexOf('/') + 1)
+ ".html";
// ファイル出力ストリームを取得します
出力 = 新しい FileOutputStream(ファイル名);
// ファイルに出力
int tempByte = -1;
while ((tempByte = input.read()) > 0) {
出力.write(tempByte);
}
// 入力ストリームを閉じる
if (入力 != null) {
input.close();
}
// 出力ストリームを閉じる
if (出力 != null) {
出力.close();
}
true を返します。
}
false を返します。
}
public static void main(String[] args) {
試す {
// Baidu のホームページを取得して出力します
Spider.downloadPage("http://www.baidu.com");
} catch (例外 e) {
e.printStackTrace();
}
}
}
しかし、このような基本的なクローラでは、さまざまなクローラのニーズを満たすことができません。
まずは幅優先クローラを紹介しましょう。
幅優先については皆さんよくご存知だと思いますが、簡単に言うと幅優先クローラーはこのように理解できます。
私たちはインターネットを超巨大な有向グラフとして考えます。Web ページ上のすべてのリンクは有向エッジであり、リンクのないすべてのファイルまたは純粋なページはグラフの終点です。
幅優先クローラはこのようなクローラであり、ルート ノードから開始して新しいノードのデータを層ごとに巡回します。
幅トラバーサルアルゴリズムは次のとおりです。
(1) 頂点 V がキューに入れられます。
(2) キューが空でない場合は実行を続行します。そうでない場合はアルゴリズムが空です。
(3) デキューし、ヘッド ノード V を取得し、頂点 V を訪問し、V が訪問済みであることをマークします。
(4) 頂点 V の最初の隣接頂点列を見つけます。
(5)Vの隣接する頂点colが訪問されていない場合、colはキューに入れられる。
(6)Vの他の隣接する頂点colを検索し続け、Vのすべての隣接する頂点を訪問した場合、ステップ(2)に進む。
幅トラバーサル アルゴリズムによると、上の図のトラバース順序は A->B->C->D->E->F->H->G->I となり、レイヤーごとにトラバースされます。 。
幅優先クローラは実際に一連のシード ノードをクロールします。これは基本的にグラフ トラバーサルと同じです。
クロールする必要があるページの URL を TODO テーブルに入れ、訪問したページを Visited テーブルに入れることができます。
幅優先クローラの基本プロセスは次のとおりです。
(1) 解析されたリンクと Visited テーブル内のリンクを比較します。リンクが Visited テーブルに存在しない場合は、そのリンクは訪問されていないことを意味します。
(2) TODOテーブルにリンクを貼ります。
(3) 処理後、TODO テーブルからリンクを取得し、Visited テーブルに直接配置します。
(4) このリンクで表される Web ページに対して上記のプロセスを続行します。等々。
次に、幅優先クローラを段階的に作成していきます。
まず、TODO テーブルを格納するデータ構造を設計します。先入れ先出しの必要性を考慮して、キューを使用し、Quere クラスをカスタマイズします。
java.util.LinkedListをインポートします。
/**
* TODOテーブルを保存するためのカスタムキュークラス
*/
パブリック クラス キュー {
/**
* キューを定義し、LinkedList を使用して実装する
*/
private LinkedList<Object> queue = new LinkedList<Object>();
/**
* t をキューに追加します
*/
public void enQueue(Object t) {
queue.addLast(t);
}
/**
* キューから最初の項目を削除して返します
*/
public Object deQueue() {
戻りキュー.removeFirst();
}
/**
* キューが空かどうかを返します
*/
public boolean isQueueEmpty() {
戻りキュー.isEmpty();
}
/**
* キューに t が含まれているかどうかを判断して返します。
*/
public boolean contians(Object t) {
戻りキュー.contains(t);
}
/**
* キューが空かどうかを判断して返します
*/
public boolean empty() {
戻りキュー.isEmpty();
}
}
訪問された URL を記録するためのデータ構造、つまり Visited テーブルも必要です。
このテーブルの役割を考慮すると、URL にアクセスするときは、まずこのデータ構造内で検索が行われ、現在の URL がすでに存在する場合、URL タスクは破棄されます。
このデータ構造は重複がなく、迅速に検索できる必要があるため、ストレージには HashSet が選択されます。
要約すると、別の SpiderQueue クラスを作成して Visited テーブルと TODO テーブルを保存します。
java.util.HashSet をインポートします。
java.util.Setをインポートします。
/**
* 訪問済みテーブルと未訪問テーブルを保存するためのカスタム クラス
*/
パブリック クラス SpiderQueue {
/**
* 訪問済み URL コレクション、つまり訪問済みテーブル
*/
private static Set<Object> VisitUrl = new HashSet<>();
/**
* 訪問済み URL キューに追加
*/
public static void addVisitedUrl(String url) {
VisitUrl.add(url);
}
/**
* アクセスした URL を削除します
*/
public static void RemoveVisitedUrl(String url) {
VisitUrl.remove(url);
}
/**
* アクセスしたURLの数を取得する
*/
public static int getVisitedUrlNum() {
戻り値の VisitUrl.size();
}
/**
* 訪問する URL のコレクション、つまり unVisited テーブル
*/
プライベート静的キュー unVisitedUrl = new Queue();
/**
* UnVisited キューを取得する
*/
パブリック静的キュー getUnVisitedUrl() {
unVisitedUrl を返します。
}
/**
* 未訪問の unVisitedUrl がデキューされます
*/
public static Object unVisitedUrlDeQueue() {
unVisitedUrl.deQueue() を返します。
}
/**
* URL を unVisitedUrl に追加するときは、各 URL が 1 回だけアクセスされるようにしてください
*/
public static void addUnvisitedUrl(String url) {
if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
&& !unVisitedUrl.contians(url))
unVisitedUrl.enQueue(url);
}
/**
* 未訪問の URL キューが空かどうかを判断します
*/
public static boolean unVisitedUrlsEmpty() {
unVisitedUrl.empty() を返します。
}
}
上記はいくつかのカスタム クラスのカプセル化です。次のステップでは、Web ページをダウンロードするためのツール クラスを DownTool クラスとして定義します。
パッケージコントローラー;
java.io.* をインポートします。
org.apache.commons.httpclient.* をインポートします。
org.apache.commons.httpclient.methods.* をインポートします。
org.apache.commons.httpclient.params.* をインポートします。
パブリック クラス DownTool {
/**
* URL と Web ページの種類に基づいて保存する Web ページのファイル名を生成し、URL 内のファイル名以外の文字を削除します
*/
private String getFileNameByUrl(String url, String contentType) {
// 7 文字「http://」を削除します。
URL = URL.substring(7);
// キャプチャされたページが text/html タイプであることを確認します
if (contentType.indexOf("html") != -1) {
// URL 内のすべての特殊記号をアンダースコアに変換します
url = url.replaceAll("[//?/:*|<>/"]", "_") + ".html";
} それ以外 {
url = url.replaceAll("[//?/:*|<>/"]", "_") + "."
+ contentType.substring(contentType.lastIndexOf("/") + 1);
}
URLを返します;
}
/**
* Web ページのバイト配列をローカル ファイルに保存します。filePath は保存するファイルの相対アドレスです。
*/
private void saveToLocal(byte[] data, String filePath) {
試す {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
新しいファイル(ファイルパス)));
for (int i = 0; i < data.length; i++)
out.write(データ[i]);
out.flush();
out.close();
} キャッチ (IOException e) {
e.printStackTrace();
}
}
// URL で指定された Web ページをダウンロードします
public String downloadFile(String url) {
文字列ファイルパス = null;
// 1. HttpClinet オブジェクトを生成し、パラメーターを設定します
HttpClient httpClient = new HttpClient();
//HTTP接続タイムアウトを5秒に設定
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(5000);
// 2. GetMethod オブジェクトを生成し、パラメーターを設定します
GetMethod getMethod = 新しい GetMethod(url);
// 取得リクエストのタイムアウトを 5 秒に設定します
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
//リクエストのリトライ処理を設定する
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
新しい DefaultHttpMethodRetryHandler());
// 3. GETリクエストを実行する
試す {
int statusCode = httpClient.executeMethod(getMethod);
// アクセスステータスコードを決定する
if (ステータスコード != HttpStatus.SC_OK) {
System.err.println("メソッドが失敗しました: "
+ getMethod.getStatusLine());
ファイルパス = null;
}
// 4. HTTP 応答コンテンツを処理する
byte[] responseBody = getMethod.getResponseBody() // バイト配列として読み込む
// Web ページの URL に基づいて保存時にファイル名を生成します
ファイルパス = "temp//"
+ getFileNameByUrl(url,
getMethod.getResponseHeader("コンテンツタイプ")
.getValue());
saveToLocal(responseBody, filePath);
} catch (HttpException e) {
//致命的な例外が発生しました。プロトコルが間違っているか、返されたコンテンツに問題がある可能性があります。
System.out.println("http アドレスが正しいかどうかを確認してください");
e.printStackTrace();
} キャッチ (IOException e) {
//ネットワーク例外が発生する
e.printStackTrace();
} ついに {
// 接続を解放します
getMethod.releaseConnection();
}
ファイルパスを返します。
}
}
ここでは、Html タグを処理するための HtmlParserTool クラスが必要です。
パッケージコントローラー;
java.util.HashSet をインポートします。
java.util.Setをインポートします。
org.htmlparser.Node をインポートします。
org.htmlparser.NodeFilter をインポートします。
org.htmlparser.Parser をインポートします。
org.htmlparser.filters.NodeClassFilter をインポートします。
org.htmlparser.filters.OrFilter をインポートします。
org.htmlparser.tags.LinkTag をインポートします。
org.htmlparser.util.NodeList をインポートします。
org.htmlparser.util.ParserException をインポートします。
インポートモデル.LinkFilter;
パブリック クラス HtmlParserTool {
// Web サイト上のリンクを取得します。フィルターはリンクのフィルターに使用されます
public static Set<String> extracLinks(String url, LinkFilter フィルター) {
Set<String> リンク = new HashSet<String>();
試す {
パーサー parser = 新しいパーサー(url);
parser.setEncoding("gb2312");
// フレームタグ内の src 属性を抽出するために使用される <frame> タグをフィルタリングします。
NodeFilter FrameFilter = new NodeFilter() {
プライベート静的最終ロングシリアルバージョンUID = 1L;
@オーバーライド
public boolean accept(Node ノード) {
if (node.getText().startsWith("frame src=")) {
true を返します。
} それ以外 {
false を返します。
}
}
};
// OrFilter でフィルター <a> タグと <frame> タグを設定します
OrFilter linkFilter = new OrFilter(new NodeClassFilter(
LinkTag.class)、frameFilter);
// フィルタリングされたすべてのタグを取得します
NodeList リスト = parser.extractAllNodesThatMatch(linkFilter);
for (int i = 0; i < list.size(); i++) {
ノードタグ = list.elementAt(i);
if (タグインスタンスオブリンクタグ)// <a> タグ
{
LinkTag リンク = (LinkTag) タグ;
String linkUrl = link.getLink();// URL
if (filter.accept(linkUrl))
リンク.add(リンクURL);
} else// <frame> タグ
{
// <frame src="test.html"/> など、フレーム内の src 属性のリンクを抽出します。
文字列フレーム = tag.getText();
int start = Frame.indexOf("src=");
フレーム = フレーム.サブストリング(開始);
int end = フレーム.indexOf(" ");
if (終了 == -1)
終了 = フレーム.indexOf(">");
文字列フレーム Url = フレーム.サブストリング(5, 終了 - 1);
if (filter.accept(frameUrl))
リンク.add(フレームUrl);
}
}
} catch (ParserException e) {
e.printStackTrace();
}
リンクを返す。
}
}
最後に、前のカプセル化クラスと関数を呼び出すクローラー クラスを作成しましょう。
パッケージコントローラー;
java.util.Setをインポートします。
インポートモデル.LinkFilter;
インポートモデル.SpiderQueue;
パブリック クラス BfsSpider {
/**
* シードを使用して URL キューを初期化する
*/
private void initCrawlerWithSeeds(String[] シード) {
for (int i = 0; i < シード.長さ; i++)
SpiderQueue.addUnvisitedUrl(seeds[i]);
}
// http://www.xxxx.com で始まるリンクを抽出するフィルターを定義します。
public void クローリング(String[] シード) {
LinkFilter フィルター = new LinkFilter() {
public boolean accept(String url) {
if (url.startsWith("http://www.baidu.com"))
true を返します。
それ以外
false を返します。
}
};
//URLキューを初期化する
initCrawlerWithSeeds(シード);
// ループ条件: クロール対象のリンクが空ではなく、クロールされた Web ページの数が 1000 以下
while (!SpiderQueue.unVisitedUrlsEmpty()
&& SpiderQueue.getVisitedUrlNum() <= 1000) {
// キューヘッド URL がデキューされました
文字列 visitUrl = (文字列) SpiderQueue.unVisitedUrlDeQueue();
if (visitUrl == null)
続く;
DownTool downLoader = new DownTool();
// Web ページをダウンロードする
downLoader.downloadFile(visitUrl);
// この URL を訪問先 URL に入れます
SpiderQueue.addVisitedUrl(visitUrl);
// ダウンロード Web ページから URL を抽出します
Set<String> links = HtmlParserTool.exracLinks(visitUrl, filter);
// 新しい未訪問の URL はキューに入れられます
for (文字列リンク:リンク) {
SpiderQueue.addUnvisitedUrl(リンク);
}
}
}
//メインメソッドのエントリ
public static void main(String[] args) {
BfsSpider クローラー = 新しい BfsSpider();
crawler.crawling(new String[] { "http://www.baidu.com" });
}
}
実行後、クローラーが Baidu Web ページの下のすべてのページをクロールしたことがわかります。
上記は、HttpClient ツールキットと幅クローラーを使用してコンテンツをクロールする Java の内容全体です。少し複雑なので、皆さんの参考になれば幸いです。