Herberth Amaral

Software development adventures

Archive for the ‘tutorial’ tag

Criando Web crawlers em Python – Parte II

with 9 comments

Ir para  Criando Web crawlers em Python – Parte I

Ir para Criando Web crawlers em Python – Parte III

No post anterior, eu mostrei como recuperar informações básicas de uma página da Web usando urllib, urllib2 e BeautifulSoup. Neste post eu mostrarei como enviar dados via GET e POST.

Há um excelente guia sobre urllib2: o urllib2 – The Missing Manual que foi de grande valia nos meus estudos. Nele, pode-se encontrar as informações sobre envio de informações do tipo GET e POST para o servidor. Isso será útil, pois é assim que a busca do SourceForge (e da maioria dos outros portais) funcionam.

Vamos dar uma olhada na URL do na pesquisa por “Python” no SF:

http://sourceforge.net/search/?type_of_search=soft&words=python

Bem, isso indica que temos que enviar duas variáveis para o SF.net: type_of_search (sempre igual à soft) e words (que é a nossa busca). Um exemplo de código que faria essa busca poderia ser esse:

import urllib,urllib2

base_url = 'http://sourceforge.net'
busca = raw_input('Pesquisar por: ')

variaveis_get = urllib.urlencode({'type_of_search':'soft','words':busca})
req = urllib2.Request(base_url+'/search/',variaveis_get)
response = urllib2.urlopen(req).read()
print response

Se der tudo certo, você  deveráver  o código fonte da página do SF que contém os resultados da pesquisa.

Feito isso, vamos analisar como a marcação do SF é organizada na busca:

O Firebug nos ajuda a não perder tanto tempo assim analisando código. De acordo com o que pudemos obter, podemos dividir as informações que queremos dessa forma:

  • O resultado da busca está dentro de uma tabela com o id=”searchtable”.
  • Um resultado da busca está sempre dentro de um td com classe description.
  • O nome do projeto está dentro de um link dentro de um h2 dentro desse td “description”.
  • O link de download se encontra na próxima td da mesma linha da tabela.

Exibindo as informações dos projetos

Agora nós temos como buscar nossa informação e como filtra-la. Como disse no post anterior, eu iria mostrar alguns usos mais avançados do BeautifulSoup, principalmente no que diz respeito à percorrer o documento. O próximo passo será mostrar as informações dos projetos que aparecem no resultado da nossa busca. Podemos fazer isso da seguinte forma:

from BeautifulSoup import BeautifulSoup
import urllib,urllib2

base_url = 'http://sourceforge.net'
busca = raw_input('Pesquisar por: ')

variaveis_get = urllib.urlencode({'type_of_search':'soft','words':busca})
req = urllib2.Request(base_url+'/search/',variaveis_get)
response = urllib2.urlopen(req).read()

soup = BeautifulSoup(response)
# procura pela tabela com id=searchtable
tabela = soup.find('table',{'id':'searchtable'})
#retorna uma lista com todas as linhas (<tr>) da tabela
linhas_tabela = tabela.findAll('tr')

i=0

for linha in linhas_tabela:
    i+=1
    #encontra a primeira coluna (descricao) da linha
    coluna_descricao = linha.find('td')

    #o atributo contents contem uma lista com o conteudo da tag
    nome_projeto = coluna_descricao.find('a').contents[0]

    descricao_projeto = coluna_descricao.contents[2].strip()
    print 'Projeto '+str(i)+': '+nome_projeto
    print descricao_projeto
    print '--------------------------------'

Legal, não?  Isso é uma demonstração básica do poder que o BeautifulSoup tem para percorrer e extrair informações em (X)HTML. O mais interessante de tudo é que quase todos os métodos da classe BeutifulSoup.Tag retornam a própria referência, o que quer dizer que podemos fazer chains (cadeias) de comando como essas:

nome_do_primeiro_projeto_da_pesquisa = soup.find('table',{'id':'searchtable'}).findAll('tr')[0].find('td').find('a').contents[0]

Isso é realmente muito útil quando se quer economizar código. E o mais interessante de tudo: é bem legível (mas não necessariamente fácil de interpretar… quem ler seu código assim terá que ter uma boa noção do BeautifulSoup).

Baixando os arquivos do projeto

Essa parte é relativamente fácil. O SF é um serviço muito bom, até para o nosso pequeno experimento :)

