listas de deputados e seus partidos

Usando dicts para organizar listas de deputados e seus partidos

Para este laboratório vamos precisar alguns conceitos novos:
  • Download de arquivos da internet
  • Ler arquivos xlsx (Excel)
Estes tópicos serão desenvolvidos em laboratórios futuros com maior profundidade. Aqui vamos só ver o básico indispensável para nosso laboratório de hoje.
Como sempre procure a documentação detalhada dos módulos usados na internet (Google, etc)

Download de arquivos

Páginas de sites na web são transferidas e salvas em arquivos texto, mas num formato conhecido como HTML. Ao abrir uma página no browser este envia ao site um pedido de transferência de um arquivo html correspondente. Este arquivo contém toda informação necessária para o browser exibir a imagem da página na forma desejada.
Em nosso caso vamos usar um script em Python para efetuar o download de arquivos em vez de usar um browser. O download de arquivos html é semelhante à leitura de arquivos texto que já vimos. A complexidade da interação com a rêde e sites remotos fica escondida num módulo chamado requests.
Para baixar um dado arquivo html vamos precisamos de seu "caminho", como no caso de arquivos texto. Aqui o caminho é uma url, um mapa único de como navegar a rêde para chegar ao arquivo desejado.
Vamos exemplificar baixando um arquivo do site da Câmara de Deputados do Brasil. A url deste site é http://www2.camara.leg.br/ e quando digitamos esta url no Firefox ou Chrome aparece uma página de boas vindas da CD.

Para nós nada vai aparecer, pois vamos somente baixar o arquivo html e depois salvá-lo no computador. Note que os caminhos de arquivos aqui mostrados são especificos para o meu computador. No seu os caminhos serão diferentes.
Primeiro vamos importar o módulo requests e alguns módulos úteis adicionais.
In [1]:
import os, requests

os.chdir(r'C:\Users\ps\Desktop\Blog')

# define a url da CD
url = 'http://www2.camara.leg.br/' 
 
Baixar o arquivo fica "trivial" porque o módulo requests faz todo o trabalho para nós. Entretanto os mais diversos problemas podem ocorrer (site fora do ar, arquivo foi movido dentro do site, não temos permissão de acesso, acesso à internet caiu, etc) e o download pode falhar. O módulo requests joga uma Exception (exceção) para avisar o usuário que tipo de problema houve. Cabe a nós capturarmos alguma Exception eventual e fazer algo a respeito. Fazemos isto do modo já visto, usando os comandos try e except. Vamos lá.
O comando get(uma_url) do módulo request é quem faz quase todo trabalho de download. Uma vez executado é necessário testar o status do objeto criado por request.get(uma_url) para verificar se ocorreram problemas durante a conexão com o site remoto. Tipicamente se usa o comando raise_for_status() do objeto criado, o qual joga qualquer exceção associada ao objeto.
In [2]:
res = requests.get(url)
try:
    res.raise_for_status()
except Exception as ex:
    msg = 'There was a web problem: %s' % (ex)
    print(msg)

type(res)             # verifica o tipo de objeto criado pelo get()
Out[2]:
requests.models.Response
 
O objeto res que obtivemos ainda não é o arquivo desejado mas somente uma espécie de acesso ao arquivo. Para obter o corpo do arquivo precisamos usar res transferindo os dados que ele disponibiliza. Estes dados vem em blocos, em formato binário. Salvamos os blocos escrevendo-os num arquivo binário.
In [3]:
src = os.getcwd()
path = os.path.join(src,'camara.htm')
htm = open(path, 'wb')
for chunk in res.iter_content(100000):
    htm.write(chunk)
htm.close()
print(path)
C:\Users\ps\Desktop\Blog\camara.htm
 
Verifique que um arquivo "camara.htm" aparece no caminho path em seu computador. Depois clique no arquivo, o que deve abrir seu browser mostrando o conteúdo do arquivo (a primeira página do site da Câmara dos Deputados). Para verificar que o arquivo aberto realmente é local no seu computador, observe que o caminho mostrado no browser deve ser algo como file:///C:/Users/ps/Desktop/Blog/camara.htm Note que começa com file://... em vez de http://...

