Front End

23 fev, 2007

Acessando serviços web do YAHOO!

Publicidade

Olá pessoal. Esse é meu primeiro artigo aqui no iMasters e espero que vocês gostem.

Conhecimentos necessários.

Para o melhor aproveito deste artigo é bom que você tenha familiaridade com alguma linguagem de programação server-side e básicos conhecimentos em Ajax e W3C DOM.

Vou explicar como fazer um serviço de busca no YAHOO! usando Ajax e PHP.

Para entender melhor o que seria um Web Service clique aqui.

As técnicas mais conhecidas são SOAP (Simple Object Access Protocol) e a WSDL (Web Services Description Language) mas a técnica que o YAHOO! escolheu é a REST.

A REST é o protocolo publico que o YAHOO! disponibiliza seus serviços. Eles a escolheram pois são fáceis de entender e apreciam sua acessibilidade à maioria das linguagens de programação modernas.

Por que usar a REST (Representational State Transfer)?

Com ela podemos solicitar dados através de uma URL (via GET) de um serviço e adicionamos somente parâmetros para a busca. É a forma que usamos hoje para muitos serviços. Um exemplo seria http://www.minhapagina.com.br?categoria=futebol.

Técnicas como a SOAP é necessário o envio de uma XML via POST, isso pode ser mais propenso a erros, pois podemos esquecer uma aspa e outra ali.

Sobre o serviço do YAHOO!

Além de você poder buscar em toda a internet, você também pode fazer a busca somente em um site específico. Desta forma, você aproveita os índices os YAHOO! para criar uma busca interna em seu site, sem se preocupar em cadastrar em um banco de dados.

Para maiores detalhes sobre parâmetros que podemos passar, acesse: http://developer.yahoo.com/search/web/V1/webSearch.html

XMLHttpRequest é o método perfeito para o acesso?

Considerando que o retorno do conteúdo do YAHOO! vem através de uma XML e podemos usar os métodos W3C DOM para manipular os resultado, ele é um ótimo método. Porém esse método só tem acesso a recursos dentro do mesmo domínio que o documento que o conteúdo foi gerado.

O que podemos fazer é criar uma gateway (nada mais é que uma porta de saída). O gateway aceitará solicitações do objeto XMLHttpRequest e a encaminhará para o YAHOO!. Quando o YAHOO! responder com os resultados da busca o gateway simplesmente roteará para o navegador. A vantagem de usamos um gateway é que posteriormente podemos estender esse serviço com outro buscadores.

Criando a Gateway

Para esse artigo usaremos PHP como linguagem server-side, mas você pode escolher a linguagem que esteja mais familiarizado. A gateway só vai ler o conteúdo de uma URL, já com os parâmetros de busca concatenados na string.

<?php
	//informa ao navegador que o retorno do documento é uma xml
	header ("content-type: text/xml");
	//recebe os parametros para gerar a query
	$query = (string) urlencode ( $_REQUEST['query'] );
	$resultados = (int) $_REQUEST['resultados'];
	$regiao = (string) $_REQUEST['regiao'] != "internet" ? $_REQUEST['regiao'] : NULL;
	$site = (string) $_REQUEST['site'];
	
	//Busca o resultado da busca 
	$handle = fopen ("http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=seu_app_id&type=all&query=".$query."&results=".$resultados."®ion=".$regiao."&site=". $site,'r');
	//enquanto houver resutados ele exibe na tela para o ajax
	while (!feof ($handle)) {
	   $buffer = fgets( $handle , 4096 );
	   echo $buffer;
	}
	//fecha a URL solicitada
	fclose ( $handle );
?>

Creio que os comentários já são auto-explicativos.

