Skip to content

Tag: JSON

Extração de Dados e Fundos de Investimento do Banco do Brasil

Eu não achei onde coletar os dados diários de rentabilidade dos fundos de investimento do Banco do Brasil em formato bem estruturado.

Num mundo ideal as coisas seriam assim, você faria uma requisição numa url como esta:

http://bb.com.br/apps/rentabilidade?fundo=Siderurgia&saida=xml

E ele cuspiria um XML com as informações da rentabilidade diária desse fundo, isso se eu não especificasse através de outro parâmetro qual a data ou intervalo de datas desejado ou outro tipo de dados para saída como YAML ou JSON. Mas por enquanto não temos isso, nem unicórnios, então temos de fazer as coisas do jeito mais difícil, que é puxando os dados feitos para humanos e escrevendo um programa pra extrair à força os dados que desejamos e quem sabe usar eles para algum uso relacionado a mineração de dados.

A primeira abordagem que eu tentei foi a de criar um desses pequenos parsers XML que eu já mostrei como fazer antes, mas o código fonte desse documento se mostrou muito incompatível com o XML que o parser estava disposto a trabalhar. A solução alternativa foi tratar o documento linha a linha.

import urllib

# abrimos o documento referenciado pela url
url = 'http://www21.bb.com.br/portalbb/rentabilidade/index.jsp?tipo=01'
documento = urllib.urlopen(url)

# fundo de investimento que me interessa
fundo = 'small caps'

# estados
INICIO = 0
ACHOU_FUNDO = 1
FIM = 2

# estado inicial
estado = INICIO

# vamos analisar linha a linha do fluxo do documento
for linha in documento:
	# simplificamos, tudo pra minusculas
	linha = linha.lower()

	# no inicio, procura uma linha que tenha o fundo
	if estado == INICIO and linha.find(fundo) != -1:
		estado = ACHOU_FUNDO

	# depois, procuramos o proximo inicio de tabela html.
	# dessa linha, pegamos o que vem depois do primeiro >
	# e entao o que vem antes do primeiro <
	# e trocamos a virgula por ponto.
	elif estado == ACHOU_FUNDO and linha.find('>')[1].split('<')[0].replace(',','.')
		estado = FIM

E para usar:

$ python rendimento_small_caps.py
0.881

Geralmente estamos mais interessados em saber o valor da cota daquele fundo, daí podemos calcular o rendimento total sabendo a cota que compramos a ação inicialmente. Nesse caso o dado está na 11º coluna.

import urllib
 
# abrimos o documento referenciado pela url
url = 'http://www21.bb.com.br/portalbb/rentabilidade/index.jsp?tipo=01'
documento = urllib.urlopen(url)
 
# fundo de investimento que me interessa
fundo = 'small caps'
 
# estados
INICIO = 0
ACHOU_FUNDO = 1
FIM = 2
 
# estado inicial
estado = INICIO
coluna = 0
 
# vamos analisar linha a linha do fluxo do documento
for linha in documento:
	# simplificamos, tudo pra minusculas
	linha = linha.lower()
 
	# no inicio, procura uma linha que tenha o fundo
	if estado == INICIO and linha.find(fundo) != -1:
		estado = ACHOU_FUNDO
 
	# para cada coluna, conta a coluna, mas nao faz nada
	elif estado == ACHOU_FUNDO and linha.find('<'):
		coluna += 1
 
	# quando chegar na coluna onze, retira o conteudo entre os sinais > e <
	# e troca virgula por ponto, transforma em float e joga na tela
	if estado==ACHOU_FUNDO and coluna == 11:
		print float(linha.split('>')[1].split('<')[0].replace(',','.'))
		estado = FIM

$ python cota_small_caps.py
6.156906634

Essa é uma abordagem que eu não gosto nem recomendo porque ela é muito frágil e está extremamente acoplada a formatação de dados para humanos. Esta formatação está interessada no saída gráfica que o usuário vai obter e não em facilitar a extração (não humana) desses dados. Isso torna a solução muito frágil:

  • Se mudarem os nomes internos dos elementos, a solução pode falhar.
  • Se mudarem a formatação da tabela, a solução pode falhar.
  • Se mudarem a disposição interna dos elementos html, a solução pode falhar.
  • Se mudarem a url do documento, a solução vai falhar.
  • Se o documento não puder mais ser tratado linha a linha, a solução vai falhar feio.