Lendo arquivos xlsx do Excel

O módulo que permite ler planilhas Excel chama-se xlrd. Vamos importá-lo.
In [4]:
import xlrd 
 
Neste módulo o comando xlrd.open_workbook(uma_path) devolve um objeto que representa o workbook que está no caminho uma_path. Ou seja, a planilha.
In [5]:
path = r'C:\Users\ps\Desktop\Blog\aula6\Book1.xlsx'
wb = xlrd.open_workbook(path) 
 
O objeto wb recem criado representa a planilha book1.xlsx que usaremos como exemplo neste exercicio.
O objeto wb (workbook) por sua vez oferece uma série de comandos. Em particular podemos obter os nomes de sheets (folhas) da planilha e com estes nomes podemos criar objetos que representem sheets especificos.
In [6]:
sheets = wb.sheet_names()
sname = sheets[0]
sname
Out[6]:
'Sheet1'
In [7]:
sheet = wb.sheet_by_name(sheets[0])
type(sheet)
Out[7]:
xlrd.sheet.Sheet
 
Com o objeto sheet podemos invocar um monte de comandos que permitem ler o conteudo de celulas e outras coisas uteis.
In [8]:
sheet.nrows        # o numero de linhas desta planilha
Out[8]:
10
In [9]:
sheet.ncols        # e o numero de colunas
Out[9]:
4
 
Células da planilha podem ser acessadas com o comando sheet.cell(rw,cl) onde rw e cl são o número (começando de 0) de uma linha e de uma coluna respectivamente. Já o valor contido numa célula é lido com o comando sheet.cell(rw,cl).value
In [10]:
titulo = sheet.cell(0,0).value
titulo
Out[10]:
'Nome Parlamentar'
In [11]:
nome = sheet.cell(1,0).value
nome
Out[11]:
'ABEL MESQUITA JR.'
In [12]:
partido = sheet.cell(1,1).value
partido
Out[12]:
'PMB'
 
Vejamos como iterar sobre todas as linhas desta planilha, imprimindo o que encontramos.
In [13]:
for rw in range(sheet.nrows):
    
    # pega o nome politico
    cl = 0
    nome = sheet.cell(rw,cl).value
    if nome == None:
        break
    
    # pega o partido
    cl = 1
    partido = sheet.cell(rw,cl).value
    
    # pega o status
    cl = 3
    status = sheet.cell(rw,cl).value
    
    #imprime a linha
    print(nome, partido,status)
Nome Parlamentar Partido Titular/Suplente/Efetivado
ABEL MESQUITA JR. PMB T
ADALBERTO CAVALCANTI PMB T
ADELMO CARNEIRO LEÃO PT S
ADELSON BARRETO PTB T
ADEMIR CAMILO PROS S
ADILTON SACHETTI PSB T
AELTON FREITAS PR T
AFONSO FLORENCE PT T
AFONSO HAMM PP T
 
Bem com isto vimos os pre-requisitos para nosso laboratório de hoje.

Aula 6 - laboratório

Nosso objetivo neste exercício será baixar duas planilhas de um site da internet, comparar seus conteúdos e imprimir as diferenças. Este exercício ilustra os conceitos usados no script dep.py que usamos no NECI para monitorar continuamente (quase) as mudanças de partidos e de status dos deputados da CD.
As urls das planilhas são respectivamente:
O conteúdo de book1.xlsx é mostrado na figura abaixo:


Você já sabe como baixar os respectivos arquivos e como abrir depois os workbooks Excel para ler os seus conteúdos. O que falta agora é bolar o algoritmo para comparar estes conteúdos, identificar o que for diferente e imprimir o resultado.
Primeiro vamos baixar os dois books em nosso diretório de trabalho. Para baixar tudo vamos ter que escrever duas vezes trechos de códigos quase identicos. Além de ser uma tarefa entediante existe a chance de cometer erros.

