Web scraping – Um spider para baixar vídeos do facebook usando Chrome Headless

No último post, vimos como criar um scraper básico usando  apenas programas do GNU/Linux e as limitações que encontramos por usar ferramentas genéricas. É melhor usar uma linguagem de programação completa para criar um spider, assim podemos usar as bibliotecas pensadas para casos de uso como esse. Vejam o mesmo programa para obter a foto do dia do site da NASA, agora feito em Python 3:

#!/usr/bin/env python3
#!-*- encoding: utf8 -*-
#Baixa a mais recente imagem do site APOD(apod.nasa.gov)
from bs4 import BeautifulSoup #Para parsear HTML
from pathlib import Path #Para obter path da pasta do usuário do sistema
import requests #Para fazer requisições a servidores web
from urllib.parse import urljoin #Para concatenar URL

#pega o html da página
#inicia o parser com esse html
#obtem a url relativa da imagem
#GET para URL da imagem
linkBase = 'http://apod.nasa.gov/apod'
req = requests.get(linkBase)
soup = BeautifulSoup(req.text, 'html.parser')
src = soup.find('img').get('src')
res = requests.get(urljoin(linkBase,src)) 

#Se houve sucesso na requisição..
if res.status_code == 200:
	imgPathDisk = str(Path.home()) + '/' + src.split('/')[-1]
	print("Salvando imagem em " + imgPathDisk)
	#salva no path ~/<nome-foto-do-dia.jpg>
	with open(imgPathDisk, 'wb') as f: f.write(res.content)
else: print("Erro, não consegui baixar a imagem do dia :(")

Com o BeautifulSoup, esse spider já entende o html da página e suporta CSS Selectors. Isso é infinitamente melhor para encontrar elementos numa página web que filtrar o código usando expressões regulares ou grep como no post anterior.

Além de entender o html ao buscar a imagem, esse script não é muito mais que uma tradução para Python do script mostrado no post anterior.

Ele pode bastar para casos de uso simples como esse mas a medida que as necessidades de scraping vão introduzindo operações como login e/ou interações com interfaces geradas por js, esse script vai se tornando limitado, ele entende html porém não processa javascript, portanto teria dificuldades para interagir com uma aplicação web gerada assim, o que é cada vez mais comum.

solareclipseHDR_largeDemeter
The Crown of the Sun 
Image Credit & Copyright: Derek Demeter (Emil Buehler Planetarium) Fonte: apod.nasa.gov

Os robôs que fizemos até agora conseguem simular interações simples com sites feitos para serem consumidos por usuários e seus browsers. Isso é ótimo porém a medida que precisarmos fazer coisas mais complexas, nossa dificuldade vai se concentrar cada vez mais na simulação do browser, se nosso robô precisar clicar em algo, precisamos ser capazes de disparar essa ação sem o click, por exemplo, encontrando a função chamada quando clicamos um determinado botão e a executá-la diretamente. Isso não é trivial e como muitos sites obfuscam o javascript, pode se tornar bem difícil de fazer.

Mas e se não precisarmos fingir que somos um browser?

A muito tempo já existe a possibilidade de automatizar ações dentro de um browser. Isso é: o seu navegador aberto executando um script sem necessidade de interação do usuário. Isso poderia ser feito via plugins, extensões, AutoIt’s que tem por aí.  O problema é que isso ocupa toda a interface gráfica e impede paralelização, além de não escalar. Você poderia realizar uma tarefa por vez pois ela ocuparia seu mouse, sua tela e seu browser. Isso é pouco prático para cenários reais onde é comum precisar obter uma grande quantidade de dados.

Também existe a possibilidade de simular essas interações com engines de browsers funcionando separadas da interface através de um software dirigindo isso como o Selenium. O problema é que o desenvolvimento dessas engines nem sempre acompanha o dos browsers e isso expõe os robôs a diferenças entre a tela que eles veriam e a tela que o usuário veria. Ou seja, seu spider enxergaria os sites de modo diferente de um usuário humano e isso é PÉSSIMO para esse tipo de trabalho. Imagina que você fez um robô para testar a interface do seu programa a cada atualização, seria inútil se quando ele visitar a página do seu programa ele visse algo diferente de seus clientes, correto?

