bash - extrai nomes de arquivos do arquivo html contendo vários links

3

Eu fiz o download de um arquivo html gerado automaticamente por um script em uma página da web. O arquivo contém vários links, incluindo links para imagens Eu estou tentando extrair os nomes completos das imagens, por exemplo

<a href="000000.jpg" title="image name.jpg" target="_blank">Image name.jpg</a>

acima, eu quero obter "Image name.jpg" armazenado em um arquivo. Como existem centenas deles, eu analiso o arquivo e armazeno cada nome à medida que surge com o seguinte comando:

grep -i -E -o "target=\"_blank\">([[:graph:]]*)\.(jpg|png|gif|webm)" "$thread" | cut -f 2 -d '>' | sed 's/ /_/g' - > "$names"

onde " $thread " é o nome do arquivo html, " $names " é a lista de nomes de arquivos como saída. Eu uso " cut " para remover a porção 'target="_blank">' e, em seguida, converto os espaços em underscores.

Como existem vários outros links no arquivo, especifico as extensões a serem capturadas (imagens e webm). todo o resto deve ser ignorado. Eu cheguei ao ponto de pegar esses links apenas, mas depois sinto falta de alguns.

Alguns arquivos contêm espaços e caracteres não alfanuméricos. Se eu usar [[:print:]] , que deve cobrir todos esses casos, não recebo nada, ou recebo um pouco da parte <head> do html e nada mais. Se eu usar [[:graph:][:space:]] , também não receberei nada. Se eu usar apenas [[:graph:]] (como acima) ou [[:alnum:][:punct:]] , posso obter arquivos com caracteres alfanuméricos / outros (como " filenamewith(parenthesis).jpg "), mas não espaços, e o inverso também é verdadeiro, [[:alnum:][:space:]] funciona, mas omite os outros caracteres imprimíveis (" file name with spaces.jpg " funciona, mas não "com (parênteses, vírgula ou outro.jpg").

Supostamente, [[:print:]] abrange todos os casos, mas não consigo o que preciso, o que, se estiver entendendo corretamente,

grep -E -o deve corresponder apenas (conforme o caso acima): *.jpg *.png *.gif ou *.webm

Eu tentei grep com e sem -E/-o/-e em diferentes variações.

Alguma ideia? Estou usando o Arch Linux, grep 2.20, bash 4.3.18

    
por CLos 11.07.2014 / 22:38

3 respostas

5

A melhor estratégia seria usar um analisador html adequado que possa cuspir o valor de todas as tags <a> .

Aqui, xmlstarlet é especificamente um analisador XML, e seu HTML pode não ser um XML bem formado, mas você pode ter a ideia:

echo '<html>
<a href="000000.jpg" title="image name.jpg" target="_blank">Image name.jpg</a>
</html>' | xmlstarlet sel -t -v //a
Image name.jpg
    
por 11.07.2014 / 22:56
2

Sua expressão regular é

target="_blank">([[:graph:]]*)\.(jpg|png|gif|webm)

Corresponde ao texto literal target="_blank"> , seguido por qualquer número de caracteres que não sejam espaços em branco, com uma das quatro sequências .jpg , .png , .gif ou .webm no final. Por exemplo, o comando grep mostraria as partes em negrito das seguintes linhas:

<a … target="_blank">something.jpg</a>
<a … target="_blank">a.gifted.child.txt</a>
<a … target="_blank">something else.jpg</a>
<a … target="_blank">something.jpg</a>+more.jpg

E se você usar [[:print:]] em vez de [[:graph:]] , isso corresponderia a algo como

<a … target="_blank">something.jpg</a> wibble wobble <a … target="_blank">something else.jpg</a>

Tudo entre o primeiro bit target … correspondente e o último ramal correspondente na linha é uma correspondência.

O que você precisa é excluir os caracteres de marcação HTML da correspondência.

target="_blank">[^<>]*\.(jpg|png|gif|webm)</a>

Com o GNU grep, você pode usar a opção -P para obter construções de expressões regulares Perl e, em particular, asserções de largura zero que permitem especificar que algo é precedido ou seguido por algum texto constante sem incluir esse texto no texto porção correspondida.

grep -o -P '(?<target="_blank">)[^<>]*\.(jpg|png|gif|webm)(?=</a>)'

Isso ainda pode falhar se houver espaço em branco inesperado (como uma nova linha entre a tag <a> e o fechamento </a> ou). Você faria melhor usar um analisador HTML adequado .

Por exemplo, em Python com BeautifulSoup (não testado):

import re, sys, BeautifulSoup
soup = BeautifulSoup(sys.stdin)
for hit in soup.find_all('a', target='_blank'):
    if re.match(r'.*\.(jpg|png|gif|webm)\Z', hit.string):
        print(hit.string)

Código semelhante pode ser escrito com HTML::Parser em Perl, Nokogiri em Ruby, etc.

    
por 13.07.2014 / 01:41
0

Acabei fazendo isso:

w3m -dump -T text/html "$thread" | grep -i -E -o 'File\:+([[:print:]]*)\.(jpg|png|webm|gif)'

O w3m limpa o código e, em seguida, eu posso procurar pelos nomes dos arquivos. (Eu preciso da parte literal "File:" para distinguir um arquivo vinculado de seu título). Eu preciso de [[: print:]] porque ele pega a maioria dos espaços em branco, caracteres unicode e outros printables.

que funciona como eu pretendia (embora eu ainda precise descobrir como evitar a substituição de arquivos com o mesmo nome, mas isso é outro dia de batalha)

    
por 17.07.2014 / 20:13