Ir para o conteúdo.

Página Como Usar os Dados Abertos da Câmara

JS: Um pouco de cada vez

A nova API do Dados Abertos entrega dados em XML e JSON de forma paginada. Neste tutorial, você pode saber mais sobre como funciona o mecanismo de paginação, e ver um exemplo em HTML e JavaScript com uma forma de juntar os resultados de diferentes requisições em um só conjunto de dados.

A nova interface de programação de aplicações (API) do serviço Dados Abertos da Câmara foi projetada para fazer jus ao nome: sua finalidade principal é fornecer conjuntos reduzidos de dados, selecionados por meio de diversos parâmetros de busca, para aplicações leves que exibam ou processem esses dados de maneiras particulares. É um tipo de trabalho diferente do que é feito com os arquivos de dados para download, que contêm todas as informações sobre todos os itens de suas respectivas entidades — e por isso exigem disponibilidade de comunicação ("largura de banda") para o download de vários megabytes de uma vez, grandes espaços de memória RAM e processamento intenso na seleção e cruzamento dos dados.

Para manter os conjuntos de dados selecionados relativamente pequenos e a transferência deles mais veloz, a nova API segue um padrão muito comum em outras interfaces que seguem a arquitetura REST: as URLs (ou, mais corretamente, os recursos) que fornecem resultados em listas dividem o resultado em páginas.

A seção links

Todas as respostas válidas do novo Dados Abertos, tanto em JSON quanto em XML, são entregues em uma estrutura padronizada de organização. Há uma seção com o nome dados, contendo os resultados da requisição, e uma seção com o nome links. O conteúdo de dados, nos recursos coletivos, é uma lista ordenada de itens que correspondam aos parâmetros de busca utilizados.

XML

<xml>
    <dados>
        <partido_>
            ...
        </partido_>
        <partido_>
            ...
        </partido_>
        ...
    </dados>
    <links>
        <link>
            <rel>self</rel>
            <href>"..."</href>
        </link>
        <link>
            <rel>next</rel>
            <href>"..."</href>
        </link>
        <link>
            <rel>previous</rel>
            <href>"..."</href>
        </link>
        <link>
            <rel>first</rel>
            <href>"..."</href>
        </link>
        <link>
            <rel>last</rel>
            <href>"..."</href>
        </link>
    </links>
</xml>    
							    

JSON

{
    "dados": [
        {
            ...
        },
        {
            ...
        },
        ...
     ],
    "links": [                                            
        {
            "rel": "self",
            "href": "http://...",
        },
        {
            "rel": "previous",
            "href": "http://...",
        },
        {
            "rel": "next",
            "href": "http://...",
        },
        {
            "rel": "first",
            "href": "http://...",
        },
        {
            "rel": "last",
            "href": "http://...",
        }
    ]
}
							    

Cada elemento link tem dois campos: href traz uma URL e rel é um termo que descreve a relação que a URL tem com o documento ou recurso retornado. Esses termos usados com rel são de certa forma padronizados, e por serem divulgados no site da Internet Assigned Numbers Authority (IANA) são conhecidos popularmente como "relações IANA".

Nem sempre todos os links mostrados nos exemplos estão presentes, e não precisam aparecer no resultado nesta mesma ordem. O valor do campo rel indica os seguintes significados para as URLs:

  • self - É sempre a URL que foi usada para obter o próprio documento, com todos os parâmetros de busca utilizados. Sua finalidade principal é permitir que, posteriormente, o usuário possa repetir ou simplesmente identificar a busca.

  • next - Se estiver presente, indica a URL (com parâmetros) que deve ser usada para acessar a próxima página de resultados do conjunto requisitado.

  • previous - Se estiver presente, indica a URL (com parâmetros) que deve ser usada para acessar a página anterior de resultados da mesma busca.

  • first - Presente quando há mais de uma página de resultados, contém a URL e os parâmetros que devem ser usados para se obter a primeira página, isto é, o início da lista de resultados.

  • last - Quando presente, indica a URL e os parâmetros necessários para se obter a última página que contenham itens correspondentes à busca realizada.