Ainda tem outra alternativa: simular uma tela de computador que abre um navegador e manipula tudo isso na memória sem nunca enviar para um display server. Ou seja: Executar o navegador “virtualmente”. Ele executa de verdade, apenas não envia as informações para a tela, esse já é um enorme avanço em relação aos métodos anteriores pois o scraping é executado de modo “invisível” e pode ser paralelizado sem problemas.

O Google varre a internet com seu robô que visita os sites e o indexa. A muito tempo pessoas suspeitavam que esse robô era uma versão modificada do Chrome para esse fim.
Em abril desse ano isso foi confirmado com o lançamento do Chrome 59 com a opção –headless.

Agora nós desenvolvedores temos todo o poder necessário pra escrever spiders de qualquer tipo, seja lá o que um usuário faça num site, poderá ser reproduzido de forma automática dentro de um browser completo e real.

Você já tentou baixar um vídeo do facebook? Pelo browser do desktop você encontra um endereço de streaming do vídeo se o examinar com o Developer Tools. O modo mais conhecido para se obter o arquivo completo é indo até a versão mobile da página do vídeo que deseja baixar e dar play. Então um vídeo será reproduzido a partir de um arquivo .mp4 que é facilmente baixável ao contrário do vídeo exibido na versão desktop. Por exemplo:

Suponha que quero baixar esse vídeo do endereço: http://www.facebook.com/dcmjunior/videos/1794891250528272/

O jeito seria visitar  a URL m.facebook.com/dcmjunior/videos/1794891250528272/ para obter a versão mobile da página do vídeo, dar play e então salvá-lo no disco.

Vamos fazer um robô que recebe o link de um vídeo do facebook e devolve o arquivo mp4 desse vídeo para nos salvar desse processo? Não vale usar a API do facebook, estamos fazendo um spider!

As bibliotecas BeautifulSoup e requests já não nos bastam mais, podemos fazer um login que mantenha a sessão porém provavelmente travaríamos na parte de dar play no vídeo para chamar o javascript que exibe o mp4 a ser baixado pois essas bibliotecas não foram feitas para simular interação de um navegador web com um site.

Vamos usar o Chrome Headless. Existem vários softwares feitos para interfacear com o ele, já testei o puppeteer, o chromeless e o TagUI mas toda semana são lançados outros.

Antes nós usamos um bando de ferramentas que precisam ser colocadas juntas pra fazer algo parecido com uma interação de usuário com o site, agora temos um browser completo, programável e invisível para interfacearmos com os sites. Melhor ainda, ele é difícil de detectar como spider, isso significa que os sites não conseguem facilmente diferenciar um spider de um usuário comum, por isso podemos confiar que a página será enxergada pelo robô como o usuário a enxergaria.

Tem situação melhor para se fazer um web-spider? Acho que não!

Para esse exemplo, escolhi o puppeteer para controlar o Chrome Headless, o script resumido para obter o link completo do vídeo fica assim:

const puppeteer = require('puppeteer');
const { URL } = require('url');

(async () => {
	const browser = await puppeteer.launch();
	const page = await browser.newPage();
	const link = new URL('http://www.facebook.com/dcmjunior/videos/1794891250528272/');

	if(["www.facebook.com",
	    "facebook.com",
	    "www.fb.com",
	    "fb.com",
	    "m.facebook.com",
	    "mobile.facebook.com"
	   ].includes(link.host)){
		await page.goto("http://m.facebook.com" +
						 link.href.substring(link.origin.length, link.href.length),
						{waitUntil: 'networkidle'});

		await page.click('#u_0_0 > div > div > div > div > div > i');
		await page.waitForSelector('#mInlineVideoPlayer', {visible: true});

		// Pega o link do video da página
		const videoLink = await page.evaluate(() => {
		return document.getElementById('mInlineVideoPlayer').src;
		});

		console.log(videoLink);
	} else {
		console.log('URL fornecida é inválida')
	}

  	browser.close();
})();

Com esse tooling já temos muito mais poder em mãos para criar spiders, com o puppeteer e o Chrome Headless podemos dizer como o programa deve se comportar tendo o browser disponível para interação. Nesse script, as linhas principais são:

page.goto("http://m.facebook.com" + ... ,{waitUntil: 'networkidle'});
page.click('#u_0_0 > div > div > div > div > div > i');
page.waitForSelector('#mInlineVideoPlayer', {visible: true});

A primeira diz que o browser deve visitar a URL do vídeo e esperar até que o browser baixe todos os requests feitos;
A segunda manda o browser clicar no vídeo, selecionando o elemento com um CSS Selector;
A terceira espera a div com o mp4 surgir, depois o script retorna a URL do vídeo.

Salve esse script como getvideo.js, depois:

$node getvideo.js | xargs wget

Pronto, agora você tem um robô que baixa o vídeo de uma página do facebook (e salva com um nome horrível!) a partir de um comando apenas.
Esse script é só um exemplo, uma versão mais completa que aceita qualquer link de vídeo no facebook como argumento e é software livre está num repositório no github.

Anúncios

Web scraping – Criando um crawler mínimo para imagens da NASA

Pra quem não sabe o que é web-scraping: É a atividade de extrair dados de um site via script programado para interagir com a interface destinada a usuários humanos.

Quando um site quer expor um conteúdo para robôs, ele não precisa considerar aspectos estéticos ou comportamentais de pessoas na construção dessa interface e por isso ela é chamada de Interface de Programação de Aplicativos (API em inglês).

Um scraper ou spider é um robô que caminha na internet pelas ruas que foram feitas para os *humanos* caminharem, ele anda pelas interfaces de usuários dos websites com o objetivo de obter dados expostos e estruturados para usuários humanos.

Um scraper clica em links, preenche e envia formulários, espera, etc; tudo como uma pessoa usando o site faria.

Se existe um caminho para robôs por que ir pela via dos usuários?

O site pode não fornecer uma API ou fornecê-la de maneira limitada. Por exemplo: usando a API do twitter você pode obter os últimos 3200 tweets de uma conta. Esse é o limite da rua que o Twitter construíu para os robôs passarem, se você quiser qualquer tweet a mais, terá de obtê-lo manualmente… OU usando um robô que se comporte como um humano e busque os tweets por você.

O MÍNIMO que um spider faz é enviar um request para uma URL e filtrar a response em busca de algo.

Imagine que você gosta muito de astronomia e diariamente visita o site da NASA Astronomic Picture of the Day e salva em seu disco a foto do dia.

Vamos automatizar esse processo.

Algo como:

#!/bin/bash
#captura o html da página
wget -qO- apod.nasa.gov |
#filtra pra ficar só <IMG SRC="image/xxxx/nnnnnnn...jpg"
grep IMG |
#remove o que não está entre aspas
cut -d '"' -f2 |
#captura a string resultante, e insere antes a raiz do link relativo
awk '{print "http://apod.nasa.gov/apod/" $0}' |
#baixa pro ~/Pictures
xargs wget --directory-prefix=/home/$USER/Pictures

Já cumpre essa tarefa, ele acessa o side da NASA, filtra o HTML em busca da imagem que deseja, e monta uma URL própria para obtê-la e salvar  na pasta /home/$USER/Pictures

Basta adicionar um job no cron para repetir esse script diariamente e pronto, você nunca mais* precisará buscar essa imagem, ela irá aparecer na sua pasta de imagens automaticamente.

Esse é um exemplo de spider rudimentar que pode ser feito. Ele não abre um navegador, não interpreta o HTML, só filtra o código fonte e não interage além do acessar e fazer download da imagem do dia. Ele funciona pois o site é simples e o acesso a ele é livre. Não pede login, não se preocupa com sessão, não usa javascript para posicionar os elementos ou renderizar o conteúdo. Cumpre uma sequência trivial: acessar o site e obter a única imagem da página.

Um roteiro mais exigente tornaria o desenvolvimento de um spider mais trabalhoso usando somente ferramentas do GNU/Linux.

Imagine fazer login enviando os dados via POST mantendo a sessão entre os requests, parsear a resposta usando regex para então obter o que se busca… Isso seria uma tarefa árdua e de difícil manutenção, por isso foram criadas bibliotecas em diversas linguagens de programação para interagir com sites de forma mais organizada e específica para esse contexto. Vamos explorar essas bibliotecas na próxima parte dessa série sobre scraping.

