Construa um leitor de tela mais esperto com Perl
Se você não tiver uma API para obter informações de páginas da web, é possível usar Perl como um pé de cabra para capturar os dados.
O firmware Tomato tem proporcionado muitos anos de confiança em serviço sem uma única falha (figura 1). Um exemplo são os três roteadores Linksys e que usam esse firmware empurrando pacotes ethernet em torno do laboratório Perlmeister Labs.

Como o site de administração do Tomato suporta todos os tipos de configurações úteis e exibe dados de caráter informativo, pensei que seria uma boa ideia escrever um leitor de dados que os recupera regularmente, os despeja em um banco de dados no meu computador em casa, e me alerta para o caso de eventos suspeitos.
Sem JavaScript!Isso mesmo! Quando tentei ler o conteúdo da página protegida da autenticação básica Auth com:
wget http://root:passwort@192.162.0.1
descobri rapidamente que o Tomato usa JavaScript para atualizar os campos exibidos e que um leitor de dados simples, como o módulo Perl WWW::Mechanize, apenas faz o download do código JavaScript em vez de baixar os dados de tempo de atividade que eu estava procurando.
Para que a página exiba os dados corretamente, é preciso utilizar um motor JavaScript: o motor interpreta o código e atualiza o DOM (Document Object Model) da página que está sendo exibida no navegador de acordo com as instruções que encontrar. Leitores de dados básicos não fazem isso; eles agem como navegadores que estão com o Java Script desativado e não vão interpretar determinados recursos.
A sétima maravilha do mundoO módulo CPAN WWW::Scripter possui a hercúlea tarefa de implementar as ações exigidas pelo navegador em Perl. Uma combinação entre o plugin WWW::Scripter::Plugin::Ajax e o módulo HTML::DOM proporciona a interface DOM necessária; o motor Pure Perl ECMAScript (basicamente JavaScript), motor JE, me garante todas as funções de que preciso.
Quando pensamos nas muitas diferenças que existem em navegadores com DOM comparando apenas Internet Explorer e Firefox, podemos imaginar quanto trabalho foi feito para o funcionamento desses módulos. Além disso, o módulo DOM age como outro navegador; as diferenças entre a sua implementação e o navegador padrão usado no desktop são inevitáveis. Outra abordagem para implementar um cliente controlado por JavaScript seria a utilização de um navegador com controle remoto, como o Selenium.
Listagem 1: Visão geral do Tomato
01 # !/usr/local/bin/perl -w
02 # ############################
03 # tomato-overview - Scrape
04 # simple JavaScript-enabled
05 # page
06 # Mike Schilli, 2011
07 # (m@perlmeister.com)
08 # ############################
09 use strict;
10 use WWW::Scripter;
11 use Sysadm::Install qw(:all);
12 use HTML::TreeBuilder::XPath;
13
14 my $w = new WWW::Scripter;
15 $w->use_plugin('Ajax');
16
17 my $pw = slurp "pw.txt";
18 chomp $pw;
19 $w->credentials("root", $pw);
20 $w->get(
21 'http://192.168.0.1');
22
23 $w->wait_for_timers(
24 max_wait => 1);
25
26 my $tree =
27 HTML::TreeBuilder::XPath
28 ->new;
29
30 $tree->parse($w‐>content());
31
32 my $uptime =
33 $tree->findvalue(
34 '/html/body//' .
35 'tr[@id="uptime"]/' .
36 'td[@class="content"]'
37 );
38
39 print "uptime: $uptime\n";
A listagem 1 começa por carregar o módulo WWW::Scripter e, em seguida, executa o plugin Ajax – disponível separadamente no CPAN – utilizando o método use_plugin(). A classe é derivada da WWW::Mechanize, que é derivada da LWP::UserAgent, por isso suporta o método get para pegar dados de sites. Como o roteador solicitará uma senha de root para execução do protocolo HTTP, o script herda e usa o método credentials() para fornecer a senha.
A terceirização da senhaPara evitar embutir a senha no script, a função slurp é usada para extraí-la do arquivo pw.txt no diretório atual. O arquivo contém uma única linha com a senha e deve ser protegido contra contra acesso não autorizado de leitura e gravação. Naturalmente, essa abordagem não é perfeita do ponto de vista da segurança, mas você terá que esconder a chave da porta debaixo do tapete em algum lugar, caso queira que o script seja executado automaticamente sem que o usuário digite a senha toda vez.
Execução dos motoresQuando o método get pega a página a partir da interface web do roteador, ele inicialmente não traz quaisquer dados – apenas o código JavaScript incorporado. A chamada wait_for_timers(), em seguida, começa a executar o motor JavaScript e o deixa trabalhar com o conteúdo da página. Isso agora bloqueia a leitura até que o último temporizador JavaScript no código pare de executar, o que levaria muitas eras para a maioria dos websites. O parâmetro max_wait então diz ao script para não esperar por mais de um segundo. Esse atraso é suficiente para a página do roteador ser preenchida com seus domínios dinâmicos. A chamada para o método content() retorna o HTML atualizado com os dados.
Pegando as coisas boas do HTMLComo podemos ver na figura 2, o valor do uptime é cercado de uma confusão de tags HTML, e você pode extraí-lo com expressões regulares ou com um analisador de HTML. A listagem 1 usa o HTML::TreeBuilder::XPath, um parser do CPAN, que é provavelmente o método mais conveniente. A expressão do caminho começa na linha 34, primeiro percorre o conteúdo da tag “body” na hierarquia do documento HTML e – graças às barras duplas – em seguida sonda as profundezas da página em busca das tags especificadas. A tag TR com o atributo id sendo “uptime” é o alvo aqui; ele inclui uma tag TD com um atributo de classe “content“, como mostrado na figura 3. O parâmetro findValue extrai o texto oculto, e o script só precisa apresentar o valor na saída de seus resultados.


