J'ai également utilisé des robots de robots, comme l'utilisation de Nutch pour ramper une graine spécifiée, des recherches sur la base des données rampantes et en regardant à peu près un code source. Bien sûr, Nutch considère les Crawlers de manière très complète et méticuleusement. Chaque fois que je vois les informations sur la page Web et le traitement des informations qui ont été rampantes à l'écran, je pense toujours que c'est une technologie très sombre. Cette fois, j'ai profité de l'occasion pour trier le printemps MVC et je voulais faire un petit robot par moi-même. Peu importe si je peux facilement obtenir de petits bugs. Tout ce dont j'ai besoin, c'est d'un site Web de semence qui peut explorer les informations que je veux. S'il y a une exception, cela peut être dû au fait que certaines API sont utilisées mal, ou elles peuvent rencontrer un statut de demande HTTP anormal, ou il y a un problème avec la lecture et l'écriture de la base de données. Dans le processus de signalement des exceptions et de la résolution des exceptions, Jewelcrawler (surnom de son fils) peut déjà ramper les données indépendamment, et il existe également une petite compétence pour l'analyse émotionnelle basée sur l'algorithme Word2Vec.
Il peut y avoir des exceptions inconnues qui attendent d'être résolues plus tard, et certaines performances doivent être optimisées, comme l'interaction avec la base de données, la lecture et l'écriture de données, etc. Cependant, je n'ai pas beaucoup d'énergie pour mettre cela dans l'année, donc je vais donner un résumé simple aujourd'hui. Les deux premiers articles se concentrent principalement sur les fonctions et les résultats. Cet article explique comment Jewelcrawler est né et met le code sur GitHub (l'adresse du code source est à la fin de l'article). Si vous êtes intéressé, vous pouvez faire attention (pour la communication et l'apprentissage uniquement, s'il vous plaît Douban. S'il vous plaît Douban. Soyez plus sincère et moins de mal)
Introduction de l'environnement
Outils de développement: Intellij Idea 14
Base de données: MySQL 5.5 + Database Management Tool Navicat (peut être utilisée pour se connecter aux bases de données de requête)
Langue: Java
Gestion des paquets de pot: maven
Gestion de la version: Git
Structure de répertoire
dans
com.ansj.vec est l'implémentation de la version Java de l'algorithme Word2Vec
com.jackie.crawler.doubanmovie est un module de mise en œuvre du robot, qui comprend également
Certains packages sont vides car ces modules ne sont pas encore utilisés, parmi lesquels
Le module de ressources stocke les fichiers de configuration et les fichiers de ressources, tels que
Le module de test est un module de test utilisé pour écrire UT.
Configuration de la base de données
1. Ajouter des packages de dépendance
Jewelcrawler utilise la gestion de Maven, vous n'avez donc qu'à ajouter les dépendances correspondantes dans pom.xml.
<Dedendency> <GroupId> org.SpringFramework </pruimId> <ArtifActid> Spring-jdbc </letefactive> <version> 4.1.1.release </ version> </Dependency> <Dendency> <proupId> Comons-Pool </ GroupId> <Artifactid> Commons-Pool </ptetifActid> <version> 1.6 <6 </ version> <GroupId> Commons-dbcp </proupId> <Artifactid> Commons-Dbcp </letifactid> <Ertifactid> Commons-dbcp </ artifactId> <ArtifActid> Commons-Dbcp </ Artifactid> <version> 1.4 </ version> </Dependency> <Dedency> <ProupID> MysQL </prouverID> <ArtefactId> MySQL-Connector-Java </ Arfactive> <Dersion> 5.1.38 </DERNIFRATION> </ Dependency> <Dedency> <ProupID> MySQL </rom grouped> <Artifactid> MySQL-Connector-Java </ Artifactid> <Dersion> 5.1.38 </DERNIFRODICATIONS>
2. Déclarer le haricot de source de données
Nous devons déclarer le haricot de la source de données dans les haricots.xml
<context: propriété-placeholder location = "classPath *: *. Properties" /> <bean id = "dataSource" destrement-méthod = "close"> <propriété name = "driverclassname" value = "$ {jdbc.driver}" /> <propriété name = "url" value = "$ {jdbc.url}" /> <propriété name = "username" Value = "$ {jdbc.Username}" /> <propriété name = "mot de passe" value = "$ {jdbc.password}" />Remarque: Voici le fichier de configuration externe JDBC.Properties lié, et les paramètres de la source de données spécifiques sont lus à partir de ce fichier.
Si vous rencontrez le problème, "SQL [insérer dans l'utilisateur (ID) VALEURS (?)]; Field 'Name' n'a pas de valeur par défaut;" La solution consiste à définir le champ correspondant de la table sur un champ d'auto-croissance.
Problèmes rencontrés lors de l'analyse des pages
Pour les données de la page Web que vous avez rampées, vous devez analyser la structure DOM et obtenir les données souhaitées. Pendant cette période, vous rencontrez l'erreur suivante
org.htmlparser.Node n'est pas reconnu
Solution: ajouter la dépendance du package JAR
<dependency> <proupId> org.htmlparser </rompuprid> <letfactId> htmlparser </letefactive> <version> 1.6 </ version> </dependency>
org.apache.http.httpentity n'est pas reconnu
Solution: ajouter la dépendance du package JAR
<dependency> <proupId> org.apache.httpComponents </rom groupeid> <ArtifActid> httpclient </retifactid> <version> 4.5.2 </ version> </pedidency>
Bien sûr, il s'agit d'un problème rencontré au cours de la période, et l'analyse de page effectuée par JSoup est enfin utilisée.
La vitesse de téléchargement de l'entrepôt Maven est lente
J'ai déjà utilisé le référentiel Maven Central par défaut, et la vitesse de téléchargement des packages JAR était très lente. Je ne sais pas si c'était mon problème de réseau ou d'autres raisons. Plus tard, j'ai trouvé le référentiel Maven d'Alibaba Cloud Online. Après la mise à jour, il a été recommandé de vomir du sang par rapport auparavant.
<Mirrors> <Mirror> <id> alimaven </id> <name> Aliyun maven </name> <url> http://maven.aliyun.com/nexus/content/groups/public/ </url> <Mirrorof> Central </ Mirrorof> </ Mirror> </ Mirrors>
Recherchez le fichier paramètres.xml de Maven et ajoutez cette image.
Un moyen de lire des fichiers sous le module de ressource
Par exemple, lisez le fichier Seed.properties
@Test public void TestFile () {file SeedFile = new File (this.getClass (). GetResource ("/ Seed.properties"). GetPath ()); System.out.print ("==========" + SeedFile.length () + "=========="); }Sur les expressions régulières
Lorsque vous utilisez des expressions régulières Regrex, si le modèle défini est apparié, vous devez appeler la méthode de recherche du Matcher avant de pouvoir utiliser la méthode de groupe pour trouver la sous-chaîne. Il n'y a aucun moyen de trouver le résultat que vous souhaitez en appelant directement la méthode du groupe.
J'ai regardé le code source de la classe de matchur ci-dessus
package java.util.regex; import java.util.objects; public class final class implémente MatchResult {/ ** * l'objet Pattern qui a créé ce match. * / Modèle parentPattern; / ** * Le stockage utilisé par les groupes. Ils peuvent contenir des valeurs non valides si * un groupe a été sauté pendant la correspondance. * / int [] groupes; / ** * La plage dans la séquence qui doit être appariée. Ancre * correspondra à ces limites "dures". La modification de la région * modifie ces valeurs. * / int de, à; / ** * LookBehind utilise cette valeur pour garantir que la correspondance de sous-expression * se termine au point où le lookbehind a été rencontré. * / int lookbehindto; / ** * La chaîne d'origine correspondait. * / Texte de CharSequence; / ** * État de correspondance utilisé par le dernier nœud. Le noanchor est utilisé lorsqu'une correspondance * n'a pas à consommer toutes les entrées. Endanchor est * le mode utilisé pour correspondre à toutes les entrées. * / statique final int endanchor = 1; statique final int noanchor = 0; int acceptemode = noanchor; / ** * La plage de chaîne qui correspondait au motif pour la dernière fois. Si le dernier match * a échoué, alors d'abord est -1; Le dernier tient initialement 0, alors il détient l'index de la fin de la dernière correspondance (c'est là que la * recherche suivante commence). * / int premier = -1, dernier = 0; / ** * L'indice final de ce qui correspondait à la dernière opération de match. * / int oldLast = -1; / ** * L'indice de la dernière position appliquée dans une substitution. * / int lastAnPEndPosition = 0; / ** * Stockage utilisé par les nœuds pour dire quelle répétition ils se trouvent dans * un modèle et où les groupes commencent. Les nœuds eux-mêmes sont apatrides * donc ils comptent sur ce champ pour tenir l'état lors d'un match. * / int [] locaux; / ** * booléen indiquant si une entrée plus importante pourrait modifier * les résultats de la dernière correspondance. * * Si Hittend est vrai et une correspondance a été trouvée, alors plus d'entrée * pourrait entraîner un match différent. * Si Hittend est vrai et une correspondance n'a pas été trouvée, alors plus * entrée pourrait entraîner un match. * Si Hittend est faux et qu'une correspondance n'a pas été trouvée, alors plus * l'entrée ne fera pas partie d'un match. * / Boolean Hittend; / ** * Boolean indiquant si une entrée plus importante pourrait changer * une correspondance positive en une correspondance négative. * * Si BidEnd est vrai et une correspondance a été trouvée, alors plus * entrée pourrait entraîner la perte du match. * Si BidEnd est faux et qu'une correspondance a été trouvée, alors plus * entrée pourrait modifier le match, mais le match ne sera pas perdu. * Si une correspondance n'a pas été trouvée, il ne faut pas de sens. * / boolean requireend; / ** * Si TransparentBounds est vrai, les limites de la région de cette * Matcher sont transparentes pour regarder, lookbehind * et les constructions correspondantes des limites qui essaient de voir au-delà d'eux. * / booléen transparentbounds = false; / ** * Si Anchoringbounds est vrai, les limites de la région de cette * correspondance correspondent aux ancres telles que ^ et $. * / Boolean Anchoringbounds = true; / ** * Aucun constructeur par défaut. * / Matcher () {} / ** * Tous les matchs ont l'état utilisé par le modèle pendant une correspondance. * / Matcher (Pattern Parent, CharSequence Text) {this.parentPattern = parent; this.text = text; // allouer State Storage int parentGroupCount = math.max (parent.CapturingGroupCount, 10); groupes = new int [parentGroupCount * 2]; Locals = new int [Parent.LocalCount]; // Mettez les champs dans les états initiaux reset ();} .... / ** * Renvoie la sous-séquence d'entrée correspondante par la correspondance précédente. * * <p> pour un matchur <i> m </i> avec séquence d'entrée <i> s </i>, * les expressions <i> m. </i> <tt> Group () </tt> et * <i> s. </i> <tt> sous-critère (</ tt> <i> m. </i> <tt> start (), </tt> <i> m. </i> <tt> end ()) </tt> * sont équivalents. </p> * * <p> Notez que certains modèles, par exemple <tt> a * </tt>, correspondent à la chaîne vide *. Cette méthode renverra la chaîne vide lorsque le modèle * correspond avec succès à la chaîne vide dans l'entrée. </p> * * @return La séquence (peut-être vide) correspondante par le match précédent, * sous forme de chaîne * * @throws illégalStateException * Si aucune correspondance n'a encore été tentée, * ou si l'opération de match précédente a échoué * / public String Group () {Return Group (0);} / ** * Renvoie la sous-séquence d'entrée capturée par le groupe donné pendant le fonctionnement du match précédent. * * <p> pour un matchur <i> m </i>, la séquence d'entrée <i> s </i>, et le groupe d'index * <i> g </i>, les expressions <i> m. </i> <tt> Groupe (</tt> <i> g </i> <tt>) </tt> et * <i> S </p> * * <p> <a href = "motif.html # cg"> Les groupes de capture </a> sont indexés de gauche * à droite, en commençant par un. Le groupe zéro désigne l'ensemble du modèle, donc * l'expression <tt> M.Group (0) </tt> est équivalente à <Tt> M.Group () </TT>. * </p> * * <p> Si la correspondance a été réussie mais que le groupe spécifié n'a pas réussi * aucune partie de la séquence d'entrée, alors <tt> null </tt> est renvoyé. Remarque * que certains groupes, par exemple <tt> (a *) </tt>, correspondent à la chaîne vide. * Cette méthode renverra la chaîne vide lorsqu'un tel groupe réussit avec succès * correspond à la chaîne vide dans l'entrée. </p> * @param groupe * L'indice d'un groupe de capture dans le modèle de ce matchur * * @return le (éventuellement vide) ultérieur capturé par le groupe * pendant la correspondance précédente, ou <TT> null </ tt> Si le groupe * n'a pas réussi à correspondre à la partie de l'entrée * * @throws illégalstateException * Si la correspondance n'a pas encore été la tentative * ou si l'opération de correspondance précédente * * * @throws indextSExe, ou si l'opération de correspondance précédente * * pas de groupe de capture dans le modèle * avec l'index donné * / groupe de chaîne publique (groupe int) {if (premier <0) lance un nouveau IllégalStateException ("No Match Found"); if (group <0 || groupe> groupCount ()) lance un nouvel indexoutofboundSexception ("no groupe" + groupe); if ((groupes [groupe * 2] == -1) || (groupes [groupe * 2 + 1] == -1)) renvoie null; return getSubSequence (groupes [groupe * 2], groupes [groupe * 2 + 1]). toString ();} / ** * tente de trouver la séquence suivante de la séquence d'entrée qui correspond * au motif. * * <p> Cette méthode démarre au début de la région de ce Matcher, ou, si * une invocation précédente de la méthode a été réussie et que le Matcher n'a pas été réinitialisé depuis, au premier personnage non apparié par le match * précédent. * * <p> Si le match réussit, plus d'informations peuvent être obtenues via les méthodes * <TT> START </TT>, <TT> end </tt> et <tt> Group </TT>. </p> * * @return <tt> true </tt> si, et seulement si, une séquence de la séquence d'entrée * correspond à la motif de ce matchur * / booléen public find () {int nextSearchIndex = dernier; if (nextSearchIndex == First) nextSearchIndex ++; // Si la recherche suivante démarre avant la région, démarrez-la dans la région si (nextSearchIndex <from) nextSearchIndex = From; // Si la recherche suivante démarre au-delà de la région, il échoue si (nextSearchIndex> to) {for (int i = 0; i <groupes.length; i ++) groupes [i] = -1; retourne false; } return search (nextSearchIndex);} / ** * initie une recherche pour trouver un modèle dans les limites données. * Les groupes sont remplis de valeurs par défaut et la correspondance de la racine * de la machine d'état est appelée. La machine d'état conservera l'état * du match au fur et à mesure qu'il se déroule dans ce correspondant. * * Matcher.from n'est pas défini ici, car c'est la frontière "dure" * du début de la recherche sur laquelle les ancres seront définies. Le From Param * est la limite "douce" du début de la recherche, ce qui signifie que le * Regex essaie de correspondre à cet index mais ne correspondra pas là. Les appels * ultérieurs * aux méthodes de recherche commencent à une nouvelle limite "Soft" qui est * la fin de la correspondance précédente. * / booléen Search (int from) {this.Hitend = false; this.requireend = false; de = de <0? 0: de; this.first = de; this.oldLast = oldLast <0? De: OldLast; pour (int i = 0; i <groupes.length; i ++) groupes [i] = -1; AcceptMode = Noanchor; booléen result = parentPattern.root.match (this, from, texte); if (! Résultat) this.first = -1; this.oldLast = this.last; Retour Résultat;} ...}La raison en est la suivante: si vous n'appelez d'abord pas la méthode de recherche et appelez directement le groupe, vous pouvez constater que le groupe d'appels de méthode de groupe (groupe int). Il y a si d'abord <0 dans le corps de la méthode de la méthode. De toute évidence, cette condition est vraie ici, car la valeur initiale de la première est -1, donc une exception sera lancée ici. Cependant, si vous appelez la méthode Find, vous pouvez trouver que la recherche (nextSearchIndex) sera finalement appelée. Notez que le NextSearchIndex ici a été attribué par dernier, et que la valeur de la dernière est de 0, puis de passer à la méthode de recherche
booléen search (int from) {this.Hitend = false; this.requireend = false; de = de <0? 0: de; this.first = de; this.oldLast = oldLast <0? De: OldLast; pour (int i = 0; i <groupes.length; i ++) groupes [i] = -1; AcceptMode = Noanchor; booléen result = parentPattern.root.match (this, from, texte); if (! Résultat) this.first = -1; this.oldLast = this.last; Retour Résultat;} Cette recherche nextSearch est transmise à partir de, et de la première fois dans le corps de la méthode. Par conséquent, après avoir appelé la méthode de recherche, le premier ne est plus -1, et il ne lance pas d'exception.
Le code source a été téléchargé sur baidu netdisk: http://pan.baidu.com/s/1dfwtvnz
Les problèmes mentionnés ci-dessus sont relativement brisés, et ils sont un résumé lorsqu'ils rencontrent des problèmes et de les résoudre. Il y aura d'autres problèmes pendant des opérations spécifiques. Si vous avez des questions ou des suggestions, n'hésitez pas à mentionner ^^.
Enfin, mettez jusqu'à présent quelques morceaux de données rampantes
Table record
Parmi eux, 79 032 sont stockés et 48 471 pages Web rampantes sont
table de cinéma
Actuellement, 2964 travaux de cinéma et de télévision ont été rampants
Table de commentaires
29 711 records ont été rampants
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.