A URL que o link “Download Now” aponta sofre 3 redirects até o arquivo final. Tudo isso serve para o SF determinar a sua localização e apontar o mirror mais próximo de você. Então a única coisa que precisamos fazer é passar esse link para o wget (um programa bem útil para download de arquivos na Web presente na maioria das distribuições Linux. Um clone para Windows pode ser encontrado aqui) e ele irá baixar o projeto para nós. Simples, não?

Então vamos ao código:

from BeautifulSoup import BeautifulSoup
import urllib,urllib2
from subprocess import call

base_url = 'http://sourceforge.net'
busca = raw_input('Pesquisar por: ')

variaveis_get = urllib.urlencode({'type_of_search':'soft','words':busca})
req = urllib2.Request(base_url+'/search/',variaveis_get)
response = urllib2.urlopen(req).read()

soup = BeautifulSoup(response)

# procura pela tabela com id=searchtable
tabela = soup.find('table',{'id':'searchtable'})

 #retorna uma lista com todas as linhas da tabela
linhas_tabela = tabela.findAll('tr')

i=0
links_download = []

for linha in linhas_tabela:
    i+=1
    coluna_descricao = linha.find('td')
    nome_projeto = coluna_descricao.find('a').contents[0]
    link_download = linha.find('a',{'class':'downloadnow'})['href']
    links_download.append(link_download)
    descricao_projeto = coluna_descricao.contents[2].strip()
    print 'Projeto '+str(i)+': '+nome_projeto
    print descricao_projeto
    print '--------------------------------'

opcao = int(raw_input('Qual projeto gostaria de baixar? '))
opcao_url = base_url+links_download[opcao-1]
call(['wget',opcao_url]) #chama o wget com o link para download

A única coisa que eu fiz de novo foi adicionar o link de download em uma lista e perguntar ao usuário qual projeto ele deseja baixar. Então, eu passei o link de download para o wget que fez o serviço de baixar o arquivo pra mim. Pronto! Nosso crawler de exemplo do SF.net está pronto :)

No proximo post eu mostrarei como guardar valores de sessão para percorrermos páginas protegidas por senha. See ya!

Written by Herberth Amaral

March 7th, 2010 at 11:08 am

Posted in Python,crawler

Tagged with , , ,

Criando screencasts no Linux

with 2 comments

Eu vi algumas pessoas reclamando sobre a dificuldade de criar screencasts usando Linux. É, eu também tive algumas dificuldades. Nenhuma das ferramentas que eu consegui encontrar com minhas buscas não resolviam meu problema:

  1. Istanbul – Bonitinho, parece leve, grava legal, mas simplesmente dá um erro de IO (não entendi direito qual era o erro ) e não salva.
  2. xvidcap - Levíssimo, completo, mas tem problema com o PulseAudio e não grava o áudio do microfone. Tentei inicializa-lo com o comando padsp xvidcap, mas também não resolveu: mesmo chiado no som.
  3. recordMyDesktop - Grava áudio normal, mas as imagens tinham uma qualidade horrível. Parecia que ele atualizava uma parte da tela e esquecia da outra. Resultado: eu gravava um teste no VIM na linha de comando (preta) e quando eu passava pro GMail (verde) a tela ainda continuava preta…

Não sei o por quê, mas nem todos os programas que vem no Linux vêm com as configurações mais comuns já de cara. O problema é que eu não percebi que o recordMyDesktop encaixaria perfeitamente pra mim se ele já viesse com uma opção já marcada desde o início:

screenshot das configurações do recordMyDesktopEstranho, não? Eu pensava que marcar “Full shots at every frame” resolveria meu problema, mas, inexplicavelmente, o “Encode On the Fly” resolveu. Apesar da tooltip dizer que essa opção consome mais poder de processamento, eu não notei tanto. A qualidade do vídeo ficou impecável e eu estou com os efeitos no máximo :)

Se após gravado o screencast você desejar edita-lo ou converte-lo, há uma gama de softwares para Linux mesmo que fazem isso. Para conversão eu uso muito o ffmpeg (não conheço nenhum outro melhor e mais completo) e me recomendaram o Avidemux para edição de vídeo. A única coisa que eu sinto falta agora é algo que coloque os caracteres digitados na tela e que destaque o clique do mouse.

See ya!

Written by Herberth Amaral

February 21st, 2010 at 12:14 am

Posted in Linux

Tagged with , , ,

Testes unitarios no Flex usando o FlexUnit 4

without comments

