Sua opnião - Web Crawler [RESOLVIDO]

Bom dia,

venho ate vcs hoje com o intuito de obter algumas opiniões sobre o meu projeto de Web Crawler. Vale ressaltar que este é um projeto pessoal, não se trata de “dever de casa” nem nada do gênero.

O meu crawler tem o funcionamento basico de todos os outros

  1. visitar um site
  2. coletar informações
  3. coletar links
  4. visitar outro site

O que eu estou tentando é melhorar o desempenho da aplicação e reduzir o consumo de memoria. Ao realizar o passo 3, o crawler insere os links encontrados no frontier global da aplicação.

A estrutura que eu estou usando é um Stack. porem pude reparar que os links na base da pilha não estão sendo visitados. Pensei em utilizar um LinkedList pois cada thread retira o primeiro link da pilha, ou seja, eu não faço remoção no meio do buffer, apenas nas extremidades e o LinkedList comporta estas funcionalidades. Vcs estao de acordo com a escolha ? coinhecem outra estrutura que pode otimizar a performance ?

No teste de stress, rodando com 100 threads, o consumo de memoria alcança a casa dos 200 Mega de RAM, alguem me sujere alguma estrategia para reduzir o consumo de memoria ? eu não gostaria de fazer cache no disco pois isso prejudica o desempenho. Alguma solução mais viavel ?

Percebi tambem que o uso de expressoes regulares para percorrer grandes textos consome muita RAM, isso e normal ?

Bem, se alguem ai ja codificou este tipo de aplicação ? gostaria de sugestões a respeito

Att,

Nunca fiz nada assim, mas tenho estado bastante interessado em projetos desse tipo, pois estou precisando em um projeto pessoal meu e o que tem de open source ou é complexo demais ou é ruim demais.

Testei recentemente o apache nutch e o crawler4j. O Nutch é extremamente performático, conta com integrações com Hadoop, Solr e tudo o mais, mas é tão complexo que eu não conseguí descobrir como customizar o processo (ou seja, interferir no job map/reduce dele).

O crawler4j é mais simples, permite fazer isso, mas a cada release lançada são bugs novos, sem contar que não é compatível com as versões anteriores. Está sendo um pesadelo.

Estou pensando em começar a desenvolver um que atenda às minhas necessidades, mas partindo do ponto de aprendizado que estes projetos já me deram. Se estiver interessado em uma parceria, me mande uma MP.

[]'s

Cara,tenta dar uma lida no livro “A Arte do Java”,nele tem um capítulo sobre como fazer um webcrawler que e´muito interessante!

Pessoal , preciso mesmo de ajuda, eu alterei o projeto para gravar os links no MySQL, pq pensei que armazenar os links na memoria estivesse consumindo muita RAM, os dados encontrados tambem estao sendo armazenados no SGBD.

Fiz isso para evitar a criação de muitos objetos para acesso ao disco, mas mesmo assim as threads estão consumindo muita RAM e gerando Out Of Memory

não sei como resolver

[quote=mmx]Pessoal , preciso mesmo de ajuda, eu alterei o projeto para gravar os links no MySQL, pq pensei que armazenar os links na memoria estivesse consumindo muita RAM, os dados encontrados tambem estao sendo armazenados no SGBD.

Fiz isso para evitar a criação de muitos objetos para acesso ao disco, mas mesmo assim as threads estão consumindo muita RAM e gerando Out Of Memory

não sei como resolver [/quote]

Em geral, os crawlers não salvam esse tipo de dado em bancos relacionais - até porque, esse tipo de coisa tem mais cara de bancos OLAP do que OLTP. É mais a cara de processadores como o Hadoop do que o MySQL. Tem também o BerkeleyDB, que é o que o crawler4j usa.

Quanto ao seu OutOfMemory, um bom profiler deve ajudar (aliás, pra esse tipo de aplicação, o uso de um profiler deve ser prática constante). Recomendo o JProfiler.

obrigado por responder, vou pesquisar sobre as suas sugestões e posto os resultados

bom, eu passei o projeto para o netbeans para usar o profiler, bem eu nunca tinha usado ele antes, mas se eu interpretei certo,
eu pude perceber o total de objetos de cada classe, e percebi que a API que eu estou usando pra trabalhar com HTML, a JSoup, pode ser o gargalo do meu programa.
\

percebi um grande consumo de objetos String, byte[] e char[], que estavam vindo da minha função para obter codigo de uma pagina web. O codigo e obtido mas pelo visto a memoria não e liberada.

observei que alimentando o meu buffer de links, com o limite maximo, mantem o consumo de RAM estavel, mesmo com 100 threads, parsear os dados com o uso de regex tb não consumiu muita memoria

Alguem conhece alguma maneira eficiente de obter codigo de uma pagina web que não consuma muita memoria ram ?

