pontoNETpt
A comunidade PontoNetPT está direccionada a todos os programadores que trabalhem com a plataforma .NET.
Abordagem técnica ao projecto ITComponents.net parte 2
 Tal como prometi aqui vão os detalhes técnicos da implementação do projecto ITComponents.

Muitas pessoas perguntam como é que faço a recolha dos produtos dos variados sites que depois apresento no ITComponents. Quem me pergunta isso geralmente está à procura de uma resposta que tenha a ver com um produto que ja exista.

Pois é, a resposta aqui vai. Não uso nenhum produto. Uso um crawler feito por mim em C#. Pode parecer complicado fazer isto mas de facto não é.

Para cada loja que existe no ITComponents eu tenho de dar as seguintes informações ao meu crawler:
 1) Expressão regular para achar os links dentro de uma pagina
 2) Expressão regular para encontrar os produtos dentro de uma pagina
 3) Profundidade de iterações que vou fazer no site
 4) Pagina Inicial de crawl

Isto é o principal. Claro que existem outras pequenas coisas como: qual o caracter decimal, site hospedado em host nacional ou internacional, parametros de query string que são para ignorar, etc.

O Motor de crawl não é mais do que uma serie de "gets" de paginas do site em que depois pego no html retornado e aplico-lhe algumas expressões regulares.

WebClient client ;
bytes[] bytes;
client= new WebClient();

bytes = client.DownloadData("http://www.omeusite.com"); 

Após isto feito eu tenho um Array de Bytes com o html retornado pelo site. Pego nesse array de bytes e passo tudo para texto para poder trabalhar com strings. Com a string com o html eu primeiro aplico a expressão regular para encontrar os links dessa pagina. Todos esses links serão percorridos da mesma maneira na iteração seguinte. Exemplo:

Primeira Iteração - É sempre apenas um URL - O html desse url tem 20 links que são adicionados a um ArrayList

Segunda Iteração - São percorridos esses 20 links à procura de mais links. No conjunto dos 20 links são encontrados 300 links

Terceira Iteração - São percorridos esses 300 links .................. e por ai a fora até atingir a profundidade de iterações que eu estipulei para aquele site.

Para cada link que vou buscar o html eu aplico tambem uma expressão regular para encontrar os produtos. Essa expressão regular vai buscar os seguintes dados: Id, Nome, Descrição, Link, Preço

private MatchCollection match(string html, string regularExpression)
{
 RegexOptions options = ((RegexOptions.Multiline |  RegexOptions.IgnorePatternWhitespace) | RegexOptions.IgnoreCase);
 
 Regex reg = new Regex(regularExpression, options);

 return reg.Matches(html,0);
}

O codigo do motor de crawl é relativamente simples. A unica dificuldade aqui está em construir a expressão regular que vai buscar os produtos ao html.

Um exemplo de uma expressão regular usada num dos sites que faço crawl é a seguinte:

<td\sclass="TextoAzulPequeno"><strong><a\s href="(?<Link>[^"]*)(?<=(?<Id>\d*))[^>]* >(?<Name>.*?)</a>.*?<td\s width="71%"\sclass="TextoPretoPequeno" >(?<Description>.*?)</td>.*?<td\s width="27%"\sclass=" TextoPretoPequeno">[^:]*:\s*(?<Price>\d*(?:[\.|,]\d*)*)