O uso dos links, e principalmente de suas descrições semânticas dos campos rel, são recomendações fundamentais da arquitetura REST e da chamada "Web 3.0". É que está na essência de conceitos como linked data e HATEOAS (Hypermedia As The Engine Of Application State, ou "hipermídia como o motor de estado da aplicação").


Parâmetros de paginação

Na API do Dados Abertos, todos os recursos coletivos (isto é, que fornecem listagens) que têm suporte a paginação usam os mesmos parâmetros de query string para configurá-la:

  • itens - Quantidade de itens por página, sendo 1 o valor mínimo. Quando da publicação deste tutorial, o valor máximo é 100, e se o parâmetro estiver ausente é assumido o valor padrão de 15.

  • pagina - Número da página, isto é, do subconjunto de itens que se deseja obter como resposta à requisição. Quando ausente, é retornada a primeira página, isto é, os X primeiros itens, sendo X o valor passado pelo parâmetro itens.

Para se obter, por exemplo, uma lista de dados de 50 deputados de cada vez, a URL inicial seria .../deputados?itens=50. Na resposta dessa primeira requisição, haveria um link, identificado com o parâmetro rel com o valor next, com a URL já configurada como .../deputados?itens=50&pagina=2, para a obtenção dos dados dos 50 deputados seguintes.


Requisições assíncronas do JavaScript

O correto uso dos elementos link permite que você obtenha todos os itens de um recurso paginado, de qualquer tamanho total, usando um conceito simples: enquanto cada resposta tiver um link cujo campo rel tenha o valor next, é só fazer uma nova requisição usando a URL que esse link traz como valor do campo href.

É fácil imaginar isso como um loop, mas em linguagens como JavaScript as requisições para obter dados via rede são feitas de maneira assíncrona: logo que a requisição é feita, o fluxo do programa continua sem esperar a resposta, e é preciso definir o que vai acontecer quando enfim essa resposta chegar. Essa característica exige uma abordagem um pouco diferente daquela de loop WHILE que se poderia imaginar para o problema.

Neste tutorial, é criada uma simples página HTML, sem qualquer formatação visual, que monta uma lista com todos os deputados em exercício, usando a API do Dados Abertos, e exibe os nomes desses deputados em um menu tipo combobox. Ao escolher qualquer dos nomes, é exibida na página a foto do parlamentar.

A abordagem dessa aplicação para montar uma só lista com várias requisições (que não pretende ser apresentada como a melhor, é importante observar) é centrada na variável global listaDeps. É definida uma função a ser executada quando chegar a resposta, e a cada vez que é chamada essa função acrescenta os dados retornados à lista. No jargão técnico, esse tipo de função, que é invocada como reação de resposta a um evento, é chamada de callback.

Além de acrescentar itens a listaDeps, a função verifica a existência de um link cujo rel seja next na resposta à requisição. Isso é feito em um loop FOR que verifica o conteúdo do campo rel em todos os nós ou objetos link que são retornados como itens da lista links. Há também, comentada, uma versão que usa o método .forEach() da variável.

Assim que é encontrado um link de rel igual a next, a URL que está nesse link, em seu campo href, é usada como argumento para chamar a função principal que cria uma nova requisição, buscarListaDeps. A cada vez que é invocada, buscarListaDeps cria uma nova callback para a chegada da resposta — e todo o processo se repete até que não haja mais links next a obter.

Quem é quem dos deputados


Código