É provável que quando você estiver lendo isso ela nem funcione mais do jeito que está descrita aqui.

Por outro lado, a solução funciona e nesse caso é o que me interessa. Quando ela quebrar, se ainda for do meu interesse eu posso rapidamente conserta-la e os dados já coletados no passado continuam válidos.

Isso somado  a uma programa como o Cron pode se tornar uma ferramenta realmente poderosa.

JavaFX, Retrieving non XML/JSON data from clouds

tango weather overcast

Usuually on JavaFX we grab data using HttpRequest from external resources on formats like JSON or XML. I showed how to get it on the post Reading Twitter with JavaFX and how to parse it using PullParser on the post Parsing a XML sandwich with JavaFX.

Another day I need to grab and interpret some plain results, not in XML nor JSON, while consuming a REST service. In this case we don’t have a well structure data so the PullParser won’t help us.

Example 1: Reading Raw Data

In this example we’ll load a plain text file served in a remote location.

var planetsRequest = HttpRequest {
    location: "http://silveiraneto.net/downloads/planets";
    onInput: function(stream: InputStream) {
        var buff = new BufferedReader(new InputStreamReader(stream));
        var line = "";
        while((line = buff.readLine())!=null){
            println(line);
        }
    }
}
planetsRequest.enqueue();

This will produce the output:

Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune

Example 2: Discovering your IP Address

In this example we’ll examine how to integrate a request of a remote data in a running graphical program.

The best way to know your real IP address is asking for a remote server to look which IP made that request. It’s like calling for a friend and asking him which number appeared in his mobile. =) This server side Python script prints the IP address of who requested the page.

#!/usr/bin/env python
import os

print "Content-type: text/html"
print
print os.environ['REMOTE_ADDR']

In the client side, with JavaFX, we’ll load the remote value into a local variable. The ip is assigned with the value “…” and later the ipRequest will replace it with a String with the IP. The bind feature will automatically fix the GUI String text.

For the user he will see the ellipsis for a few seconds and so their IP.

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.io.http.HttpRequest;
import java.io.*;

var ip = "...";

Stage {
    title: "What is my IP?" width: 250 height: 80
    scene: Scene {
        content: Text {
            x: 10, y: 30
            content: bind "My IP is {ip}"
        }
    }
}

var ipRequest = HttpRequest {
    location: "http://silveiraneto.net/scripts/myip.py";
    onInput: function(stream: InputStream) {
        var buff = new BufferedReader(new InputStreamReader(stream));
        ip = buff.readLine();
    }
}
ipRequest.enqueue();

You can try this JavaFX applet here.

Example 3: Reading Integer values

Until now we handled just plain Strings. But in some cases you want to get number as non structured data. In this case you need to know previously which type the data is. In the case of a web service this probably will be described in a WSDL file.

Here I’m writing a very simple service script at Zembly, a great platform for cloud computing. It’s called aplusb, it justs add the first parameter A to the second B.

if ((Parameters.a != null) && (Parameters.b!= 0)) {
return Parameters.a+Parameters.b;
}

The service is published at Zembly here where you can see more details on how to invoke it.

A simple way to invoke it on JavaFX and than getting the value as an Integer:

import java.io.*;
import javafx.io.http.HttpRequest;

var a = 100;
var b = 200;
var result = 0 on replace {
    println(result);
}

var zemblyRequest = HttpRequest {
    location: "http://zembly.net/things/1827f696529d4e6f940c36e8e79bea1c;exec?a={a}&b={b}";
    onInput: function(stream: InputStream) {
        var buff = new BufferedReader(new InputStreamReader(stream));
        result = Integer.valueOf(buff.readLine());
    }
}
zemblyRequest.enqueue();

The output will be:

0
300

The first 0 is from the first assignment on the var result. The 300 is from the webservice itself.

The same approach can be used to convert the ASCII/Unicode result from the stream to the suitable type on a variable.