grato

Eu estou projetando um Web Crawler somente por diversão, e sei o problema que você está tendo.

Na verdade, não é que as URLs da base da sua pilha nunca esteja sendo visitada, o problema é que a sua pilha sempre vai receber novas URL, fazendo com que a base sempre esteja com um nível de prioridade inferior.

Vamos imaginar por exemplo um site com a seguinte estrutura:

Index
|— Produtos
|— Funcionários
|— Sobre
|— Contato
|— Telefone
|— Email

O nosso Web Crawler começa a varrer os links da página Index. Então a pilha ficaria da seguinte forma:

Index -> Produtos -> Funcionários -> Sobre -> Contato (Contato é o topo da nossa pilha no momento).

Uma vez que varremos a Index, devemos passar para a próxima página. Aqui vai depender da sua lógica, mas vamos supor que ela pegue como próxima página o topo da nossa pilha. Sendo assim, nossa pilha ficaria da seguinte forma:

Index -> Produtos -> Funcionários -> Sobre -> Contato -> Telefone -> Email

E depois o processo iria se repetir até chegar em um momento onde não se tem mais saída (Algo quase impossível de ocorrer, considerando a topologia da rede).

Caso a sua lógica não seja essa, posta aqui no tópico a sua lógica para nós discutir.

O que eu acho o ideal é construir uma lista de adjacências, onde a lista principal são as páginas pai, e as de adjacência são as páginas filho. Dessa forma eu consigo controlar e analisar a semântica das conexões.

Veja o esquema abaixo:

Index -> Produtos -> Funcionários
|
Produtos -> Eletrônicos
|
Funcionários - Administrativos -> Operacional

Fica muito fácil trabalhar com as URLs organizadas em Pai/Filho e também útil para extrair informações.

Stacker, vc esta certo, a minha logica era essa mesmo, então eu alterei a estrutura para uma fila FIFO. A ordem dos links a serem vivitados não tem importancia no meu caso, tambem criei filtros pro spider rodar so dentro de um site se eu quiser, pq tava acontecendo do meu spider começar em um dominio e acabar parando em sites que eu não queria.

O meu grande problema agora e liberar memoria. Mesmo atribuindo null para as referencias de objetos , a memoria não é desalocada. Vou colocar o codigo de teste que eu estou avaliando

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class teste{

private static void visitaSite(String url){
	
try{	
	System.out.println("Visitando " + url); 
	
	//conecta no site e pega o codigo html
	Document codigo = Jsoup.connect(url).userAgent("Mozilla").timeout(10*60*1000).get();
	 codigo = codigo.normalise();
	 
	//obtem o texto do site visitado
	String pagina = codigo.text();
	   
	System.gc();
	   
    System.out.println(" Site visitado "); 
    
    codigo = null;
    pagina = null;
    
  }catch(Exception e){
	     visitaSite(url); }
	
}//end of visitaSite	

public static void main(String args[]){
	
visitaSite("http://www.globo.com");	
visitaSite("http://www.bol.uol.com.br");	
visitaSite("http://www.terra.com.br");	

while(true)

try {
	Thread.sleep(60*60*1000);
} catch (InterruptedException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}
	
}//main

}//end of class

a memoria não cresce muito porem não é liberada. Eu creio que este codigo esta correto. Como que vc esta fazendo para pegar o codigo html de um site ? a sua função consome muita memoria ?

Utilize a biblioteca import java.net.* e use a classe URL.

Fica bem mais fácil de baixar conteúdo.

Eu ainda não terminei a implementação do meu Web Crawler, só trabalho nisso nas horas vagas, então não sei como será o consumo de memória.

Só terminei de implementar minhas estruturas de dados, que eu fiz questão de implementar do zero para ter maior controle.

Quanto ao consumo, onde você está armazenando essas páginas para o analisador sintático identificar os links?

Depois de quantas páginas analisadas que o consumo de memória aumenta?

Desculpa, agora que vi que você armazena numa String.

Nem prestei muita atenção no código :lol:.

Vou testar aqui no meu computador e já te respondo.

Eu acho que descobri o motivo pelo qual o consumo de memória está alto, mas antes eu preciso saber como você está identificando os links da página.

Você pode postar o código completo?

Certamente você está visitando a mesma página várias vezes, o que está acarretando em um maior consumo de memória.

O problema de estar visitando uma mesma página várias vezes é proveniente da lógica da Pilha.

Recomendo você experimentar a lista ligada de adjacências, seguindo a regra que descrevi anteriormente.

Stacker, não creio que seja este o motivo do meu consumo de memoria alto. o meu memory leak deve estar em dois pontos

1- As APIs do JSoup que eu estou usando pois não sei como elas trabalham com a memoria

