quarta-feira, 14 de maio de 2014

Web Crawler em Python usando beautifulsoup4

Quem nunca precisou baixar uma serie de arquivos de um determinado site mas não teve paciência para ficar clickando em cada links de download? Recentemente tive que fazer isso e utilizei o urllib2 para executar minha requisições e o beautifulsoup4 para navegar no HTML. Vamos ver alguns exemplos básicos.

user_agent = "Mozilla/5.0 (X11; U; Linux x86_64; en-US) 
AppleWebKit/534.7 (KHTML, like Gecko) Chrome /7.0.517.41 
Safari/534.7"
opener = urllib2.build_opener() 
opener.addheaders = [('User-agent', user_agent)] 

html = opener.open("http://www.caelum.com.br").read()

Precisamos criar um objeto opener, este será responsável por executar nossas requisições através do método open, em seguida, chamamos o método read para ler a resposta da nossa requisição.

Também defini no cabeçalho da requisição (addHeaders) o attributo "User-agent".

Esse é o primeiro passo, obter o html, agora vem a mágica

soup = BeautifulSoup(html)

A classe BeautifulSoup é quem vai nos ajudar a navegar no html de maneira mais OO.
Digamos que precisamos encontrar todas as tags divs:

divs = soup.findAll('div')

Podemos inclusive definir algumas condições de pesquisa.
Vamos buscar todas as tags divs com o atributo class igual a "grupo"

divs = [div for link in soup.findAll('div')
    if div.get("class") == "grupo "]

Pronto, acabamos de definir um filtro de pesquisa.
E para fazermos o download de um determinado arquivo?

Digamos que a página em questão possui links das suas músicas preferidas, podemos criar um filtro que pesquise todos os link e em seguida execute cada um desses link para fazer o download do mp3.

link = [link for link in soup.findAll('a')
    if link.get("href").endswith(".mp3")]

Pronto, filtramos os links que terminam com a extensão mp3, agora vamos fazer o download desses arquivos:

for link in link:
    nome_arquivo = link[link.rfind("/"):]
    urllib.urlretrieve(nome_arquivo,"/tmp/"+nome_arquivo)

Com o método urlretrieve da biblioteca urlib baixamos e gravamos o arquivo, passando como primeiro parâmetro o nome do arquivo e o segundo onde ele será salvo.

Este foi um exemplo bem básico e trivial de como usar o BeautifulSoup, nos próximos post sobre o assunto, irei detalhar mais sobre algumas formas mais complexas de navegação, filtros e pesquisa.

O código completo do exemplo pode ser encontrado aqui.

quarta-feira, 7 de maio de 2014

Jeito certo de comparar Integers em Java

class A {
 public static void main(String[] args){
     Integer a1 = 125;
     Integer b1 = 125;
     System.out.println(a1==b1); //true
 }
}

class B {
 public static void main(String[] args){
  Integer a2 = 280;
  Integer b2 = 280;
  System.out.println(a2==b2);//false
 }
}

class C {
 public static void main(String[] args){
  Integer a3 = 280;
  System.out.println(a3==280);//true
 }
}


Quem já estudou para certificação está acostumado com esse tipo de pegadinha. Para entender esse comportamento vamos analisar o bytecode da classe A e B:

class A extends java.lang.Object{
A();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: bipush 125
   2: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   5: astore_1
   6: bipush 125
   8: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   11: astore_2
   12: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   15: aload_1
   16: aload_2
   17: if_acmpne 24
   20: iconst_1
   21: goto 25
   24: iconst_0
   25: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
   28: return

}

Podemos perceber o autoboxing acontecendo com o Integer.valueOf para as duas variaveis "a" e "b".
Se olharmos o código do método valueOf veremos que se os primitivos estiverem dentro de uma faixa (low e high) o objeto será buscado de um cache. Por padrão os valores do low e high são -127 e 128 respectivamente e podemos alterar o valor do high usando o parametro -Djava.lang.Integer.IntegerCache.high.

public static Integer valueOf(int i) {
 assert IntegerCache.high >= 127;
 if (i >= IntegerCache.low && i <= IntegerCache.high)
  return IntegerCache.cache[i + (-IntegerCache.low)];
 return new Integer(i);
}

Se o número estiver dentro da faixa, o objeto será sempre o mesmo, logo, a comparação com "==" será sempre true. Agora se o número estiver fora dessa faixa, o metodo valueOf sempre cria um novo objeto e a comparação com "==" será false.

Então por que no exemplo da classe C o resultado é true?
Neste caso, a comparação não é entre dois objetos e sim entre um objeto e um primitivo, isso só é possível por conta do unboxing. Vamos ver o bytecode:

class C extends java.lang.Object{
C();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: sipush 280
   3: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   6: astore_1
   7: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   10: aload_1
   11: invokevirtual #4; //Method java/lang/Integer.intValue:()I
   14: sipush 280
   17: if_icmpne 24
   20: iconst_1
   21: goto 25
   24: iconst_0
   25: invokevirtual #5; //Method java/io/PrintStream.println:(Z)V
   28: return

}

Vemos o unboxing acontecendo no método intValue(), então nesse caso a comparação passa a ser entre dois primitivos, o que explica o resultado true.

Para não cair nesse tipo de situação, o correto seria fazer a comparação utilizando o método equals.

sexta-feira, 7 de março de 2014

Configurando seu ambiente de desenvolvimeto android Android

A fim de lembrar e aprender mais sobre o desenvolvimento para a plataforma Android, decidi registrar os passos necessários para este recomeço.

Downloads Necessários:

1) JDK (http://www.oracle.com/technetwork/java/javase/downloads/index.html)
2) Android SDK (http://developer.android.com/sdk/index.html)
3) Eclipse IDE (http://www.eclipse.org/downloads/)


Instalações:

1) Instale a JDK seguinte as instruções do instalador padrão.
2) Instale o Android SDK seguindo as instruções do instalador padrão.
3) Descompacte o Eclipse.

Configurações

Após feitas as instalações, devemos configurar o plugin que vai integrar o Eclipse a Android SDK.
Este plugin chama-se ADT (Android Development Tools).

Para isto, inicie o Eclipse e no barra de menu superior selecione a opção Help -> Install New Software.
Colo que o nome do plugin de ADT Plugin e a enseguida, coloque a seguinte URL: https://dl-ssl.google.com/android/eclipse/


Quando terminar esta configuração o eclipse será reiniciado e quando terminar, vá no menu Windows -> Preferences -> Android e configure o local da Android SDK que foi instalada nos passos anteriores.

Testando tudo

Para verificarmos se tudo esta corretamente instalado e configurado, vamos criar um dispositivo virtual, que nada mais é do que um emulador.

Para configurar um dispositivo virtual abra o SDK Manager e configure de acordo com as necesidades.
Selecione o dispositivo e clique em start. Se tudo estiver correto o dispositivo criado será inicializado.