Criando o JavaScript

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Minha busca no YAHOO!</title>
<script type="text/javascript">
var xmlHttp;
	//CRIA UMA CONEXÃO XMLHttpRequest
	function ajaxInit () {
		try {
			xmlHttp = new XMLHttpRequest();
		}
		catch (ee){
			try {
				xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch (e) {
				try {
					xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
				}
				catch (E) {
					alert("Seu browser não está comJavaScript ativado!");
				}
			}
		}
	}
	
	function buscar (){
		//exibe um status da busca para o usuario final
		loading();
		//URL que iremos utilizar com gateway 
		var url = "http://localhost/imasters/yahoo_gateway.php?" + generateQueryString();
		//instacia o objeto XMLHttpRequest
		ajaxInit();
		//chama a função que controla os estados da requisição
		xmlHttp.onreadystatechange = mudarEstado;
		//abre a conexão
		xmlHttp.open ( "GET" , url , true );
		xmlHttp.send(null);
	}
	
	function loading () {
		//somente adiciona um texto carregando para o usuario saber quer algo está sendo feito
		var resultados = document.getElementById('resultados').innerHTML = "Carregando...";
	}

A primeira coisa a fazer é criar o objeto XMLHttpRequest. Creio que você já esteja acostumado a manipular esse objeto.

O próximo passo é criar a função que será chamada ao executar o evento da busca.

Nesta função, também não temos tantas novidades. Somente atribuímos a função mudarEstado para cada alteração onreadystatechange.

/**
 * Busca os valores e os concatena em uma string para enviar ao nosso gateway
 */
function generateQueryString() {
	//pega os valores da busca do usuario
	var query = document.getElementById('query').value;
	var quantidade = document.getElementById('quantidade').value;
	var site = document.getElementById('local').value;
	var regiao = document.getElementById('regiao').value;
	var ts = new Date().getTime();
	//A hora ao final da query string é somente para evitarmos cache
	return "query=" + query + "&resultados=" + quantidade + "&site=" + site + "®iao=" + regiao + "&ts=" + ts;
}

function mudarEstado(){
	if ( xmlHttp.readyState == 4 ){
		if ( xmlHttp.status == 200 ) {
			parseTodosResultados();
		}
		else {
			alert ("Ocorreu um erro ao acessar o YAHOO!");
		}
	}
}

Na função mudarEstado verificamos se o documento foi recebido com sucesso. Caso não tenha problemas, chamamos a função parseTodosResultados.

/**
 * Vamos fazer o parse de toda XML aqui
 * Ela navega em cada nó RESULT e passa para a parseResultado para exibirmos para o usuario final
 */
function parseTodosResultados (){
	//apaga se houver resultados anteriores!
	limpar();

	//pega o nó com os resultados "Result"
	var resultados = xmlHttp.responseXML.getElementsByTagName("Result");
	//Caso não encontre resultados
	if ( resultados.length == 0 ){
		document.getElementById('resultados').innerHTML = "não há resultados para a busca";
		return;
	}
	//para saber o total de resultados
	headerSets();
	
	var resultado = null;
	//navega entre os resultados
	for (var i = 0 ; i < resultados.length; i++){
		resultado = resultados[i];
		parseResultado( resultado );
	}
}
/**
 * Função para uma futura paginação de resultados.
 */
function headerSets(){
	//antes/após lermos as tags result podemos querer gerar uma páginação dos resultados
	//para isso o YAHOO! disponibiliza como a primeira  tag ResultSet onde eles exibem o total de resultados disponiveis
	var resultSet = xmlHttp.responseXML.getElementsByTagName ("ResultSet");
	var totalResultados = resultSet[0].attributes['totalResultsAvailable'].nodeValue;
	//para a criação de páginação de resultados deixarei como exercicio.
}

Na função parseTodosResultados() atribuímos a variável “resultados” todos os nós <Result>. Caso não tenha nenhum nó, exibimos para o usuário que não há resultados.

Do contrário vamos navegar em cada nó <Result> e passaremos os resultados para a função parseResultado().

/**
 * Esssa função recebe um nó result e irá extrair seus valores
 * Ela utiliza métodos W3C DOM para gerar código XHTML 
 * OBS.: criei uma função chamada getElementoTexto ela extrai o texto do nó informado. 
 */
function parseResultado ( resultado ){
	//cria uma div onde o resultado ficara dentro
	var div = document.createElement("div");
	//cria um elemento h3 para os titulos
	var titulo = document.createElement("h3");
	//extrai do nó "Title" seu texto e adiciona o texto para o titulo
	titulo.appendChild( document.createTextNode( getElementoTexto( resultado , "Title" ) ) );
	//adiciona o titulo para a div
	div.appendChild ( titulo );
	//cria um texto e extrai o texto do no "Sumary"
	var descricao = document.createTextNode( getElementoTexto( resultado , "Summary" ) );
	//adiciona para a div o texto
	div.appendChild( descricao );
	//cria um elemento <br /> para quebrar a linha
	div.appendChild( document.createElement("br") );
	//criamos um elemento para um link
	var tagLink = document.createElement( "a" );
	//adiciona o atrubuto href da tag e extrai o link absoluto até a página da busca
	tagLink.setAttribute ("href" , getElementoTexto( resultado , "ClickUrl" ));
	//para abrir em uma nova página
	tagLink.setAttribute ("target" , "_blank" );
	//o texto do link que vai ser exibido para o usuario final
	txtLink = document.createTextNode( getElementoTexto ( resultado , "DisplayUrl") );
	//adiconamos o texto do link para a tag A
	tagLink.appendChild ( txtLink );
	//coloca o link dentro da div
	div.appendChild( tagLink );	
	//um linha vertical
	div.appendChild( document.createElement("hr") );
	//colocamos toda a div no local dos resultados
	document.getElementById('resultados').appendChild( div );
}

A função parseResultado nada mais faz que criar o XHTML que iremos exibir os resultados. Nela utilizamos o W3C DOM para gerarmos a página de resultados.

O primeiro passo da função é criar uma <div> que irá abrigar todos os dados da busca (titulo, descrição, link, etc..).

Depois criamos um <h3> que nos servirá como titulo da cada resultado.

Então nos adicionamos ao titulo o texto contido na tag <Title> e encontramos outra função muito importante a getElementoTexto.

/**
 * Essa função extrai o texto dentro de uma tag especifica
 * Ela recebe como parâmetro todo o nó result e ainda o elemento que desejamos extrair
 */
function getElementoTexto ( parentNode , atributo ) {
	//no nó result ele ira busca todas as tag com o atributo q desejamos ("title","sumary")
	var childTag = parentNode.getElementsByTagName( atributo );
	//caso tenha algum valor
	if (childTag[0].firstChild != null)
		//do primeiro nó encontrado (firstChild) ele retorna o conteudo dentro da tag (nodeValue)
		return childTag[0].firstChild.nodeValue;
}

function limpar(){
	//pega os resultados antigos
	var resultados = document.getElementById('resultados');
	while (resultados.childNodes.length > 0){
		//remove o primeiro no dos resultados antigos
		resultados.removeChild(resultados.childNodes[0]);
	}
}
</script>
<style type="text/css">
#resultados {
border: 1px solid #ccc;
background-color: #eee;
font-family: Arial, Helvetica, sans-serif;
}
#resultados h3 {

color: #000066;
}
</style>
</body>
</html>

Considerações finais

Como vocês viram, é bem simples utilizar a API do YAHOO! Para realizar consultas na internet ou mesmo no seu site. Confira na parte de developers do YAHOO! outros parâmetros que podemos utilizar para filtrar melhor ainda a busca.

Outra coisa importante, que vale a pena mencionar, é que na primeira TAG de retorno do YAHOO!, ele nos informa a quantidade de resultados. A função headerSets pega esses valores. Eu só não implementei a parte de criar a paginação de resultados. Deixo isso como um tarefa de casa. Tente fazê-la utilizando métodos W3C DOM.

Estes são os arquivos do artigo Download

Bom pessoal, é isso! Até a próxima!