A página de administração do Tomato, com as estatísticas de uso de largura de banda atual, traz um problema mais dinâmico. O gráfico mostrado na figura 4 está disponível no arquivo /bwm-realtime.asp dentro na raiz da URL do endereço do roteador. O gráfico, que é criado por JavaScript, mostra as variações durante as últimas 24 horas. A tabela abaixo mostra os valores atuais (kilobits por segundo, Kbps) para os dados recebidos (RX) e os dados transmitidos (TX). Além do primeiro valor para a medição de corrente, são registrados os picos, os valores médios (AVG), e o número total de bits transferidos desde que a medição começou, o que teve início quando a página foi carregada.

A primeira vez em que a página for carregada, todos os valores virão zerados; mas o gráfico começa a ser preenchido com dados interessantes em apenas alguns segundos. É por isso que a listagem 2 não espera por cerca de 5 buscas na função rounds() chamada na linha 27, em que se executa a função check_timers() para dizer ao script para executar os temporizadores no código JavaScript. Em seguida, ele espera por um segundo na linha 28. Depois de completar as buscas obrigatórias, a linha 42 executa novamente a função rounds(); essa é uma concha vazia para as buscas de teste da linha 27, mas a função extract_bandwidth está definida na linha 46 para as operações reais como as da linha 28.
Listagem 2: Largura de banda do Tomato
01 #!/usr/local/bin/perl -w
02 #############################
03 # tomato-bandwith - Java-
04 # Script-enabled screen
05 # scraper
06 # Mike Schilli, 2011
07 # (m@perlmeister.com)
08 #############################
09 use strict;
10 use Sysadm::Install qw(:all);
11 use WWW::Scripter;
12 use HTML::TableExtract;
13 use YAML qw(Dump);
14
15 my $w = new WWW::Scripter;
16 $w->use_plugin('Ajax');
17
18 my $pw = slurp "pw.txt";
19 chomp $pw;
20
21 $w->credentials("root", $pw);
22 $w->get(
23 'http://192.168.0.1/' .
24 'bwm-realtime.asp'
25 );
26
27 rounds($w, 5, sub { });
28 rounds($w, 1,
29 \&extract_bandwidth);
30
31 #############################
32 sub rounds {
33 #############################
34 my ($w, $rounds, $callback)
35 = @_;
36
37 for (1 .. $rounds) {
38 $w->check_timers()
39 sleep(1);
40 }
41
42 $callback->($w->content);
43 }
44
45 #############################
46 sub extract_bandwidth {
47 #############################
48 my ($html) = @_;
49
50 my $te =
51 HTML::TableExtract‐>new();
52 $te->parse($html);
53
54 my $ts =
55 $te->first_table_found();
56
57 my %bw = ();
58
59 foreach my $row ($ts->rows)
60 {
61 my @cols =
62 map { /(\S+)/ } @$row;
63
64 $bw{ $cols[0] } = {
65 avg => $cols[3],
66 peak => $cols[5],
67 };
68 }
69
70 print Dump(\%bw);
71 }
O script usa o módulo CPAN HTML::TableExtract como analisador para a informação escondida nas tabelas HTML. Na linha 52, o seu método parse() aceita o código HTML da página, que foi modificado pelo JavaScript, e cria sua árvore de sintaxe. O método first_table_found (), em seguida, procura a primeira tabela HTML que, coincidentemente, contém os dados necessários sobre a largura de banda da página do Tomato.
Durante a fase de teste, quando eu não sabia qual tabela continha as informações, o método table() no mesmo módulo me ajudou a descobrir, retornando todas as tabelas que foram encontradas como objetos. A função coords() diz a posição e os detalhes da hierarquia e das rows() retornando o conteúdo página por página. A página do manual explica como esse módulo funciona detalhadamente.
O Tomato exibe os valores em Kbps, mas também adiciona um valor para kbps (kilobytes por segundo). Uma vez que eles diferem apenas por um fator constante, a expressão regular da instrução map na linha 62 filtra o último valor por truncagem após o primeiro espaço em branco. A primeira coluna em @cols pode ser tanto “RX” como “TX”, seguida pela taxa de transferência atual.
A quarta e a sexta colunas (índices de array 3 e 5 no script) contêm os valores para a largura de banda e os valores do pico. A linha 64 passa os valores como hashes com as chaves avg e peak em um outro hash utilizando as chaves RX ou TX.
Para permitir um processamento de máquina mais fácil na saída do script, a linha 70 utiliza o método Dump fornecido pelo módulo YAML carregado no cabeçalho do programa para imprimir o hash resultante. A figura 5 mostra os resultados na linha de comando. Um script de pós-processamento pode usar o método de carga do mesmo módulo para carregar o hash na memória e trabalhar com ele.
InstalaçãoAlém do leitor web WWW::Mechanize, os scripts grabber dinâmicos precisam dos módulos CPAN WWW::Scripter e WWW::Scripter::Plugin::Ajax, que você pode instalar através de um shell CPAN no Ubuntu. O analisador XPath usado na listagem 1, HTML::TreeBuilder::XPath) e o analisador de tabela usado na listagem 2 (HTML::TableExtract) também estão disponíveis a partir do CPAN.
É claro que você pode usar SSH para criar uma comunicação segura com o roteador Tomato, e poderia até criar uma nova imagem que forneça uma API web. Mas os truques mostrados aqui servem como um exemplo perfeito para a captura de tela avançada e pode ser aplicada a praticamente qualquer página na web.
Os leitores de tela hackers apenas têm que se certificar de que a leitura dos dados não viola os termos de serviço dos provedores, já que muitos provedores proíbem esse tipo de leitura.