* Enquanto o site mantiver essa estrutura, ou similar (que não quebre o filtro)

Boas novidades, não tão brevemente depois

Eu havia deixado de lado esse blog depois de me envolver muito com as outras coisas que estavam acontecendo.

Engraçado voltar aqui 8 meses depois e ver que  já havia plantado a sementinha de uma idéia que floresceu a poucas semanas, engraçado porque me parecia ter tido essa idéia a pouco tempo mas a verdade é que demorei muito até conhecer um pouco do que precisava saber para construir essa idéia em aplicação. Isso somado a bastante procrastinação..

No entanto, finalmente:

As boas novidades:

Estou desenvolvendo o protótipo de uma sala de aula virtual da maneira que gostaria que uma sala de aula virtual fosse: rápidas, interativas e empática.

Acontece que eu até pouco, eu só conhecia ExtJs 4 para desenhar minhas aplicações web, daí fiz algo que ficou assim:

Como se parece uma aplicação web quando o desenvolvedor não conhece nenhum framework mobile.
Como se parece uma aplicação web quando o desenvolvedor não conhece nenhum framework mobile.

Você pode acompanhar o desenvolvimento desse protótipo visitando o seguinte endereço: mendesc.com/alpha

Quero ainda transformar esse protótipo em uma boa experiência mobile e por isso estou estudando HTML5 e um framework para desenvolvimento de aplicações mobile chamado Sencha Touch,  podendo depois transformar ele num MVP e também..

Estou, junto ao Gustavo,  trabalhando no desenvolvimento de um produto mas, sobre isso, acredito que é melhor conversar com ele antes de publicar detalhes. Espero ter mais para contar, em breve!!

E-learning

Estou muito envolvido com o e-learning.

É engraçado eu dizer isso pois a um mês atrás eu não não era de forma alguma envolvido com isso e só pensava em conseguir um estágio bacana já que a universidade estava em vias de entrar em greve.

Criei o blog com isso em mente, era pra ser uma boa apresentação profissional da minha pessoa mas antes que eu pudesse usá-lo assim eu consegui o estágio, e deixei aqui de lado! Não quero abandonar o blog, então vou contar um pouco sobre as coisas com as quais eu tenho me envolvido profissionalmente.

Trabalho em uma companhia que é uma instituição de ensino a distância. Dá pra afirmar que os sistemas de e-learning são o motor da empresa.

A educação a distância é uma área que ainda vem sendo explorada e o mercado ainda se prepara para atender as demandas, que estão crescentes. Eu percebi que quase todas as soluções para streaming ao vivo hoje existentes ignoram o fato de que temos problemas de infra-estrutura em TI, principalmente quando se fala de largura de banda. Não vou entrar em detalhes mas tenho estudado nos últimos dias a integração de tecnologias modernas pra construir um ambiente online de aprendizado em tempo real que seja moderno, dinâmico, e que atenda as necessidades de um país em desenvolvimento.. A cada dia que passa estou mais otimista quanto as possiblidades (elas vêm se mostrando reais)!

Espero postar boas novidades em breve.

Ideias gigantes

Já conhecia o YCombinator? É uma empresa que ajuda startup’s a nascerem. Funciona assim, se você quer iniciar uma startup, você pode inscrevê-la no YCombinator e eles poderão escolher sua startup junto com algumas dezenas de inscritos para receberem um pequeno investimento e se mudarem para o Vale do Silício, onde os escolhidos desenvolverão e refinarão suas startups por 3 meses, para que no final desse período eles a promovam ante uma plateia de investidores.

Recentemente, Paul Graham, que faz parte do time do YCombinator, publicou isso em sua página pessoal.

No artigo que linkei acima, Paul conta que a maioria das startups realmente ambiciosas, propunham ideias que mais assustavam que atraiam! Depois ele lista algumas ideias que ao mesmo tempo em que parecem beirar o impossível, podem tornar bilionária a pessoa que as trouxer à realidade, vou falar sobre as que eu achei mais interessantes:

Ideias que podem ter tornar um bilionário

  • Criar um novo motor de busca

