Nem só de MVC vive o Zend Framework. Neste artigo, irá se mostrar como utilizar o ZF para a criação de um Spider e transformação do mesmo em um Web Service.
O que são spiders? A grosso modo, são robôs construídos com o objetivo de simular o comportamento de um navegador. Portanto, através de um spider pode-se realizar requisições HTTP de forma automatizada. A idéia é criar um spider e disponibilizá-lo através de um WebService. Para quem não sabe, WebService é uma forma de comunicação entre sistemas. Ou seja, após a transformação do Spider em um WebService, o serviço poderá ser acessado por qualquer aplicação, independente da linguagem da mesma.
Existem várias tecnologias para construção de WebServices; A tecnologia usada no exemplo será SOAP. O artigo não entrará em detalhes da tecnologia SOAP, mas citará as responsabilidades de forma simples e objetiva de cada componente pertencente à tecnologia para entendimento do artigo.
Resumidamente, um Spider é uma classe que realiza requisições HTTP, faz o parse do HTML gerado pela requisição, geralmente utilizando expressões regulares, para recuperação das informações relevantes ao domínio do problema e faz a estruturação dessas informações, retornando-as em objetos que representam o domínio do problema. Portanto, uma das características marcantes no desenvolvimento de um Spider é a possível quantidade de modificações necessárias ao longo do tempo, justamente por depender do HTML gerado pelo site. Caso o site mude o HTML de retorno, há a necessidade de mudança no Spider, seja na forma como as requisições sejam feitas ou na forma como os resultados são parseados.
Enfim, a idéia é seguir o conjunto de passos abaixo. A construção e transformação de um Spider em WebService segue basicamente este fluxo.
1 - Definir serviço de um site e analisar o fluxo de entrada e saída.
O serviço a ser usado neste artigo será do site do Tribunal Superior Eleitoral, mais especificamente o serviço do link <http://www.tse.gov.br/internet/servicos_eleitor/consultaSituacaoNome.htm>.
Este serviço do TSE tem como objetivo exibir a situação cadastral de um eleitor, bem como exibir seu título também. Para isso é necessário entrar com o NOME COMPLETO e DATA DE NASCIMENTO. Caso encontre o cidadão, como saída se tem o TITULO DE ELEITOR, NOME DO ELEITOR, DATA DE NASCIMENTO e SITUAÇÃO DO TÍTULO. Caso não encontre o cidadão, uma mensagem de erro é exibida pela aplicação.
As telas abaixo descrevem os casos acima.
O que são spiders? A grosso modo, são robôs construídos com o objetivo de simular o comportamento de um navegador. Portanto, através de um spider pode-se realizar requisições HTTP de forma automatizada. A idéia é criar um spider e disponibilizá-lo através de um WebService. Para quem não sabe, WebService é uma forma de comunicação entre sistemas. Ou seja, após a transformação do Spider em um WebService, o serviço poderá ser acessado por qualquer aplicação, independente da linguagem da mesma.
Existem várias tecnologias para construção de WebServices; A tecnologia usada no exemplo será SOAP. O artigo não entrará em detalhes da tecnologia SOAP, mas citará as responsabilidades de forma simples e objetiva de cada componente pertencente à tecnologia para entendimento do artigo.
Resumidamente, um Spider é uma classe que realiza requisições HTTP, faz o parse do HTML gerado pela requisição, geralmente utilizando expressões regulares, para recuperação das informações relevantes ao domínio do problema e faz a estruturação dessas informações, retornando-as em objetos que representam o domínio do problema. Portanto, uma das características marcantes no desenvolvimento de um Spider é a possível quantidade de modificações necessárias ao longo do tempo, justamente por depender do HTML gerado pelo site. Caso o site mude o HTML de retorno, há a necessidade de mudança no Spider, seja na forma como as requisições sejam feitas ou na forma como os resultados são parseados.
Enfim, a idéia é seguir o conjunto de passos abaixo. A construção e transformação de um Spider em WebService segue basicamente este fluxo.
1 - Definir serviço de um site e analisar o fluxo de entrada e saída.
O serviço a ser usado neste artigo será do site do Tribunal Superior Eleitoral, mais especificamente o serviço do link <http://www.tse.gov.br/internet/servicos_eleitor/consultaSituacaoNome.htm>.
Este serviço do TSE tem como objetivo exibir a situação cadastral de um eleitor, bem como exibir seu título também. Para isso é necessário entrar com o NOME COMPLETO e DATA DE NASCIMENTO. Caso encontre o cidadão, como saída se tem o TITULO DE ELEITOR, NOME DO ELEITOR, DATA DE NASCIMENTO e SITUAÇÃO DO TÍTULO. Caso não encontre o cidadão, uma mensagem de erro é exibida pela aplicação.
As telas abaixo descrevem os casos acima.
2 - Modelar domínio do problema.
Após a análise do site, definindo entradas e saída, há a necessidade de se definir as classes que irão representar a modelagem do serviço.
Inicialmente, como já foi dito, sabe-se que o Spider é representado por uma classe. Por convenção, irá se usar o nome 'EleitorSpider'. Já se percebe também um método desta classe, que é justamente o método responsável por fazer a busca das informações. O nome do método será 'consultaSituacao'. Como entrada o método receberá um nome e uma data de nascimento. E o retorno do método será um objeto de uma outra classe que, por convenção, se chamará EleitorModel. Esta classe é responsável por armazenar as informações que serão retornadas pelo método 'consultaSituacao'. Ou seja: TITULO DE ELEITOR, NOME DO ELEITOR, DATA DE NASCIMENTO e SITUAÇÃO DO TÍTULO.
A imagem abaixo descreve a modelagem citada acima.
3 - Implementar a classe EleitorSpider.
Hora de implementar a classe EleitorSpider. Esta classe será responsável por encapsular todos os métodos para recuperação das informações.
Para implementar essa classe, irá se usar o ZendFramework. Mais especificamente a classe Zend_Http_Client. Essa classe possui métodos para fazer requisições HTTP (GET e POST) bem como manipular o resultado destas requisições. Portanto, perfeito para implementação do Spider. Além disso, nesta etapa do processo de desenvolvimento é muito útil usar ferramentas que analisam a requisição HTTP, facilitando o trabalho do desenvolvedor. Essa facilidade diz respeito às variáveis que são passadas através das requisições, principalmente pelo método POST. Uma ferramenta muito útil é o HTTPFox. Ele funciona como um plugin do Firefox e permite a visualização das requisições HTTP e conteúdo gerado.
A imagem abaixo mostra com exatidão a requisição HTTP/POST feita pelo site para buscar as informações e que variáveis são passadas. Note que o HTTPFox funciona como um ícone bem no canto inferior direito do navegador e precisa ser ativado para visualização das requisições.
Analisando pelo HTTPFox, percebe-se o envio das variáveis necessárias para a consulta e a presença de um token. Este token é gerado pela página principal (a que carrega o formulário). Em geral, ao se criar spiders, pode-se enviar os dados via POST diretamente para a página de destino. Porém alguns sites implementam mecanismos para evitar a construção de spiders. Nesse caso, há a necessidade de seguir o fluxo completo.
Portanto, o fluxo da implementação já deve ficar definido na cabeça do programador, antes mesmo de começar a implementação. Este fluxo é:
a) Carregar página do formulário e recuperar o token a ser enviado posteriormente
b) Enviar os dados via POST (Token + Dados de pesquisa)
c) Parsear os resultados, montando objeto de saída (EleitorModel).
A classe EleitorModel é bem simples e a implementação dela é mostrada abaixo:
Note o uso dos comentários que documentam os atributos. São necessários e obrigatórios para facilitar a construção de serviços mais adiante. Portanto, devem ser preenchidos.
Abaixo a implementação da classe EleitorSpider, seguindo estes fluxos.
A classe EleitorSpider possui um atributo privado (client) que representa o objeto da classe Zend_Http_Client. Ela é instanciada e criada no construtor da classe.
Na modelagem, definimos um método chamado 'consultaSituacao'. Percebe-se que ele é o único método público da classe. E para implementar o método, outros métodos, privados, foram criados. Ou seja, são métodos que são usados apenas por outros métodos da classe, neste caso, 'consultaSituacao' apenas.
Dividir para conquistar sempre é uma boa prática de programação. Por isso, a implementação do método 'consultaSituação é bem simples e se utiliza destes métodos privados (step1, step1Parse, step2, step2Parse) para completar a tarefa. Olhando para o fluxo mencionado acima e analisando os métodos percebe-se a seguinte situação:
- Passo 1 (step1 e step1Parse)
O primeiro passo cobre o fluxo descrito no item (a) acima. Ou seja, ele carrega a página do formulário para recuperar o token que é enviado via POST. Para tanto, é feita uma subdivisão de tarefas também. Um método para realizar a requisição (GET) do formulário (step1) e outro método para parsear o resultado procurando pelo token (step1Parse) através de uma expressão regular. Vale notar que a URL para acesso do formulário é "invisível" aos olhos de um usuário pelo navegador e é necessário o uso do HTTPFox para descobrir a mesma (experimente!). Isso acontece em alguns sites, para esconder a URL final de pesquisa. Mas não engana o HTTPFox que verifica todas as requisições e é uma ferramenta muito útil para construção de Spiders.
- Passo 2 (step2 e step2Parse)
O segundo passo cobre o fluxo descrito nos itens (b) e (c). E segue a mesma lógica do passo 1. A idéia sempre é essa. Um método para realizar a requisição e um método para parsear o HTML gerado pela requisição, devolvendo a estrutura de dados correspondente. Nesta etapa, é realizada a requisição POST, enviando os dados necessários para a consulta, bem como o token recuperado do passo 1. O método step2parse realiza o parse do resultado. Neste passo já é possível recuperar todas as informações de retorno, portanto o objeto EleitorModel é montado e retornado neste passo. Mas antes de tentar recuperar o resultado encontrado, cria-se uma expressão regular para encontrar a frase da não existência de um resultado válido. Não existindo, uma exceção é lançada. Após esta verificação é que se tenta realmente buscar os dados corretos. Isso garante que tenhamos um resultado coerente ao nosso modelo. Uma outra observação importante é com relação a codificação da página. Como a classe irá se transformar em WebService mais para frente e é de bom costume que um Web Service retorne dados codificados em UTF-8, há a necessidade de se transformar os dados recuperados para UTF-8 quando o HTML da página for diferente de UTF-8. Neste caso, o site do TSE já está em UTF-8, não havendo necessidade dessa conversão. Mas caso fosse, o uso da função 'utf8_encode' do PHP seria necessária.
Por fim, nosso Spider está criado e montado. Para aprofundar o conhecimento nas funções do Zend_Http_Client, o manual é uma boa fonte de referência.
4 - Testar o Spider.
O código abaixo testa o Spider criado. Ele pode ser executado tanto em linha de comando quanto pelo navegador. O arquivo criado tem o nome de 'testSpider.php'. Neste artigo, considera-se que todos os arquivos estejam dentro do mesmo diretório.
- Para executar via linha de comando:
php testSpider.php 'edson arantes do nascimento' '23/10/1940'
- Para testar pelo navegador:
http://localhost/artigos/spider_to_webservice/testSpider.php?nome=edson arantes do nascimento&data=23/10/1940
Os valores mencionados acima devolvem resultados coerentes. Use outros valores para testar as outras condições, como por exemplo, um nome/data inválidos.
O resultado final imprime na tela os valores do objeto EleitorModel do resultado pesquisado ou a mensagem de exceção lançada.
5 - Criando o WebService
Hora de criar a classe que funcionará como serviço. O código abaixo contém a implementação da classe.
Alguns comentários pertinentes à implementação da classe 'EleitorService':
- Implementa basicamente o mesmo método existente no 'EleitorSpider' (consultaSituacao), com os mesmos argumentos.
- Note o uso dos comentários em formato de documentação. São úteis para geração do WSDL do serviço através da classe 'Zend_Soap_AutoDiscover'.
- O construtor da classe 'Zend_Soap_AutoDiscover' mapeia que tipo de estratégia é utilizada para mapeamento. Neste exemplo, utiliza a estratégia que permite até vetores de objeto como resultado. Maiores detalhes na documentação da classe. Além disso, ele usa os comentários feitos para gerar o WSDL, daí a importância da documentação nas classes EleitorService e EleitorModel.
- O WSDL nada mais é do que um 'contrato' que descreve que operações são existentes no serviço bem como a interface de entrada e saída deste serviço. Representa uma URL que servirá para o cliente SOAP que irá se criar posteriormente.
- Abaixo da implementação da classe, existem dois possíveis trechos de código. O primeiro, caso seja passada a string 'wsdl' na url de chamada do serviço, carrega o wsdl para leitura. Já o segundo caso, instancia uma classe 'Zend_Soap_Server' que carrega o WSDL e configura a classe 'EleitorService' como uma classe de serviço. Neste exato instante, um WebService é criado e pronto para ser chamado por um Soap Client de qualquer linguagem de programação.
6 - Testando o WebService
Semelhante ao Spider, o WebService também tem um arquivo para teste, que neste artigo se chama 'testService.php'. Abaixo a implementação do mesmo:
Note que neste arquivo, não se faz uso da classe Spider. Não há 'includes' para a mesma. Apenas para a classe 'EleitorModel' que é justamente a classe de retorno do serviço. Os dados são recebidos via URL ou linha de comando, da mesma forma como no arquivo de teste do Spider.
Uma instância da classe Zend_Soap_Client é criada, passando como argumento a URL do WSDL e um vetor de opções como segundo argumento. Neste vetor, é feita a associação entre a estrutura de dados SOAP criada pelo serviço e a classe que a representará. No caso ambas têm o nome de 'EleitorModel' como padrão.
Depois, uma chamada é feita ao método do serviço 'consultaSituacao'. O grande lance é que este script de teste do serviço pode estar em outro servidor web, de uma outra rede. Aí percebe-se a chamada do método remoto através do contrato existente (WSDL). Ou seja, qualquer aplicação que consiga visualizar a URL do WSDL tem acesso ao método do serviço criado.
Agora é só testar o serviço e partir para o abraço!
---------------------------
Finalizando, algumas considerações devem ser feitas, ao se tratar de Spiders:
- Muitos sites protegem seus dados finais através de um CAPTCHA, que são aquelas imagens com letras e números que aparecem para o usuário preencher.
- Há mecanismos de quebra de um captcha, ou seja, robôs que conseguem ler o conteúdo de imagens. Geralmente utilizam técnicas de segmentação de imagens misturadas com técnicas de inteligência artificial.
- De acordo com o grau de mudança de um site, há sempre a necessidade de manutenção do Spider. Mudança de HTMLs, de URLs, de variáveis implicam em mudança no Spider também. O próprio exemplo deste artigo pode não funcionar após certo tempo. O que importa é que uma exceção será lançada caso isso ocorra.
- Os exemplos deste artigo foram desenvolvidos dentro da seguinte estrutura, a partir de 'localhost':
- artigos/spider_to_webservice/
- EleitorModel.php
- EleitorSpider.php
- testSpider.php
- EleitorService.php
- testService.php
- artigos/ZendFramework-1.10.8
- Portanto, caso mude a estrutura, atualize as URLs dos arquivos do artigo para a estrutura que corresponda a sua. Note a existência do ZendFramework na estrutura acima.
- Os arquivos deste artigo estão disponíveis para download neste link. Já estão inclusos os arquivos do ZendFramework da versão 1.10.8 no arquivo ZIP acima.
- Caso tenha dúvidas em relação há algum trecho de código acima (métodos de classes de Zend, expressões regulares, etc) basta postar um comentário no post deste artigo com a dúvida. Será um prazer discutí-la.
É isso aí. Bom desenvolvimento de Spider a todos!