2 - O meu buffer de links que é na verdade um ArrayBlockingQueue ou seja é uma fila

Como não da pra trabalhar com indices na fila, não da pra mim setar uma posição com NULL e eu não sei se ao executar um fila.pool() este fato ocorre

Mas vamos aos fatos, eu to trabalhando com 100 threads. é mais facil pro SO criar uma thread do que o contexto inteiro de um processo, mas cada thread vai consumnindo os recursos do meu programa ate atingir o limie e derrubar a maquina virtual.

Criar threads esta aumentando do consumo de RAM, mas pelo visto é algo inevitavel. Tem outro fator ai nesse meio, o buffer de links que é alimentado muito mais rapido do que é consumido

por exemplo se eu visitar um site e extrair 20 links dele, eu consumi 1 e inseri 20 no buffer. Se eu visitar 3 sites eu consumo 3 e terei um buffer com total de 60 links, ou seja, o buffer cresce muito rapido e consome mais RAM, e este buffer so contem links unicos.

eu estou pensando em algumas saidas:

1 - setar a memoria que a maquina virtual vai usar

2 - modularizar o projeto e transformar cada funcionalidade em um novo programa, vai ser custoso pro SO ficar chamando a maquina virtual com frequencia, mas a RAM vai sendo desocupada.

3 - trocar o JSoup pra metodos desenvlvidos por mim, pra controlar os objetos criados, etc

4- testar outras APIs

O Spider fatalmente vai consumir muita banda de rede e memoria, a banda de rede nõa me importa, o link pode ser sugado sem problemas, mas a RAm pode dar muita dor de cabeça, estas medidas que eu citei podem servir de alguma coisa

Vc concorda com o meu ponto de vista ? vcs do GUJ concordam ? sugerem outra coisa ?

esqueci de mencionar que o meu buffer de links e global e todas as threads inserem e removem links neste buffer, ou seja, os acessos são synchronized ok ? é o meu unico objeto static

up !

Bom dia amigos,

Apos todo este tempo de testes, - quase um mês - eu posso declarar que eu resolvi os problemas pelo qual eu estava passando com este Spider.

As minhas atitudes foram isolar trechos de códigos e testar cada um deles para descobrir os erros. Apos esta etapa eu reescrevi alguns trechos que estavam sendo executados pelas threads e tudo se resolveu.

Eu tive que transformar pequenas funções em uma função maior. Esteticamente não é aconselhável criar funções muito grandes mas com isso foi possível reduzir a criação de de objetos, variáveis, etc, o que melhorou a performance da aplicação.

Com as novas modificações, o GC conseguiu liberar memoria em índices que variaram de 50 a 100 Megas de RAM sem alterações de configuração da JVM ou qualquer outro parâmetro adicional, comprovando assim o sucesso das modificações.

Eu não posso liberar os fontes completos da aplicação, mas vou deixar algumas dicas que me foram úteis e que podem servir aos desenvolvedores que desejarem criar programas deste tipo em JAVA.

1 - é besteira falar isso, mas Java é uma das, se não for a melhor linguagem para desenvolver spiders. Existem muitos recursos disponíveis que facilitam e muito a implementação. Se vc já tentou fazer isso em Delphi por exemplo, sabe do que eu estou falando

2 - Escreva código que vc possa isolar e fazer testes de stress, assim vc descobre e corrige Memory leaks

3 - Jsoup - a melhor API para processar HTML que eu usei, sem sombra de dúvidas, usem sem medo.

4 - A melhor estrutura que eu achei para armazenar links foi usar uma fila FIFO (LinkedList), não cheguei a testar arvores, que são log de N para busca, mas se alguém quiser tentar, a ideia é boa.

5 - Utilize filtros para o spider, é muito mais vantajoso navegar em um domínio do que navegar a esmo na net. Assim vc aumenta as chances de extrair a informação desejada com mais eficiência e evita cair em spider traps. Mas claro, tudo vai variar do objetivo de cada projeto. Em navegações por muitos sites, eu recomendo limitar o numero de links visitados.

6 - O consumo de RAM tende a ficar elevado mesmo, por fatores como: quantidade de threads e buffers de links. O segredo é evitar OutOfMemory, não vai rolar rodar uma aplicação deste naipe só com uns 10 Mega de RAM

7 - Utilize expressões regulares para extrair informações não obvias

8 - Escreva aplicações de console, é mais pratico, economiza tempo e vc não precisa gastar recursos do sistema para desenhar
telas e nem sincronizar métodos para threads exibirem informações

9 - Thread Pool vai variar da sua implementação, neste projeto não foi necessário usar este recurso, porque as threads não são
recriadas a todo instante.

10 - Boa sorte !! :slight_smile:

Agradeço às pessoas que tiveram interesse em me ajudar nesta tarefa

muito obrigado

inté