O FlexUnit 4 é a mais nova versão (não tem a oficial, só a RC, por enquanto) e apresenta uma série de vantagens sobre o seu antecessor, o FlexUnit 0.9, como os metadados de teste ([Test], [After] e [Before], para citar as mais populares), Theories, DataPoints e Assumptions que são úteis para testar grandes quantidades (talvez até infinita) de dados e ver como a aplicação se comporta e a possibilidade de executar os testes com diferentes Runners.

Este tutorial tem como objetivo mostrar o básico de testes unitários no Flex, sem se aprofundar muito nos recursos avançados do framework de testes.  Eu pretendo ir postando mais tutoriais à medida que eu for me aprofundando na tecnologia.

O setup

Para usar o FlexUnit4, você precisa baixa-lo aqui. Após isso, crie um projeto no Flex Builder e adicione todas as libs que vieram no pacote no seu diretório libs:

Verifique se todas as libs estão lá

Como o bom e velho TDD manda, vamos primeiro escrever a classe de teste de exemplo antes de escrever nosso código de produção.

A classe de teste

Uma classe de testes é uma classe comum que usa a classe Assert para fazer asserções. No exemplo que irei mostrar, usarei dois tipos básicos de asserção, mas se você observar, o FlexUnit possui vários tipos diferentes de asserções:

package tests
{
	import org.flexunit.Assert;
	import org.flexunit.runner.manipulation.filters.IncludeAllFilter;

	import production.BasicClass;

	public class BasicTests
	{
		public function BasicTests(){}
		private var basicClass:BasicClass;

		[Before]
		public function before():void
		{
			basicClass = new BasicClass();
		}

		[Test]
		public function Verifica_Se_As_Duas_Strings_Sao_Iguais():void
		{
			var str:String = "MinhaString";
			Assert.assertTrue(basicClass.areStringsEqual(str,"MinhaString"));
		}

		[Test]
		public function Verifica_Se_A_Soma_Retorna_Resultado_Correto():void
		{
			var soma:int = 10;
			Assert.assertEquals(soma,basicClass.somar(2,8));
		}

		[After]
		public function after():void
		{
			//codigo de after
		}

	}
}

A suíte de teste

A suíte de testes inclui nosso caso de teste descrito acima e será útil para o Flex executar nossos testes. Sendo assim, nossa suíte de testes ficaria mais ou menos desse jeito:

package tests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class MyTestSuite
	{
		public var baseTest:BasicTests;
		public function MyTestSuite(){}

	}
}

UITestRunner e o FlexUnitCore

O UITestRunner é um componente do FlexUnit que mostra os testes numa interface gráfica. Ele ficará na nossa aplicação e mostrará os resultados dos testes.

O FlexUnitCore será o responsável por carregar as suítes de teste e por passar os dados de saída de testes para o UITestRunner. No nosso caso, nossa aplicação principal ficaria assim:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="run()" layout="absolute" xmlns:flexUnitUIRunner="http://www.adobe.com/2009/flexUnitUIRunner">
    <mx:Script>
    <![CDATA[
       import tests.MyTestSuite;
       import org.flexunit.listeners.UIListener;
       import org.flexunit.runner.FlexUnitCore;

       public var core:FlexUnitCore;
       public function run():void
       {
           core = new FlexUnitCore();
           core.addListener(new UIListener(uiRunner));
           core.run(MyTestSuite);
      }
    ]]>
   </mx:Script>
   <flexUnitUIRunner:TestRunnerBase id="uiRunner"  width="100%" height="100%"/>
</mx:Application>

O código de produção

Ufa! Depois de escrever a classe de teste, a suíte de teste e o runner, podemos nos focar em fazer nosso código de produção :) . Dêem uma olhada em como ficaria o dito:

package production
{
	public class BasicClass
	{
		public function BasicClass(){}

		public function areStringsEqual(string1:String,string2:String):Boolean
		{
			return (string1==string2);
		}

		public function somar(valor1:int,valor2:int):int
		{
			return valor1+valor2;
		}

	}
}

E Voilà!

Depois de tudo pronto, a cara da criança ficaria mais ou menos assim:

Legal, não? E ainda dá pra fazer com que o FlexUnit4 exporte o resultado dos testes para um arquivo XML, permitindo que seus testes no Flex sejam importados pelo seu sistema de Integração Contínua, mas isso é assunto para outro post :)

Você pode baixar o código fonte aqui e ver os exemplos rodando online aqui.

Good testing!

Written by Herberth Amaral

January 28th, 2010 at 12:12 pm