Eu também já usei rastreadores antes, como usar Nutch para rastejar uma semente especificada, pesquisar com base nos dados rastejados e analisar aproximadamente algum código -fonte. Obviamente, Nutch considera os rastreadores de maneira muito abrangente e meticulosamente. Sempre que vejo as informações da página da web e as informações de processamento que foram rastejadas sobre a tela, sempre sinto que isso é uma tecnologia muito sombria. Desta vez, aproveitei a oportunidade para resolver o MVC da primavera e queria fazer um pequeno rastreador sozinho. Não importa se eu posso facilmente obter alguns pequenos bugs. Tudo o que preciso é de um site de sementes que possa rastrear as informações que eu quero. Se houver uma exceção, pode ser porque algumas APIs são usadas de maneira inadequada ou podem encontrar um status anormal de solicitação HTTP ou há um problema com a leitura e a escrita do banco de dados. No processo de relatar exceções e resolver exceções, o joalheiro (apelido do filho) já pode rastrear dados de forma independente, e também há uma pequena habilidade de análise emocional baseada no algoritmo Word2Vec.
Pode haver exceções desconhecidas esperando para serem resolvidas posteriormente, e algum desempenho precisa ser otimizado, como interação com o banco de dados, os dados de leitura e escrita etc. No entanto, não tenho muita energia para colocar isso no ano, então darei um resumo simples hoje. Os dois primeiros artigos se concentram principalmente em funções e resultados. Este artigo fala sobre como nasceu o Jewelcrawler e coloca o código no GitHub (o endereço do código -fonte está no final do artigo). Se você estiver interessado, pode prestar atenção (apenas para comunicação e aprendizado, por favor Douban. Por favor, Douban. Seja mais sincero e menos dano)
Introdução ao meio ambiente
Ferramentas de desenvolvimento: Intellij Idea 14
Banco de dados: MySQL 5.5 + Ferramenta de gerenciamento de banco de dados Navicat (pode ser usado para conectar -se aos bancos de dados de consultas)
Idioma: Java
Gerenciamento de pacotes de jar: Maven
Gerenciamento de versão: git
Estrutura de diretório
em
com.ansj.vec é a versão Java Implementação do algoritmo Word2vec
com.jackie.crawler.doubanmovie é um módulo de implementação de rastreador, que também inclui
Alguns pacotes estão vazios porque esses módulos ainda não foram usados, entre os quais
O módulo de recurso armazena arquivos de configuração e arquivos de recursos, como
O módulo de teste é um módulo de teste usado para gravar UT.
Configuração do banco de dados
1. Adicione pacotes de dependência
A JewelCrawler usa o gerenciamento do Maven, portanto, você só precisa adicionar as dependências correspondentes no pom.xml.
<Depencency> <PuerpId> org.springframework </frupId> <TRAFACTID> spring-jdbc </artifactId> <versão> 4.1.1.release </versão </dependency> <pendency> <verfactId> commons-Pool </groupid> <stifactId> comonns-Pool </artefactid> commons-potencial <GroupID> commons-dbcp </frugiD> <TRAFACTID> commons-dbcp </stifactId> <TRARFACTID> commons-dbcp </stutifactId> <stifactId> comMons-dbcp </ArtifactId> <versão> 1.4 </versão </dependence> <pendence> <puriid> <TeRtifactId> mysql-conector-java </starfactId> <versão> 5.1.38 </version> </dependency> <pendencence> <puperid> mysql </groupiD> <TRARFACTID> mySQL-Connector-java </ArtifactId> <Versão> 5.1.38 </versão>
2. Declare o feijão da fonte de dados
Precisamos declarar o feijão da fonte de dados em beans.xml
<Contexto: Propriedade-Placter Location = "ClassPath*:*. value = "$ {jdbc.username}"/> <propriedade name = "senha" value = "$ {jdbc.password}"/>NOTA: Aqui está o arquivo de configuração externo jdbc.properties ligado, e os parâmetros da fonte de dados específicos são lidos neste arquivo.
Se você encontrar o problema, "SQL [inserir nos valores do usuário (id) (?)]; Field 'Name' não possui um valor padrão;" A solução é definir o campo correspondente da tabela como um campo de auto-crescimento.
Problemas encontrados ao analisar páginas
Para os dados da página da web que você rastejou, você precisa analisar a estrutura DOM e obter os dados desejados. Durante esse período, você encontra o seguinte erro
org.htmlparser.node não é reconhecido
Solução: Adicionar dependência do pacote JAR
<Depencency> <voundid> org.htmlparser </proupId> <TRARFACTID> htmlparser </artifactId> <versão> 1.6 </versão </dependency>
org.apache.http.httpentity não é reconhecido
Solução: Adicionar dependência do pacote JAR
<Ependency> <voundId> org.apache.httpcomponents </groupiD> <TRAFACTID> httpclient </storkactid> <versão> 4.5.2 </versão </dependency>
Obviamente, este é um problema encontrado durante o período, e a análise de página realizada pelo JSOUP é finalmente usada.
A velocidade do download do armazém maven é lenta
Eu usei o repositório central do Maven padrão antes, e a velocidade de baixar pacotes JAR era muito lenta. Não sei se foi o meu problema de rede ou outros motivos. Mais tarde, encontrei o repositório Maven da Alibaba Cloud online. Após a atualização, foi recomendável vomitar o sangue em comparação com antes.
<mirrors> <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror></mirrors>
Encontre o arquivo Settings.xml do Maven e adicione esta imagem.
Uma maneira de ler arquivos no módulo de recurso
Por exemplo, leia o arquivo de sementes.properties
@Test public void testFile () {arquivo semente de semente = novo arquivo (this.getclass (). GetResource ("/semear.properties"). GetPath ()); System.out.print ("===========" + SeedFile.Length () + "==========="); }Sobre expressões regulares
Ao usar expressões regulares do Regex, se o padrão definido for correspondido, você precisará chamar o método de localização do Matcher antes de usar o método do grupo para encontrar a substring. Não há como encontrar o resultado que você deseja chamando o método do grupo diretamente.
Eu olhei para o código -fonte da classe Matcher acima
pacote java.util.regex; importar java.util.Objects; public Final Class Matcher implementa MatchResult { /*** O objeto Padrão que criou este Matcher. */ Padrão parentepattern; /*** O armazenamento usado pelos grupos. Eles podem conter valores inválidos se * um grupo foi ignorado durante a correspondência. */ int [] grupos; /*** O intervalo dentro da sequência a ser correspondente. Âncoras * corresponderão a esses limites "duros". Alterar a região * altera esses valores. */ int de, para; /** * LookBehind usa esse valor para garantir que a correspondência da subexpressão * termine no ponto em que o lookbehind foi encontrado. */ int lookbehindto; /*** A sequência original que está sendo correspondente. */ Text de charsequence; /*** Estado de fósforo usado pelo último nó. Noanchor é usado quando uma partida * não precisa consumir toda a entrada. ENDanchor é * o modo usado para corresponder toda a entrada. */ estático final int endanchor = 1; estático final int noanchor = 0; int aceitmode = noanchor; /*** O intervalo de string que correspondia pela última vez ao padrão. Se a última partida * falhou, primeiro será -1; O último é inicialmente segura 0, então ele * mantém o índice do final da última partida (que é onde a próxima pesquisa é iniciada). */ int primeiro = -1, last = 0; /*** O índice final do que correspondeu na última operação de partida. */ int Oldlast = -1; /*** O índice da última posição aplicado em uma substituição. */ int lastAppendPosition = 0; /** * armazenamento usado pelos nós para dizer em que repetição eles estão em * um padrão e onde os grupos começam. Os nós em si estão sem estado, * então eles confiam nesse campo para manter o estado durante uma partida. */ int [] habitantes locais; /** * Booleano indicando se mais a entrada poderia ou não alterar * os resultados da última partida. * * Se o hitend for verdadeiro e uma correspondência foi encontrada, mais entrada * poderá fazer com que uma correspondência diferente seja encontrada. * Se o hitend for verdadeiro e uma partida não foi encontrada, mais * a entrada poderá fazer com que uma correspondência seja encontrada. * Se o hitend for falso e uma correspondência não foi encontrada, mais * a entrada não fará com que uma correspondência seja encontrada. */ BOOLEAN HITEND; /** * Booleano indicando se mais a entrada pode ou não mudar * uma correspondência positiva em uma negativa. * * Se o requerimento for verdadeiro e uma correspondência foi encontrada, mais * a entrada poderá fazer com que a correspondência seja perdida. * Se o requend for falso e uma correspondência foi encontrada, mais * a entrada poderá alterar a partida, mas a partida não será perdida. * Se uma partida não foi encontrada, o requend não terá significado. */ boolean requerEnd; /** * Se os limites transparentes forem verdadeiros, os limites desta * região do Matcher são transparentes para os lookhead, lookbehind, * e construções de correspondência de limites que tentam ver além deles. */ boolean transparentbounds = false; /** * Se a AnchoringBounds for verdadeira, os limites das âncoras de correspondência da região deste Matcher como ^ e $. */ booleano ancoringbounds = true; /*** Nenhum construtor padrão. * / Matcher () {} / *** Todos os Matcuers têm o estado usado por padrão durante uma partida. */Matcher (Pattern Parent, CharSequence Text) {this.parentpattern = pai; this.Text = text; // alocada armazenamento estadual int parentGroupCount = Math.max (parent.capturingGroupCount, 10); grupos = new int [parentGroupCount * 2]; moradores = new int [parent.localCount]; // Coloque os campos em estados iniciais RESET ();} ..../*** Retorna a subsequência de entrada correspondente pela partida anterior. * * <p> Para um Matcher <i> m </i> com sequência de entrada <i> s </i>, * as expressões <i> m. </i> <tt> grupo () </tt> e * <i> s. <i> m. </i> <tt> end ()) </tt> * são equivalentes. </p> * * <p> Observe que alguns padrões, por exemplo, <tt> a * </tt>, correspondem à string vazia *. Este método retornará a string vazia quando o padrão * corresponder com sucesso na sequência vazia na entrada. </p> * * @RETURN A sequência (possivelmente vazia) correspondida pela partida anterior, * no formulário da string * * @THOWSOWSTATATEEXCECTIMENTO * Se nenhuma correspondência ainda foi tentada, * ou se a operação anterior falhou */public String Group () {Grupo de retorno (0);}/** * * * * * <p> Para um Matcher <i> m </i>, sequência de entrada <i> s </i> e índice de grupo * <i> g </i>, as expressões <i> m. <i> s. </i> <tt> substring (</tt> <i> m. </p> * * <p> <a href = "padrony.html#cg"> captura de grupos </a> são indexados da esquerda * para a direita, começando em um. O grupo zero indica todo o padrão, então * a expressão <TT> M.Group (0) </tt> é equivalente a <tt> m.group () </tt>. * </p> * * <p> Se a correspondência foi bem -sucedida, mas o grupo especificado falhou em corresponder * qualquer parte da sequência de entrada, então <tt> null </tt> será retornado. Observe * que alguns grupos, por exemplo, <Tt> (a *) </tt>, correspondem à sequência vazia. * Este método retornará a string vazia quando esse grupo * corresponder com sucesso na sequência vazia na entrada. </p> * @param group * The index of a capturing group in this matcher's pattern * * @return The (possibly empty) subsequent captured by the group * during the previous match, or <tt>null</tt> if the group * failed to match part of the input * * @throws IllegalStateException * If no match has yet been attempted, * or if the previous match operation failed * * @throws IndexOutOfBoundsException * If there is nenhum grupo de captura no padrão * com o Índice dado */public string grupo (grupo int) {if (primeiro <0) lança new ilegalStateException ("nenhuma correspondência encontrada"); if (grupo <0 || grupo> groupCount ()) lançar novo indexOutOfBoundSexception ("sem grupo" + grupo); if ((grupos [grupo*2] == -1) || (grupos [grupo*2+1] == -1)) retorna nulo; Retornar GetSubsequence (Grupos [Grupo * 2], Grupos [Grupo * 2 + 1]). ToString ();}/** * tenta encontrar a próxima sequência da sequência de entrada que corresponde * ao padrão. * * <p> Este método começa no início da região deste Matcher, ou, se * uma invocação anterior do método foi bem -sucedida e o Matcher * não foi redefinido desde então, no primeiro caractere não correspondido pela partida anterior *. * * <p> Se a correspondência for bem -sucedida, mais informações poderão ser obtidas através dos métodos * <tt> start </tt>, <tt> end </tt> e <tt> grupo </tt>. </p> * * @return <tt> true </tt> se, e somente se, uma sequência da sequência de entrada * corresponde ao padrão deste correspondente */public boolean find () {int próximosearchIndex = last; if (nextSearchIndex == First) NextSearchIndex ++; // Se a próxima pesquisa começar antes da região, inicie -a na região se (NextSearchIndex <de) NextSearchIndex = de; // Se a próxima pesquisa começar além da região, ela falhará se (NextSearchIndex> to) {for (int i = 0; i <groups.length; i ++) grupos [i] = -1; retornar falso; } Return Search (NextSearchIndex);} /*** inicia uma pesquisa para encontrar um padrão dentro dos limites fornecidos. * Os grupos são preenchidos com valores padrão e a correspondência da raiz * da máquina de estado é chamada. A máquina estadual manterá o estado * da partida à medida que prossegue neste Matcher. * * Matchaer.From não está definido aqui, porque é o limite "duro" * do início da pesquisa a que as âncoras definirão. O param * é o limite "soft" do início da pesquisa, o que significa que o * regex tenta corresponder nesse índice, mas ^ não corresponderá lá. As chamadas * subsequentes para os métodos de pesquisa começam em um novo limite "soft", que é o final da partida anterior. */boolean pesquisa (int de) {this.hitend = false; this.RequireEnd = false; de = de <0? 0: de; this.First = de; this.oldlast = Oldlast <0? De: Oldlast; for (int i = 0; i <groups.length; i ++) grupos [i] = -1; aceitmode = noanchor; resultado booleano = parlapattern.root.match (this, a partir de texto); if (! resultado) this.First = -1; this.oldlast = this.Last; resultado de retorno;} ...}O motivo é o seguinte: se você não chamar o método de localização primeiro e ligar diretamente ao grupo, poderá descobrir que o grupo do Método do Grupo CHAVA (INT GRUPO). Há se primeiro <0 no corpo do método do método. Obviamente, essa condição é verdadeira aqui, porque o valor inicial do primeiro é -1, portanto, uma exceção será lançada aqui. No entanto, se você chamar o método Localizar, poderá encontrar que a pesquisa (NextSearchIndex) será chamada. Observe que o NextSearchIndex aqui foi atribuído por último, e o valor do último é 0 e depois pule para o método de pesquisa
pesquisa booleana (int de) {this.hitend = false; this.RequireEnd = false; de = de <0? 0: de; this.First = de; this.oldlast = Oldlast <0? De: Oldlast; for (int i = 0; i <groups.length; i ++) grupos [i] = -1; aceitmode = noanchor; resultado booleano = parlapattern.root.match (this, a partir de texto); if (! resultado) this.First = -1; this.oldlast = this.Last; resultado de retorno;} Esse próximo pesquisa é passado, e é atribuído a primeiro no corpo do método. Portanto, depois de chamar o método de localização, o primeiro disso não é mais -1 e não está lançando uma exceção.
O código -fonte foi enviado para Baidu NetDisk: http://pan.baidu.com/s/1dfwtvnz
Os problemas mencionados acima são relativamente quebrados e são um resumo ao encontrar problemas e resolvê -los. Haverá outros problemas durante operações específicas. Se você tiver alguma dúvida ou sugestão, não hesite em mencionar ^^.
Finalmente, coloque alguns dados rastejados até agora
Tabela de registros
Entre eles, 79.032 são armazenados e 48.471 páginas da web rastejadas são
mesa de filme
Atualmente, 2964 trabalhos de cinema e televisão foram rastejados
Tabela de comentários
29.711 registros foram rastejados
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.