Vamos aprender sobre a VOYAGER, para aprender expressões regulares; este é um projeto da NASA que há décadas atrás lançou uma nave muito especial, não tripulada, contendo um artefato com informações relevantes sobre.. nós! O povo terráqueo. Dentro de um disco de ouro há coordenadas, informações, e música! Música do povo terrestre, de diversos lugares do mundo. Esta missão é a mais distante nave desenvolvida pela humanidade a alcançar o Espaço! E expressões-regulares? Porra, muito mais importante que a VOYAGER! Bora entender pq?
Olá pessoal! Nesta segunda xDICA estaremos utilizando o truque anterior para parsear com segurança certas strings de uma página web! Ao final desta sequência criaremos um programa para baixar músicas (domínio público, patrimônio da humanidade) de forma dinâmica, utilizando a página de referência dada aqui.
A brincadeira é a seguinte: na página da NASA temos uma lista de músicas impressas no disco dourado da Voyager. Enquanto a nave viaja com estas informações, nós vamos treinar shell script e alguns outros conceitos de Linux, sendo que ao final deste conjunto de dicas, você terá um script que busca as músicas contidas nesta lista. Divertido? Mãos à obra.
Vamos recordar a dica I:
( ls -1 | egrep ‘*$’ -n ) | while read LINE; do LINE=$( echo $LINE | cut -d\: -f2- ); echo \’$LINE\’; done
Desta vez ao invés de simplesmente, listar o diretório ( com ls -1 ), queremos o output das músicas que estão nesta página: http://voyager.jpl.nasa.gov/spacecraft/music.html
Como o site da Voyager recorrentemente deixa de responder, não sei qual a razão, criei um mirror do mesmo em https://www.gu.pro.br/voyager
Portanto, você utilizará a seguinte URL nos testes: https://www.gu.pro.br/voyager/spacecraft/music.html
Desta forma não se corre o risco de aloprar a NASA nem que a NASA alopre a gente.
Aliás na próxima dica mostrarei como é fácil criar um mirror através de funções que buscam links recursivamente. Daí vou mostrar também uns truques para ajeitar os links referenciados dentro da página copiada, de modo a utilizar URLs relativas no lugar de absolutas, para deixar o mirror o mais “independente” possível do servidor principal.
Por enquanto apenas parsearemos a listagem de músicas, então em primeiro lugar, você precisará baixar o documento da URL a seguir: https://www.gu.pro.br/voyager/spacecraft/music.html
Existem muitos programas que podem fazer isto, mas a forma mais eficiente e portável na minha opinião é o
cURL
, disponível em qualquer distribuição, com bibliotecas para C, PHP, Python, e um belíssimo executável para que você utilize em shell-scripts, que é nosso caso.
O comando para baixar a página correta e visualizar a fonte HTML com cURL é o seguinte:
curl -B https://www.gu.pro.br/voyager/spacecraft/music.html
Rodando isto no seu shell você verá algo do tipo:

Que é o output do arquivo HTML contido no servidor. Se você quiser inspecionar o HTML por sua conta, faça um PIPE com o comando “more” ou “less”, sério mesmo, não vou ensinar isso pois creio que já sabem direito como fazer pipes simples e redirecionar output. Teremos uma dica sobre isso mas tratando de coisas mais avançadas, até mesmo explicando um exploit, wow, juro juro juro, mas não agora, amiguinho(a)(s).
Enfim: Não queremos a página inteira, apenas as músicas. Daí buscamos um PADRÃO para solucionar com simplicidade nossos problema. O mestre dos magos de busca de padrões em cadeias de caracteres é o G R E P
! Para isso vamos utilizar o egrep, de modo a filtrar a saída e obter apenas o que queremos, utilizando EXPRESSÕES REGULARES, mais ou menos dessa forma:
curl -sB https://www.gu.pro.br/voyager/spacecraft/music.html | egrep ^.*[0-9]:[0-9][0-9]$
Só que isto não vai dar certo, pois quando baixamos a página via cURL, ela não está renderizada e sim em código HTML de diagramação. Só por hoje, vamos dar um Mandrake utilizando o comando “SED” (assunto pra mais tarde) substituindo qualquer coisa entre TAGS < >
por NADA.. Aí fica assim o comando:
curl -sB https://www.gu.pro.br/voyager/spacecraft/music.html | sed 's/<[^>]*>//g' | egrep ^.*[0-9]:[0-9][0-9]$
Yupi! Eis nossas músicas, extraídas quentinhas da página web. Agora tem que entender o procedimento, pô! Vamos lá?
Note a adição do parametro -s ao cURL : isto é para ele ficar quieto e não mostrar estatísticas de download, que nada importam, pro nosso script feliz.
Não se incomode com o sed
. É um truque meia-boca para remover as tags de HTML. Digo meia-boca pois nem todo HTML é estruturado como da NASA, sacou? Não existe receita mágica genérica pra tudo. Sempre teste as coisas e raciocine. Decoreba não é nada hacker, nada científico, nada filosófico. E não vale a pena — o mundo muda constantemente — uma hora o decoreba falha, pois o cenário é diferente. Neste caso vai funcionar.
Vamos focar no GREP! Isso que quero que seja entendido.
O comando grep no pipe utiliza expressões regulares, que traduzirei: BUSQUE o PADRÃO seguinte: linhas que COMECEM ( ^ ) por QUALQUER CARACTERE ( . ) repetidamente ( * ) até encontrar um padrão de ALGARISMOS DE 0-9 seguidos por DOIS PONTOS e mais DOIS CARACTERES que sejam algarismos numéricos, sendo isto o término da LINHA ( $ ); eita.
Meio complicado traduzir para o portuguẽs mas acho essencial a compreensão, neste ponto, destes sinais. Não fique lendo mil vezes a frase anterior: prossiga que entenderá melhor, prometo.
^
indica INÍCIO de linha na string.
$
por sua vez o cifrão indica o FINAL da linha.
Entre estes temos os metacaracteres e quantificadores em expressões regulares, e com eles vocẽ poderá literalmente programar output buscando por padrões, dentro de strings. É fundamental o conhecimento de expressões regulares para uma agilidade maior, e as vezes, você só soluciona seu problema, se tiver a expressão regular que combina com o output desejado. APRENDA EXPRESSÕES REGULARES! Não sou um mestre disto e existem zilhões de guias, mas o pouco que sei-que-nada-sei dividirei com prazer durante todas as dicas.
\ | ( ) [ { ^ $ * + ? . < >
Estes são caracteres reservados em regular expressions, caso você queira buscar pelo caractere literalmente, será necessário em alguns engines, “escapá-lo” com uma BARRA INVERTIDA \
seguida do caractere, por exemplo, \(oi\)
busca literalmente um parenteses abrindo, o caractere “o”, seguido do “i”, seguido de um parenteses fechando. Sem escapar o paranteses ele tem sua função reservada que você ainda aprenderá. Isto realmente depende da implementação de regexp que você está utilizando!
Note que utilizamos o metachar PONTO ( . ) e o ASTERISCO ( * ) , estrela, pentagrama, chame do que quiser, o que importa é que este QUANTIFICADOR instrui o ENGINE DE REGEXP utilizado a REPETIR a incidência do caractere imediatamente anterior, infinitamente até prosseguir com o padrão. Dependendo do engine este pára no FINAL DE UMA LINHA, dependendo dos parâmetros, ele vai todavida até o final do input. Só que fazer o engine buscar por “qualquer caractere” é caríssimo e muitas vezes desnecessário. Ao invés do esquema testar QUALQUER CARACTERE a cada iteração, é mais fácil construir expressões como a que vem a seguir.
Como o ( . ) ponto significa “QUASE QUALQUER CARACTERE” (veremos mais pra frente, que não são todos os caracteres contemplados pelo PONTO) seguido do * que instrui a repetição, estamos ordenando o motor de expressão regular a “BUSCAR quase QUALQUER CARACTERE repetidamente …”
. . . até que . . .
na sequencia encontre nossos CONJUNTOS de caracteres, no caso alfanuméricos: [0-9]:[0-9][0-9]
e o final da linha, $. Uma breve análise da página da NASA revela que, opa, as únicas linhas que terminam com este padrão são as que se referem as músicas!
Por isso mesmo poderíamos reescrever a expressão, de modo mais performático, simplesmente buscando linhas que contenham este padrao de “numerais – doispontos – dois numerais ao final da linha”.
grep [0-9]:[0-9][0-9]$
Quando colocamos caracteres entre colchetes, como veremos mais pra frente, estamos pedindo para o programa trabalhar agora com a “match” de CONJUNTOS de caracteres, no caso, todos os ALGARISMOS DE ZERO A NOVE. Olha só: ele buscará uma incidência de um algarismo de 0-9, depois a incidência de ( : ) DOIS PONTOS e novamente mais dois caracteres numéricos. Acho que deu pra sacar agora. Muito melhor que testar cada caractere por todos os caracteres a cada iteração… enfim. Dá pra entender.
DE UMA VEZ POR TODAS, AGORA VOCÊ nãaaao VAI ESCAPAR DE APRENDER! ASTERISCO NÃAAAAO funciona como no M$ D.O.S. ou command prompt, ele é MUITO mais poderoso do que isso. Já vi muito, muito mesmo, gente que erra na programação shell, php, whatever, por não entender a diferença entre um metachar e um quantificador.
Poderíamos facilmente mudar isto para [2-9]*
— instruindo assim o engine de REGEXP a buscar REPETIDAMENTE, caracteres numéricos de 2-9. [1239]*
por sua vez buscaria o algarismo 1, ou o 2, ou o 3, ou o 9, repetidamente. Mas mais tarde veremos melhor tudo isso. Por enquanto apenas consiga distinguir um QUANTIFICADOR de um METACARACTERE e elimine o pensamento “caractere coringa” e comece a pensar mais em “iterações” — ao invés de imaginar que o computador magicamente “substitui” as strings, comece a cair no deserto do mundo real, onde operações com strings são caras, mas se você souber programar as expressões, terá performance e o resultado desejado com MUITA facilidade.
Caramba! Vamos continuar?
Ok, tendo entendido por cima, não só copiando, a receitinha da regexp que vai pegar SÓ AS LINHAS das músicas. Respectivamente, com HTML e sem HTML:
curl -sB https://www.gu.pro.br/voyager/spacecraft/music.html | grep [0-9]:[0-9][0-9]
filtrando html:
curl -sB https://www.gu.pro.br/voyager/spacecraft/music.html | sed 's/<[^>]*>//g' | grep [0-9]:[0-9][0-9]$
Vamos parar por aqui. Experimente combinar o output com o truque da DICA 1, pois isso será fundamental para nosso script funcionar mediante ESPAÇOS por exemplo, que quebrariam tudo, se não fosse nosso truque lindo de Deus ensinado na dica 1.
Na próxima dica vamos fazer algo útil com o output.
Por enquanto, brinque com o GREP. Não há comando mais eficaz para rapidamente, DEBUGAR CÓDIGO, encontrar expressões, filtrar conteúdo, enfim,
AME o GREP. Você ainda vai precisar dele.
Obrigado pela participação!
May the source be with you. Always.
[ ]s G.C.:
PS: experimente um "cut" com delimitador dois pontos, filtrando html, você já terá a string prontinha. Mas um modo muito mais elegante é pegar apenas as strings entre as TAGS de parágrafo "p": experimente criar uma expressão regular que busque apenas o que está entre estas tags, no output sem filtragem de HTML. Te desafio.
Vale a pena brincar com variantes das expressões regulares, para entender, como conjuntos funcionam, como metacaracteres e quantificadores podem alterar completamente o “match” da string.
ADIOS!