Por esta razão vamos colocar numa função o código de download e salvamento em arquivo. Depois é só chamar duas vezes.
A função baixa(url, nomearq) recebe a url completa donde baixar o arquivo e um nome desejado para o arquivo salvo no diretório corrente.
In [14]:
'''url é a url do arquivo remoto a ser baixado, arq é o nome desejado
para o arquivo local'''
def baixa(url, arq):
    # cria conexão com o arquivo remoro
    res = requests.get(url)
    try:
        res.raise_for_status()
    except Exception as ex:
        msg = 'There was a web problem: %s' % (ex)
        print(msg)
        return
    # agora copia o conteudo do arquivo remoto
    src = os.getcwd()
    path = os.path.join(src, arq)
    xl = open(path, 'wb')
    for chunk in res.iter_content(100000):
        xl.write(chunk)
    xl.close()
    print('Baixou ' + arq) 
 
Agora é só definir as urls, os nomes dos arquivos locais e chamar nossa função baixa()
In [15]:
url1 = r'https://dl.dropboxusercontent.com/u/50004393/Book1.xlsx'
url2 = r'https://dl.dropboxusercontent.com/u/50004393/Book2.xlsx'

arq1 = 'dep1.xlsx'
arq2 = 'dep2.xlsx'

# baixa a primeira planilha
baixa(url1, arq1)

# baixa a segunda planilha
baixa(url2,arq2)
Baixou dep1.xlsx
Baixou dep2.xlsx
 
Se tudo foi bem e os arquivos Excel estão salvos no diretório (observe que em seu computador os caminhos podem ser diferentes!), então agora falta fazer a comparação dos conteúdos.
Isto pode ser feito de diversas formas e vamos ilustrar aqui a maneira que usamos no script do NECI.
A idéia é usar dicionários dict para armazenar o conteúdo de uma planilha destas. A chave de cada item do dicionário é o nome do deputado, que supomos ser imutável. O valor de cada item é uma tupla contendo seu partido e seu status naquela planilha.
Vejamos como fica a primeira planilha.
In [18]:
src = os.getcwd()
path1 = os.path.join(src, arq1)
wb = xlrd.open_workbook(path1) 
sheets = wb.sheet_names()
sheet = wb.sheet_by_name(sheets[0])
sheet.nrows        # se tudo foi bem deve imprimir o numero de linhas
Out[18]:
10
 
Agora implementamos nossa ideia. Primeiro criamos um dicionario vazio, depois percorremos as linhas da planilha sheet criando cada item do dicionário e inserindo-o.
In [19]:
# cria dicionario com colunas nome, partido e status
dic1 = {}
for rw in range(1, sheet.nrows):
    
    # pega o nome politico
    cl = 0
    nome = sheet.cell(rw,cl).value
    if nome == None:
        break
    
    # pega o partido
    cl = 1
    partido = sheet.cell(rw,cl).value
    
    # pega o status
    cl = 3
    status = sheet.cell(rw,cl).value

    item = [partido, status]
    dic1[nome] = item
In [20]:
import pprint
pprint.pprint(dic1)
{'ABEL MESQUITA JR.': ['PMB', 'T'],
 'ADALBERTO CAVALCANTI': ['PMB', 'T'],
 'ADELMO CARNEIRO LEÃO': ['PT', 'S'],
 'ADELSON BARRETO': ['PTB', 'T'],
 'ADEMIR CAMILO': ['PROS', 'S'],
 'ADILTON SACHETTI': ['PSB', 'T'],
 'AELTON FREITAS': ['PR', 'T'],
 'AFONSO FLORENCE': ['PT', 'T'],
 'AFONSO HAMM': ['PP', 'T']}
 
Muito bem até aqui. Agora vamos repetir tudo isto para a segunda planilha, obtendo outro dicionário dic2
In [22]:
path2 = os.path.join(src, arq2)
wb = xlrd.open_workbook(path2) 
sheets = wb.sheet_names()
sheet = wb.sheet_by_name(sheets[0])

# cria dicionario com colunas nome, partido e status
dic2 = {}
for rw in range(1, sheet.nrows):
    
    # pega o nome politico
    cl = 0
    nome = sheet.cell(rw,cl).value
    if nome == None:
        break
    
    # pega o partido
    cl = 1
    partido = sheet.cell(rw,cl).value
    
    # pega o status
    cl = 3
    status = sheet.cell(rw,cl).value

    item = [partido, status]
    dic2[nome] = item
    
