En parlant de robots d'exploration, l'utilisation d'URLConnection fournie avec Java peut réaliser certaines fonctions d'exploration de page de base, mais pour certaines fonctions plus avancées, telles que le traitement de redirection et la suppression des balises HTML, la simple utilisation d'URLConnection ne suffit pas.
Ici, nous pouvons utiliser le package jar tiers HttpClient.
Ensuite, nous utilisons HttpClient pour écrire simplement une démo qui explore Baidu :
importer java.io.FileOutputStream ;
importer java.io.InputStream ;
importer java.io.OutputStream ;
importer org.apache.commons.httpclient.HttpClient ;
importer org.apache.commons.httpclient.HttpStatus ;
importer org.apache.commons.httpclient.methods.GetMethod ;
/**
*
* @auteur Appelez-moiPourquoi
*
*/
classe publique Araignée {
privé statique HttpClient httpClient = new HttpClient();
/**
* Chemin @param
* Lien vers la page cible
* @return Renvoie une valeur booléenne, indiquant si la page cible est téléchargée normalement
* @throwsException
* Exception IO lors de la lecture d'un flux de page Web ou de l'écriture d'un flux de fichiers local
*/
public static boolean downloadPage (String path) lève une exception {
//Définir les flux d'entrée et de sortie
Entrée InputStream = null ;
Sortie OutputStream = null ;
// Récupère la méthode post
GetMethod getMethod = new GetMethod(chemin);
//Exécuter, renvoyer le code d'état
int statusCode = httpClient.executeMethod(getMethod);
// Traite le code d'état
// Par souci de simplicité, seul le code d'état avec une valeur de retour de 200 est traité
si (statusCode == HttpStatus.SC_OK) {
input = getMethod.getResponseBodyAsStream();
// Récupère le nom du fichier à partir de l'URL
Nom de fichier de chaîne = chemin.substring(path.lastIndexOf('/') + 1)
+ ".html" ;
// Récupère le flux de sortie du fichier
sortie = nouveau FileOutputStream (nom de fichier);
// Sortie dans un fichier
int tempByte = -1;
while ((tempByte = input.read()) > 0) {
sortie.write(tempByte);
}
// Ferme le flux d'entrée
si (entrée != null) {
input.close();
}
// Ferme le flux de sortie
if (sortie != null) {
sortie.close();
}
renvoie vrai ;
}
renvoie faux ;
}
public static void main (String[] arguments) {
essayer {
// Récupère la page d'accueil et la sortie de Baidu
Spider.downloadPage("http://www.baidu.com");
} attraper (Exception e) {
e.printStackTrace();
}
}
}
Cependant, un robot d’exploration aussi basique ne peut pas répondre aux besoins des différents robots d’exploration.
Commençons par présenter le robot d'exploration en largeur.
Je pense que tout le monde connaît la largeur d'abord. En termes simples, vous pouvez comprendre les robots d'exploration de largeur d'abord comme celui-ci.
Nous considérons Internet comme un très grand graphe orienté. Chaque lien sur une page Web est un bord orienté, et chaque fichier ou page pure sans lien est le point final du graphe :
Le robot d'exploration en largeur est un tel robot. Il explore ce graphe orienté, en commençant par le nœud racine et en explorant les données des nouveaux nœuds couche par couche.
L'algorithme de parcours en largeur est le suivant :
(1) Le sommet V est mis dans la file d'attente.
(2) Continuez l'exécution lorsque la file d'attente n'est pas vide, sinon l'algorithme est vide.
(3) Retirer la file d'attente, obtenir le nœud principal V, visiter le sommet V et marquer que V a été visité.
(4) Trouvez le premier col de sommet adjacent du sommet V.
(5) Si le sommet col adjacent de V n'a pas été visité, alors col est mis dans la file d'attente.
(6) Continuez à rechercher d'autres sommets adjacents col de V et passez à l'étape (5). Si tous les sommets adjacents de V ont été visités, passez à l'étape (2).
Selon l'algorithme de parcours en largeur, l'ordre de parcours de l'image ci-dessus est : A->B->C->D->E->F->H->G->I, de sorte qu'il soit parcouru couche par couche. .
Le robot d'exploration en largeur explore en fait une série de nœuds de départ, ce qui est fondamentalement identique à la traversée d'un graphique.
Nous pouvons mettre les URL des pages qui doivent être explorées dans une table TODO, et les pages visitées dans une table Visité :
Le processus de base du robot d'exploration en largeur en premier est le suivant :
(1) Comparez le lien analysé avec le lien de la table Visité. Si le lien n'existe pas dans la table Visité, cela signifie qu'il n'a pas été visité.
(2) Mettez le lien dans la table TODO.
(3) Après traitement, obtenez un lien de la table TODO et placez-le directement dans la table Visité.
(4) Continuez le processus ci-dessus pour la page Web représentée par ce lien. Et ainsi de suite.
Ensuite, nous allons créer étape par étape un robot d'exploration de largeur d'abord.
Tout d'abord, concevons une structure de données pour stocker la table TODO. Compte tenu de la nécessité du premier entré, premier sorti, nous utilisons une file d'attente et personnalisons une classe Quere :
importer java.util.LinkedList ;
/**
* Classe de file d'attente personnalisée pour enregistrer la table TODO
*/
File d'attente de classe publique {
/**
* Définir une file d'attente et l'implémenter à l'aide de LinkedList
*/
file d'attente privée LinkedList<Object> = new LinkedList<Object>();
/**
* Ajouter t à la file d'attente
*/
public void enQueue (Objet t) {
file d'attente.addLast(t);
}
/**
* Supprimez le premier élément de la file d'attente et renvoyez-le
*/
Objet public deQueue() {
return queue.removeFirst();
}
/**
* Indique si la file d'attente est vide
*/
public booléen isQueueEmpty() {
return queue.isEmpty();
}
/**
* Déterminer et renvoyer si la file d'attente contient t
*/
contiens booléens publics (Objet t) {
return queue.contains(t);
}
/**
* Déterminer et renvoyer si la file d'attente est vide
*/
public booléen vide() {
return queue.isEmpty();
}
}
Une structure de données est également nécessaire pour enregistrer les URL qui ont été visitées, à savoir la table Visited.
Compte tenu du rôle de cette table, chaque fois qu'une URL doit être consultée, elle est d'abord recherchée dans cette structure de données. Si l'URL actuelle existe déjà, la tâche URL est ignorée.
Cette structure de données ne doit pas être dupliquée et peut être recherchée rapidement, c'est pourquoi HashSet est choisi pour le stockage.
Pour résumer, nous créons une autre classe SpiderQueue pour sauvegarder la table Visited et la table TODO :
importer java.util.HashSet ;
importer java.util.Set ;
/**
* Classe personnalisée pour enregistrer la table visitée et la table non visitée
*/
classe publique SpiderQueue {
/**
* La collection d'url visitées, à savoir la table Visité
*/
private static Set<Object> visitUrl = new HashSet<>();
/**
* Ajouter à la file d'attente des URL visitées
*/
public static void addVisitedUrl (String url) {
visitéUrl.add(url);
}
/**
* Supprimer les URL visitées
*/
public static void removeVisitedUrl (String url) {
visitéUrl.remove(url);
}
/**
* Obtenez le nombre d'URL visitées
*/
public static int getVisitedUrlNum() {
return visitUrl.size();
}
/**
* La collection d'URL à visiter, c'est-à-dire la table non visitée
*/
File d'attente statique privée unVisitedUrl = new Queue();
/**
* Obtenez la file d'attente non visitée
*/
File d'attente statique publique getUnVisitedUrl() {
retourner unVisitedUrl ;
}
/**
* L'URL non visitée non visitée est retirée de la file d'attente.
*/
Objet statique public unVisitedUrlDeQueue() {
return unVisitedUrl.deQueue();
}
/**
* Assurez-vous que chaque URL n'est visitée qu'une seule fois lors de l'ajout d'une URL à unVisitedUrl
*/
public static void addUnvisitedUrl (URL de chaîne) {
if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
&& !unVisitedUrl.contians(url))
unVisitedUrl.enQueue(url);
}
/**
* Déterminez si la file d'attente des URL non visitées est vide
*/
public statique booléen unVisitedUrlsEmpty() {
return unVisitedUrl.empty();
}
}
Ce qui précède est une encapsulation de certaines classes personnalisées. L'étape suivante consiste à définir une classe d'outils pour télécharger des pages Web. Nous la définissons comme la classe DownTool :
contrôleur de paquet ;
importer java.io.* ;
importer org.apache.commons.httpclient.* ;
importer org.apache.commons.httpclient.methods.* ;
importer org.apache.commons.httpclient.params.* ;
classe publique DownTool {
/**
* Générez le nom de fichier de la page Web à enregistrer en fonction de l'URL et du type de page Web, et supprimez les caractères autres que le nom de fichier dans l'URL.
*/
chaîne privée getFileNameByUrl (URL de chaîne, type de contenu de chaîne) {
// Supprime les sept caractères "http://"
url = url.substring(7);
// Confirmez que la page capturée est de type texte/html
if (contentType.indexOf("html") != -1) {
// Convertit tous les symboles spéciaux des URL en traits de soulignement
url = url.replaceAll("[//?/:*|<>/"]", "_") + ".html";
} autre {
url = url.replaceAll("[//?/:*|<>/"]", "_") + "."
+ contentType.substring(contentType.lastIndexOf("/") + 1);
}
URL de retour ;
}
/**
* Enregistrez le tableau d'octets de la page Web dans un fichier local, filePath est l'adresse relative du fichier à enregistrer
*/
private void saveToLocal (octet [] données, chaîne filePath) {
essayer {
DataOutputStream out = nouveau DataOutputStream(nouveau FileOutputStream(
nouveau fichier (filePath)));
pour (int i = 0; i < data.length; i++)
out.write(data[i]);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Téléchargez la page Web pointée par l'URL
public String downloadFile (URL de la chaîne) {
Chaîne filePath = null ;
// 1. Générer l'objet HttpClinet et définir les paramètres
HttpClient httpClient = new HttpClient();
//Définit le délai d'expiration de la connexion HTTP sur 5 s
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(5000);
// 2. Générer l'objet GetMethod et définir les paramètres
GetMethod getMethod = new GetMethod(url);
//Définit le délai d'attente de la requête d'obtention sur 5 s
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
//Définir le traitement des nouvelles tentatives de requête
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
// 3. Exécuter la requête GET
essayer {
int statusCode = httpClient.executeMethod(getMethod);
// Détermine le code d'état d'accès
si (statusCode != HttpStatus.SC_OK) {
System.err.println("Échec de la méthode : "
+ getMethod.getStatusLine());
cheminfichier = null ;
}
// 4. Traiter le contenu de la réponse HTTP
byte[] ResponseBody = getMethod.getResponseBody(); // Lire sous forme de tableau d'octets
// Génère le nom du fichier lors de l'enregistrement en fonction de l'URL de la page Web
cheminfichier = "temp//"
+ getFileNameByUrl(url,
getMethod.getResponseHeader("Type de contenu")
.getValue());
saveToLocal(responseBody, filePath);
} catch (HttpException e) {
//Une exception fatale se produit. Il se peut que le protocole soit incorrect ou qu'il y ait un problème avec le contenu renvoyé.
System.out.println("Veuillez vérifier si votre adresse http est correcte");
e.printStackTrace();
} catch (IOException e) {
//Une exception réseau se produit
e.printStackTrace();
} enfin {
// Libère la connexion
getMethod.releaseConnection();
}
retourner le chemin du fichier ;
}
}
Ici, nous avons besoin d'une classe HtmlParserTool pour gérer les balises HTML :
contrôleur de paquet ;
importer java.util.HashSet ;
importer java.util.Set ;
importer org.htmlparser.Node ;
importer org.htmlparser.NodeFilter ;
importer org.htmlparser.Parser ;
importer org.htmlparser.filters.NodeClassFilter ;
importer org.htmlparser.filters.OrFilter ;
importer org.htmlparser.tags.LinkTag ;
importer org.htmlparser.util.NodeList ;
importer org.htmlparser.util.ParserException ;
importer le modèle.LinkFilter ;
classe publique HtmlParserTool {
// Récupère des liens sur un site Web, le filtre est utilisé pour filtrer les liens
public static Set<String> extracLinks (URL de chaîne, filtre LinkFilter) {
Set<String> links = new HashSet<String>();
essayer {
Analyseur parser = new Parser(url);
parser.setEncoding("gb2312");
// Filtre la balise <frame>, utilisée pour extraire l'attribut src dans la balise frame
NodeFilter frameFilter = nouveau NodeFilter() {
privé statique final long serialVersionUID = 1L ;
@Outrepasser
public boolean accept (nœud de nœud) {
if (node.getText().startsWith("frame src=")) {
renvoie vrai ;
} autre {
renvoie faux ;
}
}
} ;
// OrFilter pour définir la balise de filtre <a> et la balise <frame>
OrFilter linkFilter = nouveau OrFilter (nouveau NodeClassFilter (
LinkTag.class), frameFilter);
// Récupère toutes les balises filtrées
Liste NodeList = parser.extractAllNodesThatMatch(linkFilter);
pour (int i = 0; i < list.size(); i++) {
Balise de nœud = list.elementAt(i);
if (balise instance de LinkTag)// balise <a>
{
Lien LinkTag = balise (LinkTag) ;
Chaîne linkUrl = link.getLink();// URL
si (filter.accepter(linkUrl))
liens.add(linkUrl);
} else//balise <frame>
{
// Extrait le lien de l'attribut src dans le frame, tel que <frame src="test.html"/>
Cadre de chaîne = tag.getText();
int start = frame.indexOf("src=");
frame = frame.substring(début);
int end = frame.indexOf(" ");
si (fin == -1)
end = frame.indexOf(">");
Chaîne frameUrl = frame.substring(5, end - 1);
si (filter.accepter(frameUrl))
liens.add(frameUrl);
}
}
} catch (ParserException e) {
e.printStackTrace();
}
liens de retour ;
}
}
Enfin, écrivons une classe de robot pour appeler la classe et la fonction d'encapsulation précédentes :
contrôleur de paquet ;
importer java.util.Set ;
importer le modèle.LinkFilter ;
importer le modèle.SpiderQueue ;
classe publique BfsSpider {
/**
* Initialiser la file d'attente d'URL à l'aide de seed
*/
private void initCrawlerWithSeeds (graines String[]) {
pour (int i = 0; i < graines.longueur; i++)
SpiderQueue.addUnvisitedUrl(seeds[i]);
}
//Définissez un filtre pour extraire les liens commençant par http://www.xxxx.com
exploration du vide public (graines String[]) {
Filtre LinkFilter = nouveau LinkFilter() {
public booléen accepter (URL de chaîne) {
si (url.startsWith("http://www.baidu.com"))
renvoie vrai ;
autre
renvoie faux ;
}
} ;
// Initialiser la file d'attente des URL
initCrawlerWithSeeds(graines);
// Condition de boucle : le lien à explorer n'est pas vide et le nombre de pages Web explorées ne dépasse pas 1000
tandis que (!SpiderQueue.unVisitedUrlsEmpty()
&& SpiderQueue.getVisitedUrlNum() <= 1000) {
//URL de tête de file d'attente retirée de la file d'attente
String visitUrl = (String) SpiderQueue.unVisitedUrlDeQueue();
si (visitUrl == null)
continuer;
DownTool downLoader = nouveau DownTool();
// Télécharger la page Web
downLoader.downloadFile(visitUrl);
// Mettez cette URL dans l'URL visitée
SpiderQueue.addVisitedUrl(visitUrl);
//Extraire l'URL de la page Web de téléchargement
Set<String> links = HtmlParserTool.extracLinks(visitUrl, filter);
// Les nouvelles URL non visitées sont mises en file d'attente
pour (String link : liens) {
SpiderQueue.addUnvisitedUrl(lien);
}
}
}
//entrée de la méthode principale
public static void main (String[] arguments) {
Robot d'exploration BfsSpider = new BfsSpider();
crawler.crawling(new String[] { "http://www.baidu.com" });
}
}
Après l'avoir exécuté, vous pouvez voir que le robot a exploré toutes les pages de la page Web Baidu :
Ce qui précède est tout le contenu de Java utilisant la boîte à outils HttpClient et le robot d'exploration de largeur pour explorer le contenu. C'est un peu plus compliqué, donc les amis devraient y réfléchir attentivement, j'espère que cela pourra être utile à tout le monde.