Apropos Crawler: Mit der in Java enthaltenen URLConnection können einige grundlegende Crawling-Funktionen für Seiten erreicht werden. Für einige erweiterte Funktionen wie die Umleitungsverarbeitung und das Entfernen von HTML-Tags reicht die Verwendung von URLConnection jedoch nicht aus.
Hier können wir das JAR-Paket eines Drittanbieters HttpClient verwenden.
Als Nächstes verwenden wir HttpClient, um einfach eine Demo zu schreiben, die nach Baidu crawlt:
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
/**
*
* @author CallMeWhy
*
*/
öffentliche Klasse Spider {
privater statischer HttpClient httpClient = new HttpClient();
/**
* @param Pfad
* Link zur Zielseite
* @return Gibt einen booleschen Wert zurück, der angibt, ob die Zielseite normal heruntergeladen wird
* @throwsException
* E/A-Ausnahme beim Lesen eines Webseiten-Streams oder beim Schreiben eines lokalen Datei-Streams
*/
public static boolean downloadPage(String path) löst eine Ausnahme aus {
//Eingabe- und Ausgabeströme definieren
InputStream input = null;
OutputStream-Ausgabe = null;
// Holen Sie sich die Post-Methode
GetMethod getMethod = new GetMethod(path);
//Ausführen, Statuscode zurückgeben
int statusCode = httpClient.executeMethod(getMethod);
// Statuscode verarbeiten
// Der Einfachheit halber wird nur der Statuscode mit einem Rückgabewert von 200 verarbeitet
if (statusCode == HttpStatus.SC_OK) {
input = getMethod.getResponseBodyAsStream();
// Den Dateinamen von der URL abrufen
String Dateiname = path.substring(path.lastIndexOf('/') + 1)
+ ".html";
// Holen Sie sich den Dateiausgabestream
Ausgabe = neuer FileOutputStream(Dateiname);
// Ausgabe in Datei
int tempByte = -1;
while ((tempByte = input.read()) > 0) {
Ausgabe.write(tempByte);
}
// Eingabestream schließen
if (Eingabe != null) {
input.close();
}
// Den Ausgabestream schließen
if (Ausgabe != null) {
Ausgabe.close();
}
return true;
}
return false;
}
public static void main(String[] args) {
versuchen {
// Baidu-Homepage holen und ausgeben
Spider.downloadPage("http://www.baidu.com");
} Catch (Ausnahme e) {
e.printStackTrace();
}
}
}
Ein solcher Basis-Crawler kann jedoch nicht die Anforderungen verschiedener Crawler erfüllen.
Lassen Sie uns zunächst den Breiten-First-Crawler vorstellen.
Ich glaube, jeder kennt Breiten-First-Crawler. In einfachen Worten kann man solche Breiten-First-Crawler verstehen.
Wir stellen uns das Internet als einen supergroßen gerichteten Graphen vor. Jeder Link auf einer Webseite ist eine gerichtete Kante, und jede Datei oder reine Seite ohne Link ist der Endpunkt des Graphen:
Der Breiten-First-Crawler ist ein solcher Crawler. Er kriecht auf diesem gerichteten Diagramm, ausgehend vom Wurzelknoten, und kriecht die Daten neuer Knoten Schicht für Schicht heraus.
Der Breitendurchlaufalgorithmus lautet wie folgt:
(1) Vertex V wird in die Warteschlange gestellt.
(2) Setzen Sie die Ausführung fort, wenn die Warteschlange nicht leer ist, andernfalls ist der Algorithmus leer.
(3) Entfernen Sie die Warteschlange, erhalten Sie den Kopfknoten V, besuchen Sie den Scheitelpunkt V und markieren Sie, dass V besucht wurde.
(4) Finden Sie die erste benachbarte Scheitelpunktspalte des Scheitelpunkts V.
(5) Wenn der benachbarte Scheitelpunkt col von V nicht besucht wurde, wird col in die Warteschlange gestellt.
(6) Suchen Sie weiter nach anderen benachbarten Scheitelpunkten von V und fahren Sie mit Schritt (5) fort. Wenn alle benachbarten Scheitelpunkte von V besucht wurden, fahren Sie mit Schritt (2) fort.
Gemäß dem Breitendurchlaufalgorithmus lautet die Durchlaufreihenfolge des obigen Bildes: A->B->C->D->E->F->H->G->I, sodass es Schicht für Schicht durchquert wird .
Der Breiten-First-Crawler durchsucht tatsächlich eine Reihe von Startknoten, was im Grunde dem Durchqueren von Diagrammen entspricht.
Wir können die URLs der Seiten, die gecrawlt werden müssen, in eine TODO-Tabelle und die besuchten Seiten in eine Visited-Tabelle einfügen:
Der grundlegende Prozess des Breiten-First-Crawlers ist wie folgt:
(1) Vergleichen Sie den analysierten Link mit dem Link in der Visited-Tabelle. Wenn der Link in der Visited-Tabelle nicht vorhanden ist, bedeutet dies, dass er nicht besucht wurde.
(2) Fügen Sie den Link in die TODO-Tabelle ein.
(3) Rufen Sie nach der Verarbeitung einen Link aus der TODO-Tabelle ab und fügen Sie ihn direkt in die Visited-Tabelle ein.
(4) Setzen Sie den obigen Vorgang für die durch diesen Link dargestellte Webseite fort. Und so weiter.
Als nächstes erstellen wir Schritt für Schritt einen Breiten-First-Crawler.
Entwerfen Sie zunächst eine Datenstruktur zum Speichern der TODO-Tabelle. Unter Berücksichtigung der Notwendigkeit des First-In-First-Out-Prinzips verwenden wir eine Warteschlange und passen eine Quere-Klasse an:
import java.util.LinkedList;
/**
* Benutzerdefinierte Warteschlangenklasse zum Speichern der TODO-Tabelle
*/
öffentliche Klassenwarteschlange {
/**
* Definieren Sie eine Warteschlange und implementieren Sie sie mithilfe von LinkedList
*/
private LinkedList<Object> queue = new LinkedList<Object>(); // Warteschlange
/**
* t zur Warteschlange hinzufügen
*/
public void enQueue(Object t) {
queue.addLast(t);
}
/**
* Entfernen Sie das erste Element aus der Warteschlange und geben Sie es zurück
*/
öffentliches Objekt deQueue() {
return queue.removeFirst();
}
/**
* Gibt zurück, ob die Warteschlange leer ist
*/
öffentlicher boolescher Wert isQueueEmpty() {
return queue.isEmpty();
}
/**
* Bestimmen Sie, ob die Warteschlange t enthält, und geben Sie sie zurück
*/
öffentliche boolesche Contians(Objekt t) {
return queue.contains(t);
}
/**
* Bestimmen und zurückgeben, ob die Warteschlange leer ist
*/
public boolean empty() {
return queue.isEmpty();
}
}
Außerdem ist eine Datenstruktur erforderlich, um die besuchten URLs aufzuzeichnen, nämlich die Visited-Tabelle.
In Anbetracht der Rolle dieser Tabelle wird bei jedem Zugriff auf eine URL zunächst in dieser Datenstruktur gesucht. Wenn die aktuelle URL bereits vorhanden ist, wird die URL-Aufgabe verworfen.
Diese Datenstruktur darf nicht dupliziert sein und kann schnell durchsucht werden. Daher wird HashSet für die Speicherung ausgewählt.
Zusammenfassend erstellen wir eine weitere SpiderQueue-Klasse, um die Visited-Tabelle und die TODO-Tabelle zu speichern:
import java.util.HashSet;
import java.util.Set;
/**
* Benutzerdefinierte Klasse zum Speichern der besuchten und nicht besuchten Tabelle
*/
öffentliche Klasse SpiderQueue {
/**
* Die besuchte URL-Sammlung, nämlich die Visited-Tabelle
*/
private static Set<Object> VisitUrl = new HashSet<>();
/**
* Zur Warteschlange für besuchte URLs hinzufügen
*/
public static void addVisitedUrl(String url) {
besuchteUrl.add(url);
}
/**
* Besuchte URLs entfernen
*/
public static void removeVisitedUrl(String url) {
besuchteUrl.remove(url);
}
/**
* Ermitteln Sie die Anzahl der besuchten URLs
*/
public static int getVisitedUrlNum() {
return besuchteUrl.size();
}
/**
* Die Sammlung der zu besuchenden URLs, also die nicht besuchte Tabelle
*/
private statische Warteschlange unVisitedUrl = new Queue();
/**
* Holen Sie sich die UnVisited-Warteschlange
*/
öffentliche statische Warteschlange getUnVisitedUrl() {
return unVisitedUrl;
}
/**
* Nicht besuchte unVisitedUrl wird aus der Warteschlange entfernt
*/
öffentliches statisches Objekt unVisitedUrlDeQueue() {
return unVisitedUrl.deQueue();
}
/**
* Stellen Sie sicher, dass jede URL nur einmal besucht wird, wenn Sie eine URL zu unVisitedUrl hinzufügen
*/
public static void addUnvisitedUrl(String url) {
if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
&& !unVisitedUrl.contians(url))
unVisitedUrl.enQueue(url);
}
/**
* Stellen Sie fest, ob die Warteschlange für nicht besuchte URLs leer ist
*/
öffentlicher statischer boolescher Wert unVisitedUrlsEmpty() {
return unVisitedUrl.empty();
}
}
Das Obige ist eine Kapselung einiger benutzerdefinierter Klassen. Der nächste Schritt besteht darin, eine Tool-Klasse zum Herunterladen von Webseiten zu definieren. Wir definieren sie als DownTool-Klasse.
Paketcontroller;
java.io.* importieren;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.params.*;
öffentliche Klasse DownTool {
/**
* Generieren Sie den Dateinamen der zu speichernden Webseite basierend auf der URL und dem Webseitentyp und entfernen Sie Zeichen, die keine Dateinamen sind, in der URL
*/
privater String getFileNameByUrl(String url, String contentType) {
// Entfernen Sie die sieben Zeichen „http://“
url = url.substring(7);
// Bestätigen Sie, dass die erfasste Seite vom Typ Text/HTML ist
if (contentType.indexOf("html") != -1) {
// Alle Sonderzeichen in URLs in Unterstriche umwandeln
url = url.replaceAll("[//?/:*|<>/"]", "_") + ".html";
} anders {
url = url.replaceAll("[//?/:*|<>/"]", "_") + "."
+ contentType.substring(contentType.lastIndexOf("/") + 1);
}
Rückgabe-URL;
}
/**
* Speichern Sie das Webseiten-Byte-Array in einer lokalen Datei. filePath ist die relative Adresse der zu speichernden Datei
*/
private void saveToLocal(byte[] data, String filePath) {
versuchen {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
neue Datei(filePath)));
for (int i = 0; i < data.length; i++)
out.write(data[i]);
out.flush();
out.close();
} Catch (IOException e) {
e.printStackTrace();
}
}
// Laden Sie die Webseite herunter, auf die die URL verweist
public String downloadFile(String url) {
String filePath = null;
// 1. HttpClinet-Objekt generieren und Parameter festlegen
HttpClient httpClient = new HttpClient();
//HTTP-Verbindungs-Timeout auf 5 Sekunden festlegen
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(5000);
// 2. GetMethod-Objekt generieren und Parameter festlegen
GetMethod getMethod = new GetMethod(url);
//Setzen Sie das Zeitlimit für die Get-Anfrage auf 5 Sekunden
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
//Verarbeitung der Anforderungswiederholung festlegen
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
// 3. GET-Anfrage ausführen
versuchen {
int statusCode = httpClient.executeMethod(getMethod);
// Bestimmen Sie den Zugriffsstatuscode
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Methode fehlgeschlagen: "
+ getMethod.getStatusLine());
filePath = null;
}
// 4. HTTP-Antwortinhalt verarbeiten
byte[] ResponseBody = getMethod.getResponseBody(); // Als Byte-Array lesen
// Beim Speichern den Dateinamen basierend auf der Webseiten-URL generieren
filePath = "temp//"
+ getFileNameByUrl(url,
getMethod.getResponseHeader("Content-Type")
.getValue());
saveToLocal(responseBody, filePath);
} Catch (HttpException e) {
//Es tritt eine schwerwiegende Ausnahme auf. Möglicherweise ist das Protokoll falsch oder mit dem zurückgegebenen Inhalt stimmt etwas nicht.
System.out.println("Bitte überprüfen Sie, ob Ihre http-Adresse korrekt ist");
e.printStackTrace();
} Catch (IOException e) {
//Eine Netzwerkausnahme tritt auf
e.printStackTrace();
} Endlich {
// Verbindung freigeben
getMethod.releaseConnection();
}
return filePath;
}
}
Hier benötigen wir eine HtmlParserTool-Klasse, um Html-Tags zu verarbeiten:
Paketcontroller;
import java.util.HashSet;
import java.util.Set;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import model.LinkFilter;
öffentliche Klasse HtmlParserTool {
// Links auf einer Website abrufen, Filter wird zum Filtern von Links verwendet
public static Set<String> extracLinks(String url, LinkFilter filter) {
Set<String> links = new HashSet<String>();
versuchen {
Parser parser = neuer Parser(URL);
parser.setEncoding("gb2312");
// Filtern Sie das <frame>-Tag, um das src-Attribut im Frame-Tag zu extrahieren
NodeFilter frameFilter = new NodeFilter() {
private static final long serialVersionUID = 1L;
@Override
öffentlicher boolescher Wert Accept(Node node) {
if (node.getText().startsWith("frame src=")) {
return true;
} anders {
return false;
}
}
};
// OrFilter zum Festlegen des Filter-<a>-Tags und <frame>-Tags
OrFilter linkFilter = new OrFilter(new NodeClassFilter(
LinkTag.class), FrameFilter);
// Alle gefilterten Tags abrufen
NodeList list = parser.extractAllNodesThatMatch(linkFilter);
for (int i = 0; i < list.size(); i++) {
Knotentag = list.elementAt(i);
if (Tag-Instanz von LinkTag)// <a> Tag
{
LinkTag link = (LinkTag)-Tag;
String linkUrl = link.getLink();// URL
if (filter.accept(linkUrl))
links.add(linkUrl);
} else// <frame>-Tag
{
// Extrahieren Sie den Link des src-Attributs im Frame, z. B. <frame src="test.html"/>
String Frame = tag.getText();
int start = frame.indexOf("src=");
Frame = Frame.Substring(Start);
int end = frame.indexOf(" ");
if (Ende == -1)
end = frame.indexOf(">");
String frameUrl = frame.substring(5, end - 1);
if (filter.accept(frameUrl))
links.add(frameUrl);
}
}
} Catch (ParserException e) {
e.printStackTrace();
}
Rücklinks;
}
}
Zum Schluss schreiben wir eine Crawler-Klasse, um die vorherige Kapselungsklasse und -funktion aufzurufen:
Paketcontroller;
import java.util.Set;
import model.LinkFilter;
model.SpiderQueue importieren;
öffentliche Klasse BfsSpider {
/**
* URL-Warteschlange mit Seed initialisieren
*/
private void initCrawlerWithSeeds(String[] seeds) {
for (int i = 0; i < seeds.length; i++)
SpiderQueue.addUnvisitedUrl(seeds[i]);
}
//Definieren Sie einen Filter, um Links zu extrahieren, die mit http://www.xxxx.com beginnen
public void crawling(String[] seeds) {
LinkFilter-Filter = neuer LinkFilter() {
öffentlicher boolescher Wert Accept(String URL) {
if (url.startsWith("http://www.baidu.com"))
return true;
anders
return false;
}
};
//URL-Warteschlange initialisieren
initCrawlerWithSeeds(seeds);
// Schleifenbedingung: Der zu crawlende Link ist nicht leer und die Anzahl der gecrawlten Webseiten beträgt nicht mehr als 1000
while (!SpiderQueue.unVisitedUrlsEmpty()
&& SpiderQueue.getVisitedUrlNum() <= 1000) {
//Warteschlangenkopf-URL aus der Warteschlange entfernt
String visitUrl = (String) SpiderQueue.unVisitedUrlDeQueue();
if (visitUrl == null)
weitermachen;
DownTool downLoader = new DownTool();
// Webseite herunterladen
downLoader.downloadFile(visitUrl);
// Fügen Sie diese URL in die besuchte URL ein
SpiderQueue.addVisitedUrl(visitUrl);
//Extrahieren Sie die URL von der Download-Webseite
Set<String> links = HtmlParserTool.extracLinks(visitUrl, filter);
// Neue nicht besuchte URLs werden in die Warteschlange gestellt
for (String link : links) {
SpiderQueue.addUnvisitedUrl(link);
}
}
}
//Hauptmethodeneintrag
public static void main(String[] args) {
BfsSpider crawler = new BfsSpider();
crawler.crawling(new String[] { "http://www.baidu.com" });
}
}
Nach dem Ausführen können Sie sehen, dass der Crawler alle Seiten unter der Baidu-Webseite gecrawlt hat:
Das Obige ist der gesamte Inhalt von Java, der das HttpClient-Toolkit und den Wide-Crawler zum Crawlen verwendet. Es ist etwas komplizierter, daher sollten Freunde sorgfältig darüber nachdenken. Ich hoffe, es kann für alle hilfreich sein.