pprint.pprint(dic2)
{'ABEL MESQUITA JR.': ['DEM', 'T'],
 'ADAIL CARNEIRO': ['PP', 'T'],
 'ADALBERTO CAVALCANTI': ['PTB', 'T'],
 'ADELMO CARNEIRO LEÃO': ['PT', 'S'],
 'ADELSON BARRETO': ['PR', 'T'],
 'ADEMIR CAMILO': ['PTN', 'S'],
 'ADILTON SACHETTI': ['PSB', 'T'],
 'AELTON FREITAS': ['PR', 'T'],
 'AFONSO FLORENCE': ['PT', 'T'],
 'AFONSO HAMM': ['PP', 'T']}
 
É evidente que existem diferenças. Por exemplo, apareceu um tal Adail Carneiro, que não aparecia na planilha anterior. Alem disto houve diversas trocas de partidos, por exemplo os primeiros dois eram de PMB e foram para o DEM e para o PP respectivamente.
Vejamos como detetar as diferenças usando estes dicionários.
Vamos detetar dois tipos de diferenças.
  • nomes que aparecem num dicionário e não aparecem no outro
  • partidos diferentes nos dicionários para o mesmo nome
Estes testes ficam melhor se implementados em funções que podem ser chamadas no corpo principal do programa.
A primeira função compara(a,b) compara dois dicionários de nosso tipo e devolve duas listas (que podem estar vazias). A primeira lista são nomes que estão em a mas não estão em b, e a segunda lista é o reverso.
In [23]:
def compara(a, b):
    '''a and b are dictionaries. Returns lists with keys in a that
     are not in b and keys in b that are not in a'''
    dif1 = []
    for n in a.keys():
        if n not in b.keys():
            dif1.append(n)

    dif2 = []
    for n in b.keys():
        if n not in a.keys():
            dif2.append(n)
    return (dif1, dif2) 
 
A segunda função difpartidos(a,b) returna um dicionário onde a chave é o nome de um deputado e o valor é uma lista com o partido dele em a seguido do partido dele em b.
In [24]:
def difpartidos(a,b):
    '''a and b are dictionaries. Returns dictionary where keys are
    names that are in both a and b and value is a list with their
    partido in a and in b'''
    dif = {}
    for n in a.keys():
        if n in b.keys():
            t1 = a[n]
            t2 = b[n]
            if t1[0] != t2[0]:
                t = [t1[0], t2[0]]
                dif[n] = t

    return dif 
 
Tendo estas funções fica fácil armar o corpo do algoritmo. Primeiro testamos logo se os dicionários tem qualquer diferença, o que pode ser feito com um mero teste de igualdade de objetos dic1 == dic2?
Caso os dicionários estejam diferentes usamos a função compara() para ver se apareceram ou sumiram nomes nas planilhas.
Continuando usamos a função difpartidos() para verificar eventuais mudanças de partidos.
In [25]:
# compara os dois dicionários diretamente
if dic1 == dic2:
        print('Nenhuma mudança detetada')
else:
    # deteta nomes novos e faltantes
    dif1, dif2 = compara(dic1, dic2)
    if dif1:
        print('Nomes que faltam em dic2')
        pprint.pprint(dif1)
        print()
    if dif2:
        print('Nomes que faltam em dic1')
        pprint.pprint(dif2) 
        print()
        
    # deteta mudanças de partidos
    difp = difpartidos(dic1, dic2)
    if difp:
        print('Mudanças de partidos')
        pprint.pprint(difp)
Nomes que faltam em dic1
['ADAIL CARNEIRO']

Mudanças de partidos
{'ABEL MESQUITA JR.': ['PMB', 'DEM'],
 'ADALBERTO CAVALCANTI': ['PMB', 'PTB'],
 'ADELSON BARRETO': ['PTB', 'PR'],
 'ADEMIR CAMILO': ['PROS', 'PTN']}
 
E isto encerra nosso laboratório de hoje, que visou ilustrar um dos inúmeros usos de dicionários num exemplo (proximo a) real.

No comments:

Post a Comment