Paul está insatisfeito com o google e sente falta do look-and-feel que ele tinha antigamente, concordo quando ele diz que hoje em dia, se você acidentalmente colocar o mouse no lugar errado, qualquer coisa pode acontecer hahaha. No meu entendimento ele propôs uma search engine cujos resultados sejam menos baseados no perfil do usuário e mais personalizáveis ao estilo argumentos de programas do linux, malícia.

  • Substituir o email

Paul diz que hoje o email é utilizado de uma maneira muito diferente da qual foi proposto, ele diz que hoje o email é usado como uma to do list. Mais ou menos assim: Quando você recebe um email, é como se alguém estivesse adicionando um item à sua lista de afazeres, se você ler o email, é como se você tivesse aceitado e cumprido essa tarefa que lhe foi delegada por quem te enviou. Quando não podemos riscar uma tarefa da lista, utilizamos de artifícios como “Marcar como não lido” ou mantemos na lembrança o afazer. Concordo com Paul, uma todolist abordaria essa forma de utilização do email de maneira matadora, imagine, você tem a sua todolist e alguém adiciona um item: Se o item for uma publicidade, pode ser considerado como feito no momento em que você leu a propaganda, certo? Então: o “item” era uma propaganda e a “tarefa” era: leia. Se você leu, menos um item na lista de afazeres de hoje. Se o item fosse uma atribuição delegada pelo seu chefe, você o marcaria como feito no momento em que realmente tivesse feito essa tarefa. Hoje, o que é feito? Bom, quando você abre um email que o seu chefe te enviou, o email já “retira da lista”, se você quiser o destaque que uma tarefa incompleta teria dentro de uma to do list, você marca esse email como não lido. Viu?

Consegue imaginar as possibilidades? Consigo imaginar isso sendo usado por todos no mundo, por empresas e escolas… Criar isso? Aí já é outra história.

  • Trazer de volta a Lei de Moore

Você sabe, a Lei de Moore é aquela que diz que a densidade dos circuitos dobra a cada ano e meio. Até 2002, se o seu software executasse de forma lenta, bastava esperar que o progresso no desenvolvimento das CPU’s aumentasse o poder de processamento e então você poderia obter melhor performance, era apenas questão de tempo.

Hoje, a Intel não consegue fornecer CPU’s com mais transistores, apenas um maior número delas, portanto, melhorar o desempenho depende de habilidades ninjas para reescrever o software de modo a distribuir o processamento de forma paralela entre as CPU’s, muito mais trabalho do que apenas esperar.

Escrever um software que possa fazer que um grande número de CPU’s aparentasse como apenas um processador bem rápido. Paul ainda dá a dica: um compilador que paralelize o código dado como entrada certamente é uma ideia ambiciosa (e bilionária!).

  • Diagnóstico em tempo real

Paul revela um truque que ele usa para ter ideias para startup’s: Ele imagina a forma como as futuras gerações vão lembrar e pensar a respeito de coisas que acontecem hoje. Algo que certamente será piada é: As pessoas iam no médico quando já apresentavam sintomas das doenças!! Exemplo: Em 2004, Bill Clinton estava com dificuldades para respirar e os doutores descobriram que várias de suas artérias estavam bloqueadas em mais de 90% (!). É razoável considerar que Bill Clinton dispunha do melhor tratamento médico disponível e mesmo assim algumas de suas artérias ficaram mais de 90% bloqueadas até que notassem algo de errado. Paul diz que em algum ponto no futuro nós conheceremos esses números mais ou menos da forma como conhecemos nosso peso. “Imagina só, antigamente as pessoas tinham que esperar por sintomas físicos até procurarem um médico por um diagnóstico!”. Sacou? Ele também alerta sobre o risco da inundação de falsos positivos que poderia acontecer, mas nos relembra que isso é por causa de uma limitação de hoje.

Uma tática de empreendedorismo:

Paul termina o artigo ensinando uma tática: Não abordar esses problemas de frente, não se deve dizer que sua startup vai substituir o email, isso vai criar expectativas gigantes e você será cobrado por isso além de criar uma platéia de hater’s que torcerão para ver a startup morrer em sua própria pretensão. É melhor lançar algo que pretende ser pequeno e vai se expandindo junto com o resto do mundo. Uma visão muito sólida de um objetivo futuro pode ser algo ruim ao invés de bom. Legal, né?