También he usado rastreadores antes, como usar Nutch para rastrear una semilla especificada, buscar en función de los datos rastreados y mirar aproximadamente algún código fuente. Por supuesto, Nutch considera rastreadores de manera muy exhaustiva y meticulosa. Cada vez que veo la información de la página web y la información de procesamiento que se ha rastreada sobre la pantalla, siempre siento que esta es una tecnología muy oscura. Esta vez, aproveché la oportunidad de ordenar Spring MVC y quería hacer un pequeño rastreador solo. No importa si puedo obtener fácilmente algunos insectos pequeños. Todo lo que necesito es un sitio web de semillas que puede rastrear la información que quiero. Si hay una excepción, puede deberse a que algunas API se usan incorrectamente, o pueden encontrar un estado anormal de solicitud HTTP, o hay un problema con la lectura y la escritura de la base de datos. En el proceso de informar excepciones y resolver excepciones, Jewelcrawler (el apodo de Son) ya puede rastrear datos de forma independiente, y también hay una pequeña habilidad para el análisis emocional basado en el algoritmo Word2Vec.
Puede haber excepciones desconocidas que se esperan para resolverse más tarde, y se debe optimizar cierto rendimiento, como la interacción con la base de datos, la lectura y la escritura de datos, etc. Sin embargo, no tengo mucha energía para poner esto en el año, por lo que hoy daré un resumen simple. Los dos primeros artículos se centran principalmente en funciones y resultados. Este artículo habla sobre cómo nació Jewelcrawler y pone el código en GitHub (la dirección del código fuente está al final del artículo). Si está interesado, puede prestar atención (solo para la comunicación y el aprendizaje, por favor Douban. Por favor Douban. Sea más sincero y menos daño)
Introducción al medio ambiente
Herramientas de desarrollo: Idea IntellInj 14
Base de datos: herramienta de administración de bases de datos MySQL 5.5 + Navicat (se puede usar para conectarse a bases de datos de consultas)
Idioma: Java
Gestión de paquetes JAR: Maven
Gestión de versiones: Git
Estructura de directorio
en
com.ansj.vec es la implementación de la versión Java del algoritmo Word2Vec
com.jackie.crawler.doubanmovie es un módulo de implementación de rastreadores, que también incluye
Algunos paquetes están vacíos porque estos módulos aún no se usan, entre los cuales
El módulo de recursos almacena archivos de configuración y archivos de recursos, como
El módulo de prueba es un módulo de prueba utilizado para escribir UT.
Configuración de la base de datos
1. Agregar paquetes de dependencia
Jewelcrawler utiliza la gestión de Maven, por lo que solo necesita agregar las dependencias correspondientes en pom.xml.
<Spendency> <MoupRoMID> org.springframework </groupid> <artifactID> spring-jdbc </artifactid> <versión> 4.1.1.release </versión> </pendency> <pendency> <proupid> commons-pool-poolid> <artifactid> commons-pool </artifactid> <verseversion> <MoupRid> Commons-DBCP </proupid> <artifactid> commons-dbcp </artifactid> <artifactid> commons-dbcp </arfactid> <artifactid> commons-dbcp </arfactid> <versión> 1.4 </versión> </dependencia> <dependency> <grupid> mysql </groupid> <AtifactId> mysql-confonector-java </artifactid> <versión> 5.1.38 </versión> </pendency> <pendency> <MoupRoD> mysql </groupid> <artifactid> mysql-connector-java </artifactid> <PROPELACIÓN> 5.1.38 </lipeers> </pendency>
2. Declarar la fuente de datos Bean
Necesitamos declarar el frijol de la fuente de datos en frijoles.xml
<context: Property-placeholder ubicación = "classpath*:*. Properties"/> <bean id = "dataSource" destruye-method = "cierre"> <propiedad name = "conductorClassName" value = "$ {jdbc.driver}"/> <propiedad = "url" valor = "$ {jdbc.url}"/> <nombre de propiedad = "nombre de la propiedad =" nombre "" UserName " valor = "$ {jdbc.username}"/> <propiedad name = "contraseña" value = "$ {jdbc.password}"/>Nota: Aquí está el archivo de configuración externa JDBC.Properties Bound, y los parámetros de la fuente de datos específica se leen de este archivo.
Si encuentra el problema, "SQL [insertar en valores de usuario (id) (?)]; Campo 'nombre' no tiene un valor predeterminado"; La solución es establecer el campo correspondiente de la tabla en un campo de crecimiento propio.
Problemas encontrados al analizar las páginas
Para los datos de la página web que se arrastró, debe analizar la estructura DOM y obtener los datos que desea. Durante este período, encuentra el siguiente error
org.htmlparser.node no se reconoce
Solución: Agregar dependencia del paquete jar
<Spendency> <MoupRoD> org.htmlparser </groupid> <artifactId> htmlparser </artifactid> <versión> 1.6 </versión> </pendency>
org.apache.http.httpentity no se reconoce
Solución: Agregar dependencia del paquete jar
<Spendency> <MoupRoid> org.apache.httpComponents </groupid> <artifactId> httpclient </arfactid> <versión> 4.5.2 </versión> </pendency>
Por supuesto, este es un problema encontrado durante el período, y el análisis de la página realizado por Jsoup finalmente se usa.
La velocidad de descarga de Maven Warehouse es lenta
Utilicé el repositorio central Maven predeterminado antes, y la velocidad de descarga de paquetes JAR fue muy lenta. No sé si era mi problema de red u otras razones. Más tarde, encontré el repositorio de Maven de Alibaba Cloud Online. Después de la actualización, se recomendó vomitar sangre en comparación con antes.
<prirrors> <prirror> <id> alimaven </id> <name> aliyun maven </name> <url> http://maven.aliyun.com/nexus/content/groups/public/ </url> <rirrorof> Central </Mirrorof> </Mirror> </Mirrors>
Encuentre el archivo settings.xml de Maven y agregue esta imagen.
Una forma de leer archivos en el módulo de recursos
Por ejemplo, lea el archivo SEED.Properties
@Test public void testFile () {file seedfile = new File (this.getClass (). GetResource ("/SEDE.Properties"). GetPath ()); System.out.print ("===========" + Seedfile.length () + "==========="); }Sobre expresiones regulares
Al usar expresiones regulares Regrex, si el patrón definido coincide, debe llamar al método de búsqueda del Matcher antes de poder usar el método de grupo para encontrar la subcadena. No hay forma de encontrar el resultado que desee llamando directamente al método de grupo.
Miré el código fuente de la clase Matcher anterior
paquete java.util.regex; import java.util.objects; public Final Clase Matcher implementa MatchResult { /*** El objeto de patrón que creó este combate. */ Pattern ParentPatnn; /*** El almacenamiento utilizado por grupos. Pueden contener valores no válidos si * se omitió un grupo durante la coincidencia. */ int [] grupos; /*** El rango dentro de la secuencia que se va a coincidir. Los anclajes * coincidirán con estos límites "duros". Cambiar la región * cambia estos valores. */ int de, a; /** * Lookbehind usa este valor para garantizar que la coincidencia de la subexpresión * termine en el punto donde se encontró el aspecto. */ int lookbehindto; /*** La cadena original se coincide. */ Texto de CharSECHENCE; /*** Estado de combate utilizado por el último nodo. Noanchor se usa cuando un * coincidencia no tiene que consumir toda la entrada. Endanchor es * el modo utilizado para hacer coincidir toda la entrada. */ static final int endanchor = 1; static final int noanchor = 0; int aceptarmode = noanchor; /*** El rango de cadena que coincidió por última vez con el patrón. Si el último partido * falló, primero es -1; El último inicialmente se mantiene 0, entonces * contiene el índice del final del último partido (que es donde comienza la próxima búsqueda). */ int primero = -1, último = 0; /*** El índice final de lo que coincidió en la última operación de coincidencia. */ int Oldlast = -1; /*** El índice de la última posición se aplica en una sustitución. */ int LastAppendPosition = 0; /** * Almacenamiento utilizado por nodos para decir en qué repetición se encuentran en * un patrón y dónde comienzan los grupos. Los nodos mismos son apátridos, * por lo que confían en este campo para mantener el estado durante un partido. */ int [] locales; /** * Boolean que indica si más entrada podrían o no cambiar * los resultados de la última coincidencia. * * Si Hitend es verdadero y se encontró una coincidencia, entonces más entrada * podría causar una coincidencia diferente. * Si Hitend es verdadero y no se encontró una coincidencia, entonces más * la entrada podría causar una coincidencia. * Si Hitend es falso y no se encontró una coincidencia, entonces más * La entrada no causará una coincidencia. */ boolean hitend; /** * Boolean que indica si más información podría cambiar * una coincidencia positiva en una negativa. * * Si requestend es verdadero, y se encontró una coincidencia, entonces más * la entrada podría hacer que la coincidencia se pierda. * Si requestend es falso y se encontró una coincidencia, entonces más * entrada podría cambiar la coincidencia, pero la coincidencia no se perderá. * Si no se encontró una coincidencia, entonces requería no tiene sentido. */ boolean requitend; /** * Si TransparentBounds es verdadero, entonces los límites de esta * región de Matcher son transparentes para Lookhead, Lookbehind, * y las construcciones de correspondencia de límites que intentan ver más allá de ellos. */ boolean transparentBounds = false; /** * Si AnchingingBounds es verdadero, entonces los límites de esta * región de matcher coinciden con anclajes como ^ y $. */ boolean AnchoringBounds = True; /*** Sin constructor predeterminado. * / Matcher () {} / *** Todos los matchers tienen el estado utilizado por el patrón durante una coincidencia. */Matcher (Pattern Parent, Charsequence Text) {this.ParentPattern = parent; this.text = text; // Asignar almacenamiento de estado int grupos = new int [ParentGroupCount * 2]; lugareños = new int [parent.localCount]; // colocar campos en los estados iniciales RESET ();} ..../*** Devuelve la entrada posterior coincidente coincidente por la coincidencia anterior. * * <p> para un combate <i> m </i> con secuencia de entrada <i> s </i>, * las expresiones <i> m. </i> <tt> group () </tt> y * <i> s. </i> <tt> substring (</tt> <i> m. </i> <tt> start (), </tt> <i> m. </i> <tt> end ()) </tt> * son equivalentes. </p> * * <p> Tenga en cuenta que algunos patrones, por ejemplo <tt> a * </tt>, coinciden con la cadena vacía *. Este método devolverá la cadena vacía cuando el patrón * coincida con éxito con la cadena vacía en la entrada. </p> * * @return la secuencia (posiblemente vacía) coincidida con la coincidencia anterior, * en forma de cadena * * @throws ilegalStateException * Si aún no se ha intentado ninguna coincidencia, * o si la operación de coincidencia anterior falló */public String group () {return Group (0);}/** * Devuelve la subsecuencia de entrada capturada por el grupo dada durante el * operación de coincidencia anterior. * * <p> para un combate <i> m </i>, secuencia de entrada <i> s </i> e índice de grupo * <i> g </i>, las expresiones <i> m. </i> <tt> group (</tt> <i> g </i> <tt>) </tt> y * y * <i> S </p> * * <p> <a href = "patrón.html#cg"> Los grupos de captura </a> están indexados de izquierda a derecha, comenzando en uno. El grupo cero denota todo el patrón, por lo que * la expresión <tt> m.group (0) </tt> es equivalente a <tt> m.group () </tt>. * </p> * * <p> Si la coincidencia fue exitosa pero el grupo especificado no pudo coincidir * cualquier parte de la secuencia de entrada, entonces <tt> null </tt> se devuelve. Tenga en cuenta * que algunos grupos, por ejemplo <tt> (a *) </tt>, coinciden con la cadena vacía. * Este método devolverá la cadena vacía cuando dicho grupo * coincida con éxito * coincida con la cadena vacía en la entrada. </p> * @param Group * El índice de un grupo de captura en el patrón de este combate * * @return el (posiblemente vacío) posterior capturado por el grupo * durante la coincidencia anterior, o <tt> null </tt> Si el grupo * no pudo coincidir con la parte de la entrada * * @throws ilegalStatexception * Si no se ha intentado, * o si la operación de coincidencia anterior * @@throws @throws ÍndiceOuts. capturar el grupo en el patrón * con el índice dado */grupo de cadena pública (grupo int) {if (primero <0) tirar nueva ilegalStateException ("no se encontró coincidencia"); if (grupo <0 || Group> GroupCount ()) Lanzar un nuevo indexOutofBoundSException ("no grupo" + grupo); if ((grupos [grupo*2] == -1) || (grupos [grupo*2+1] == -1)) return null; return getSubSequence (grupos [grupo * 2], grupos [grupo * 2 + 1]). ToString ();}/** * intenta encontrar la siguiente secuencia de la secuencia de entrada que coincide con * el patrón. * * <p> Este método comienza al comienzo de la región de este Matcher, o, si * una invocación previa del método fue exitosa y el Matcher * no se ha restablecido desde entonces, en el primer carácter no coincide con el partido anterior *. * * <p> Si la coincidencia tiene éxito, entonces se puede obtener más información a través de los métodos * <tt> start </tt>, <tt> end </tt> y <tt> group </tt>. </p> * * @return <tt> true </tt> if, y solo si, una secuencia de la secuencia de entrada * coincide con el patrón de este combate */public boolean find () {int nextSearchIndex = last; if (nextSearchIndex == First) NextSearchIndex ++; // Si la próxima búsqueda comienza antes de la región, comience en la región if (nextSearchIndex <desde) nextSearchIndex = desde; // Si la siguiente búsqueda comienza más allá de la región, entonces falla si (nextSearchIndex> to) {for (int i = 0; i <groups.length; i ++) grupos [i] = -1; devolver falso; } Search return (nextSearchIndex);} /*** Inicia una búsqueda para encontrar un patrón dentro de los límites dados. * Los grupos están llenos de valores predeterminados y se llama a la coincidencia de la raíz * de la máquina de estado. La máquina de estado mantendrá el estado * del partido a medida que avanza en esta combate. * * Matcher.fom no está configurado aquí, porque es el límite "duro" * del inicio de la búsqueda en la que se establecerán los anclajes. El de Param * es el límite "suave" del inicio de la búsqueda, lo que significa que * Regex intenta coincidir con ese índice pero ^ no coincidirá allí. Posteriormente * Las llamadas a los métodos de búsqueda comienzan en un nuevo límite "suave" que es * el final de la coincidencia anterior. */Boolean Search (int from) {this.hitend = false; this.requireend = false; de = de <0? 0: de; this.first = from; this.oldlast = Oldlast <0? De: Oldlast; para (int i = 0; i <groups.length; i ++) grupos [i] = -1; AceptMode = NoAchor; Resultado booleano = parentPattern.root.match (this, from, text); if (! resultado) this.first = -1; this.oldlast = this.last; Resultado de retorno;} ...}La razón es esta: si no llama primero el método de búsqueda y llama al grupo directamente, puede encontrar que el método de grupo llama al grupo (int Group). Hay si primero <0 en el cuerpo del método del método. Obviamente, esta condición es cierta aquí, porque el valor inicial del primero es -1, por lo que se lanzará una excepción aquí. Sin embargo, si llama al método Find, puede encontrar que la búsqueda (NextSearchIndex) eventualmente se llamará. Tenga en cuenta que el NextSearchIndex aquí ha sido asignado por el último, y el valor del último es 0, y luego salta al método de búsqueda
Boolean Search (int from) {this.hitend = false; this.requireend = false; de = de <0? 0: de; this.first = from; this.oldlast = Oldlast <0? De: Oldlast; para (int i = 0; i <groups.length; i ++) grupos [i] = -1; AceptMode = NoAchor; Resultado booleano = parentPattern.root.match (this, from, text); if (! resultado) this.first = -1; this.oldlast = this.last; Resultado de retorno;} Este NextSearchIndex se pasa desde, y desde se asigna primero en el cuerpo del método. Por lo tanto, después de llamar al método Find, el primero de esto ya no es -1, y no está lanzando una excepción.
El código fuente se ha subido a Baidu NetDisk: http://pan.baidu.com/s/1dfwtvnz
Los problemas mencionados anteriormente están relativamente destrozados, y son un resumen al encontrar problemas y resolverlos. Habrá otros problemas durante las operaciones específicas. Si tiene alguna pregunta o sugerencia, no dude en mencionar ^^.
Finalmente, ponga algunas piezas de datos rastreados hasta ahora
Mesa de registro
Entre ellos, se almacenan 79,032 y 48,471 páginas web rastreadas son
mesa de cine
Actualmente, 2964 se han rastreado las obras de cine y televisión
Mesa de comentarios
29,711 registros se rastrearon
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.