<doctype html>
<html lang="pt-br">
<head>
    <title>Quem é quem dos deputados</title>
    <meta charset="UTF-8"/>
    <script>
        "use strict";
        var listaDeps = new Array();

        /* buscarListaDeps
            Carrega 'listaDeps' com os dados obtidos do recurso paginado,
            em chamadas sucessivas
        */
        function buscarListaDeps (urlInicio) {
            var corpoResposta;
            var req = new XMLHttpRequest();
            var dados;

            req.open ("GET", urlInicio);
            req.onreadystatechange = function (evt) {
               if (req.readyState === req.DONE &&
                    req.status >= 200 && req.status < 300) {
                    // A requisição foi respondida com sucesso.
                    corpoResposta = JSON.parse(req.responseText);

                    listaDeps = listaDeps.concat(corpoResposta.dados);

                    // Se houver um link de rel="next" na resposta, chamar a função de busca
                    // outra vez usando esse link
                    // VERSÃO COM LOOP FOR
                    for (var i = 0; i < corpoResposta.links.length; i++) {

                        if (corpoResposta.links[i].rel === "next") {
                            buscarListaDeps(corpoResposta.links[i].href);
                            return;
                        }
                    }

                    /* VERSÃO USANDO FOREACH
                    corpoResposta.links.forEach (
                        function (val, idx, arr) {
                            if (val.rel === "next") {
                                buscarListaDeps (val.href);
                                return;
                            }
                        }
                    );
                    */

                    menuCarregarOpcoes();

                } // FIM DO "IF"
            } // FIM DE onreadystatechange
            req.setRequestHeader ("Accept", "application/json");
            req.send();
        }

        buscarListaDeps("https://dadosabertos.camara.leg.br/api/v2/deputados?itens=100");



        /* menuCarregarOpcoes
             Configura as opções de nomes de deputados no menu
         */
        function menuCarregarOpcoes() {
            var i=0;
            var menuwdg = document.getElementById("menudeps");
            var opt;

            // Criar o primeiro item sem o nome de um parlamentar...
            opt = document.createElement("option");
            opt.text = "Escolha um parlamentar..."
            menuwdg.add(opt);

            while (listaDeps[i]) {

                opt = document.createElement("option");
                opt.text = listaDeps[i].nome;
                menuwdg.add(opt);
                i++;
            }
        }

        /* menuOpcaoEscolhida
            Chamada quando o usuário escolhe outro nome no menu, executa
            as alterações na foto e no quadro de informações.
        */
        function menuOpcaoEscolhida() {
            var escolhido;
            var menuwdg = document.getElementById("menudeps");

            escolhido = menuwdg.value;
            for (var i = 0; i < listaDeps.length; i++) {
                if (listaDeps[i].nome === escolhido) {
                     mostrarDeputado (listaDeps[i]);
                }
            }
        }

        /*
        mostrarDeputado - recebe um item da lista de deputados,
          contendo os dados de um parlamentar, e os insere na
          exibição do HTML no navegador
        */
        function mostrarDeputado (dep) {
            var wdgFoto = document.getElementById("fotodep");
            var wdgNome = document.getElementById("nomedep");
            var wdgPartEst = document.getElementById("part-est");

            // A URL da foto é colocada como valor do atributo "src"
            //    do elemento <img> identificado com o id "fotodep"
            wdgFoto.setAttribute("src", dep.urlFoto);

            // O nome é inserido como conteúdo do elemento com id "nome"
            wdgNome.innerHTML = dep.nome;
            wdgPartEst.innerHTML = dep.siglaPartido + "-" + dep.siglaUf;
        }
    </script>
</head>
<body>
    <!-- Aqui é criado o controle de menu -->
    <select id="menudeps" onchange="menuOpcaoEscolhida()"></select><br/>

    <!-- A foto é exibida neste elemento "img"... -->
    <br/>
    <img id="fotodep" src="../../img/howtouse/avatar-anonimo.png" width="200"/>
    <br/>

    <!-- Nome e estado -->
    <h3 id="nomedep"></h3>
    <h4 id="part-est"></h4>
</body>
</html>
                            

Voltar à Central de Tutoriais