Dentro disto que acredito que pouca gente tentará perceber está
(?<Link>[^"]*)
(?<Id>\d*)
(?<Name>.*?)
(?<Description>.*?)
(?<Price>\d*(?:[\.|,]\d*)*)

Estes são os campos retornados por todos os matches da regular expression no html de uma pagina.

Com estes dados basta inserir um produto indexado ao site respectivo na base de dados e pronto, ja está. Só falta passar para o ITComponents.

À data em que escrevo este post o ITComponents tem 46 lojas adicionadas. Isto quer dizer 46 expressões regulares tão ou mais complicadas que aquela do exemplo. Sempre que um site muda o seu layout eu tenho de refazer a expressão regular.

Isto é o motor de crawl. A Aplicação que fiz é um pouco mais que isto.

Quando eu vou fazer crawl eu não o faço site a site. A minha aplicação tem um mecanismo em que eu digo-lhe quantos sites eu quero fazer crawl ao mesmo tempo e ela lança esse numero de threads cada uma a aplicar o motor de crawl num site diferente. O numero de threads que uso no momento é 9 pois a cada thread esta associado um pequeno form que me diz como esta a correr o crawl. 9 threads porque com 9 threads existem 9 forms iguais que me preenchem o ecrã. Sempre que uma thread termina outra começa e é criado um outro form na mesma posição do ecrã.


(Clique para ver imagem maior)

O motor de crawl foi feito de uma maneira genérica e como tal é um assembly separado da minha aplicação. Por ser genérico permitiu-me fazer outras coisas como por exemplo um Web Service que disponibiliza as cotações da Bolsa Portuguesa. Este Web Service está acessivél em http://www.ITComponents.net/WebServices/PSI20CotacoesWebService.asmx.

Sempre que é feito um pedido ao WebService o crawler é disparado sobre um site para ir buscar as cotações e disponibiliza-las a quem as pediu. Como é apenas um teste este WebService tem uma cache de 5 minutos por isso dois pedidos ao WebService em 2 minutos só o primeiro é que faz crawl sobre o site.

Infelizmente a unica opção que têm para invocar este WebService é através de SOAP pois o meu host tem a .NET framework 1.1 e por defeito a versão 1.1 disabilita o GET. Para o testarem basta adicionar uma Web Reference ao URL do WebService, criar uma pagina ASP.NET e no metodo load colocar o seguinte código:

nameOfWebReference.PSI20CotacoesWebService webService = new nameOfWebReference.PSI20CotacoesWebService();
nameOfWebReference.PSI20CotacoesWebServiceResult webServiceResult= webService.GetCotacoes();

Response.Write("<table>");
foreach (nameOfWebReference.Empresa empresa in webServiceResult.CotacoesEmpresas)
{
Response.Write("<tr>");
Response.Write("<td>" + empresa.Nome + "</td>");
Response.Write("<td>" + empresa.Hora + "</td>");
Response.Write("<td>" + empresa.Cotacao + "</td>");
Response.Write("<td>" + empresa.Variacao + "</td>");
Response.Write("<td>" + empresa.Volume+ "</td>");
Response.Write("</tr>");
}
Response.Write("</table>");

Eu sei que não é a melhor maneira de fazer isto, podia fazer bind bla bla bla. Esta é apenas uma maneira rapida de mostrar como funciona.

Este WebService vai estar disponivel por uns tempos apenas a titulo demostrativo.


Posted 31-8-2005 11:51 por Daniel Vinagre
Filed under:

Comments

Anonymous wrote re: Abordagem técnica ao projecto ITComponents.net parte 2
on 1-7-2009 1:40
Caro amigo,

"Eu sei que não é a melhor maneira de fazer isto, podia fazer bind bla bla bla. "

o que interessa é que as coisas funcionam e o resultado final é bom.
um colega uma vez disse-me que o informatico é aquele tipo que acha que o trabalho dele nunca esta acabado e ha sempre coisas que se podiam melhorar.. por isso, se não é a melhor maneira, azar! funciona, é eficiente e o resultado é excelente.

parabéns!

Anonymous wrote re: Abordagem técnica ao projecto ITComponents.net parte 2
on 1-7-2009 1:40
Deves poder activar o GET colocando isto no teu web.config, dentro de system.Web:

<webServices>
<protocols>
<add name="HttpGet" />
<add name="HttpPost" />
<add name="Documentation" />
</protocols>
</webServices>
Anonymous wrote re: Abordagem técnica ao projecto ITComponents.net parte 2
on 2-7-2009 1:58
Caro amigo,

"Eu sei que não é a melhor maneira de fazer isto, podia fazer bind bla bla bla. "

o que interessa é que as coisas funcionam e o resultado final é bom.
um colega uma vez disse-me que o informatico é aquele tipo que acha que o trabalho dele nunca esta acabado e ha sempre coisas que se podiam melhorar.. por isso, se não é a melhor maneira, azar! funciona, é eficiente e o resultado é excelente.

parabéns!

Anonymous wrote re: Abordagem técnica ao projecto ITComponents.net parte 2
on 2-7-2009 1:58
Deves poder activar o GET colocando isto no teu web.config, dentro de system.Web:

<webServices>
<protocols>
<add name="HttpGet" />
<add name="HttpPost" />
<add name="Documentation" />
</protocols>
</webServices>

Add a Comment

(requerido)  
(opcional)
(requerido)  
Remember Me?
If you can't read this number refresh your screen
Enter the numbers above:  
Powered by Community Server (Commercial Edition), by Telligent Systems