Como obtenho o título de um site usando a linha de comando?

50

Eu quero um programa de linha de comando que imprima o título de um site. Por exemplo:

Alan:~ titlefetcher http://www.youtube.com/watch?v=Dd7dQh8u4Hc

deveria dar:

Why Are Bad Words Bad? 

Você fornece o URL e ele imprime o título.

Ufoguy
fonte
2
Ao baixar esse título, recebo: "Por que as palavrões são ruins? - Youtube", você também quer o "- Youtube" truncado?
slm

Respostas:

44
wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si'

Você pode canalizar para o GNU recodese houver coisas como &lt;:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si' |
  recode html..

Para remover a - youtubepeça:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
 perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)(?: - youtube)?\s*<\/title/si'

Para apontar algumas das limitações:

portabilidade

Não há comando padrão / portátil para fazer consultas HTTP. Algumas décadas atrás, eu recomendaria lynx -sourceaqui. Atualmente, porém, wgeté mais portátil, pois pode ser encontrado por padrão na maioria dos sistemas GNU (incluindo a maioria dos sistemas operacionais de desktop / laptop baseados em Linux). Outros razoavelmente portáveis ​​incluem o GETcomando que vem com perla libwww que é frequentemente instalada e lynx -source, em menor grau curl. Outros comuns aqueles incluem links -source, elinks -source, w3m -dump_source, lftp -c cat...

Protocolo HTTP e manipulação de redirecionamento

wgetpode não ter a mesma página que a que, por exemplo, firefoxseria exibida. O motivo é que os servidores HTTP podem optar por enviar uma página diferente com base nas informações fornecidas na solicitação enviada pelo cliente.

A solicitação enviada pelo wget / w3m / GET ... será diferente da enviada pelo firefox. Se esse é um problema, você pode alterar o wgetcomportamento para alterar a maneira como ele envia a solicitação, com opções.

Os mais importantes aqui a esse respeito são:

  • Accepte Accept-language: informa ao servidor em que idioma e conjunto de caracteres o cliente gostaria de receber a resposta. wgetnão envia nenhum por padrão; portanto, o servidor normalmente envia com suas configurações padrão. firefoxdo outro lado, provavelmente está configurado para solicitar seu idioma.
  • User-Agent: que identifica o aplicativo cliente para o servidor. Alguns sites enviam conteúdo diferente com base no cliente (embora seja principalmente devido a diferenças entre interpretações de linguagem javascript) e podem se recusar a atendê-lo se você estiver usando um agente de usuário do tipo robôwget .
  • Cookie: se você já visitou este site antes, seu navegador pode ter cookies permanentes para ele. wgetnão vou.

wgetseguirá os redirecionamentos quando forem feitos no nível do protocolo HTTP, mas como ele não analisa o conteúdo da página, não os que são feitos por javascript ou coisas do tipo <meta http-equiv="refresh" content="0; url=http://example.com/">.

Desempenho / Eficiência

Aqui, por preguiça, perllemos todo o conteúdo na memória antes de começar a procurar a <title>tag. Dado que o título é encontrado na <head>seção que está nos primeiros bytes do arquivo, isso não é o ideal. Uma abordagem melhor, se o GNU awkestiver disponível no seu sistema, pode ser:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
  gawk -v IGNORECASE=1 -v RS='</title' 'RT{gsub(/.*<title[^>]*>/,"");print;exit}'

Dessa forma, o awk para de ler após o primeiro </titlee, ao sair, faz wgetcom que o download seja interrompido.

Análise do HTML

Aqui, wgetescreve a página enquanto a baixa. Ao mesmo tempo, perlinverte toda a saída ( -0777 -n) na memória e depois imprime o código HTML encontrado entre as primeiras ocorrências de <title...>e </title.

Isso funcionará para a maioria das páginas HTML que possuem uma <title>tag, mas há casos em que ela não funciona.

Por outro lado, a solução do coffeeMug analisará a página HTML como XML e retornará o valor correspondente para title. É mais correto se é garantido que a página é XML válida . No entanto, não é necessário que o HTML seja XML válido (as versões mais antigas da linguagem não eram) e, como a maioria dos navegadores existentes é branda e aceita códigos HTML incorretos, há muitos códigos HTML incorretos por aí.

Tanto a minha solução como a do coffeeMug falharão em vários casos de canto, às vezes iguais, às vezes não.

Por exemplo, o meu falhará:

<html><head foo="<title>"><title>blah</title></head></html>

ou:

<!-- <title>old</title> --><title>new</title>

Enquanto o dele falhará:

<TITLE>foo</TITLE>

(html válido, não xml) ou:

ou:

<title>...</title>
...
<script>a='<title>'; b='</title>';</script>

(novamente, partes htmlausentes e válidas <![CDATA[para torná-lo XML válido).

<title>foo <<<bar>>> baz</title>

(html incorreto, mas ainda encontrado e suportado pela maioria dos navegadores)

interpretação do código dentro das tags.

Essa solução gera o texto bruto entre <title>e </title>. Normalmente, não deve haver nenhuma tag HTML, pode haver comentários (embora não sejam tratados por alguns navegadores como o Firefox tão improvável). Ainda pode haver alguma codificação HTML:

$ wget -qO- 'http://www.youtube.com/watch?v=CJDhmlMQT60' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si'
Wallace &amp; Gromit - The Cheesesnatcher Part 1 (claymation) - YouTube

O que é tratado pelo GNU recode:

$ wget -qO- 'http://www.youtube.com/watch?v=CJDhmlMQT60' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si' |
   recode html..
Wallace & Gromit - The Cheesesnatcher Part 1 (claymation) - YouTube

Mas um cliente da Web também deve fazer mais transformações nesse código ao exibir o título (como condensar alguns espaços em branco, remover os principais e os finais). No entanto, é improvável que haja uma necessidade disso. Assim, como nos outros casos, cabe a você decidir se vale a pena o esforço.

Conjunto de caracteres

Antes do UTF-8, iso8859-1 costumava ser o conjunto de caracteres preferido na Web para caracteres não ASCII, embora estritamente falando eles precisassem ser escritos como &eacute;. Versões mais recentes do HTTP e da linguagem HTML adicionaram a possibilidade de especificar o conjunto de caracteres nos cabeçalhos HTTP ou HTML, e um cliente pode especificar os conjuntos de caracteres que aceita. Atualmente, o UTF-8 é o conjunto de caracteres padrão.

Então, isso significa que lá fora, você vai encontrar éescrito como &eacute;, como &#233;, como UTF-8 é, (0xC3 0xA9), como iso-8859-1 (0xE9), com os 2 últimos, por vezes, as informações sobre o charset nos cabeçalhos HTTP ou HTML (em diferentes formatos), às vezes não.

wget obtém apenas os bytes brutos, não se importa com o significado deles como caracteres e não informa o servidor da web sobre o conjunto de caracteres preferido.

recode html..tomará o cuidado de converter &eacute;ou &#233;na seqüência adequada de bytes para o conjunto de caracteres usado em seu sistema, mas, quanto ao resto, isso é mais complicado.

Se o seu conjunto de caracteres do sistema for utf-8, é provável que esteja tudo bem na maioria das vezes, pois esse tende a ser o conjunto de caracteres padrão usado hoje em dia.

$ wget -qO- 'http://www.youtube.com/watch?v=if82MGPJEEQ' |
 perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si'
Noir Désir - L&#39;appartement - YouTube

Isso éacima foi um UTF-8 é.

Mas se você deseja cobrir outros conjuntos de caracteres, mais uma vez, isso deve ser resolvido.

Também deve ser observado que esta solução não funcionará de maneira alguma para páginas codificadas em UTF-16 ou UTF-32.

Resumindo

Idealmente, o que você precisa aqui é um navegador da Web real para fornecer as informações. Ou seja, você precisa de algo para fazer a solicitação HTTP com os parâmetros adequados, interpretar a resposta HTTP corretamente, interpretar completamente o código HTML como um navegador faria e retornar o título.

Como não acho que isso possa ser feito na linha de comando com os navegadores que conheço (embora veja agora esse truquelynx ), você precisa recorrer a heurísticas e aproximações, e a acima é tão boa quanto qualquer outra.

Você também pode levar em consideração desempenho, segurança ... Por exemplo, para cobrir todos os casos (por exemplo, uma página da Web que tenha algum javascript extraído de um site de terceiros que define o título ou redireciona para outra página em um onload hook), pode ser necessário implementar um navegador da vida real com seus mecanismos dom e javascript que podem precisar fazer centenas de consultas para uma única página HTML, algumas das quais tentando explorar vulnerabilidades ...

Embora o uso de regexps para analisar HTML geralmente seja desaprovado , este é um caso típico em que é bom o suficiente para a tarefa (IMO).

Stéphane Chazelas
fonte
Também faz o download das imagens das páginas? Também deixará arquivos html indesejados para trás?
Ufoguy
2
Você provavelmente deseja encerrar o título na primeira instância, <uma vez que não é garantido que os títulos tenham tags finais e qualquer outra tag deve forçar seu término. Você também pode querer descascar novas linhas.
Brian Nickel
11
Não é recomendável usar expressões regulares para analisar HTML. Sempre. Nem mesmo neste caso. É um mau hábito. Use um analisador real. Há uma famosa resposta Stackoverflow humorístico sobre isso ...
Robin Green
4
@RobinGreen Esse post foi sobre o uso de regex para analisar um idioma não regular. Existem advertências, mas esse é um problema que é facilmente reduzido a um idioma comum. Eu recomendo usar regex para analisar HTML. As vezes. Nesse caso.
Brian Nickel
2
E o número de expressões regulares que o trabalho para quase tudo é de aproximadamente 0.
Robin Green
27

Você também pode tentar hxselect(do HTML-XML-Utils ) com wgeto seguinte:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' | hxselect -s '\n' -c  'title' 2>/dev/null

Você pode instalar hxselectem distros baseadas Debian usando:
sudo apt-get install html-xml-utils.

O redirecionamento STDERR é para evitar a Input is not well-formed. (Maybe try normalize?)mensagem.

Para se livrar do "- YouTube", canalize a saída do comando acima para awk '{print substr($0, 0, length($0)-10)}'.

coffeMug
fonte
"hxselect" não parece estar instalado no Ubuntu por padrão. Eu até não consigo encontrá-lo em meus repositórios existentes. Como instalo?
Ufoguy
7
sudo apt-get install html-xml-utils
precisa saber é o seguinte
Eu recebo este erro no Ubuntu 12.10 "A entrada não está bem formada. (Talvez tente normalizar?)"
slm
11
Não encontrei o que fazer com a mensagem. sobre como normalizar a saída. Não existe esse interruptor hxselect.
slm
11
Para o pessoal do Mac OS X, o Homebrew possui uma fórmula com o hxselect. Instale com brew install html-xml-utils.
Sukima
18

Você também pode usar curle grepfazer isso. Você vai precisar de recorrer a utilização do PCRE (Perl Compatible Regular Expressions) em greppara obter a aparência trás e instalações à frente olhar para que possamos encontrar as <title>...</title>tags.

Exemplo

$ curl 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -so - | \
    grep -iPo '(?<=<title>)(.*)(?=</title>)'
Why Are Bad Words Bad? - YouTube

Detalhes

Os curlcomutadores:

  • -s = silencioso
  • -o - = envia a saída para STDOUT

Os grepcomutadores:

  • -i = insensibilidade ao caso
  • -o = Retorna apenas a parte que corresponde
  • -P = Modo PCRE

O padrão para grep:

  • (?<=<title>) = procure uma string que comece com isso à esquerda dela
  • (?=</title>) = procure uma string que termine com isso à direita
  • (.*)= tudo no meio <title>..</title>.

Situações mais complexas

Se <title>...</titie>abranger várias linhas, o item acima não o encontrará. Você pode atenuar essa situação usando tr, para excluir qualquer \ncaractere, ou seja tr -d '\n'.

Exemplo

Arquivo de exemplo.

$ cat multi-line.html 
<html>
<title>
this is a \n title
</TITLE>
<body>
<p>this is a \n title</p>
</body>
</html>

E uma amostra:

$ curl 'http://www.jake8us.org/~sam/multi-line.html' -so - | \
     tr -d '\n' | \
     grep -iPo '(?<=<title>)(.*)(?=</title>)'
this is a \n title

lang = ...

Se o <title>conjunto estiver definido assim, <title lang="en">você precisará removê-lo antes de grepinseri-lo. A ferramenta sedpode ser usada para fazer isso:

$ curl 'http://www.jake8us.org/~sam/multi-line.html' -so - | \
     tr -d '\n' | \
     sed 's/ lang="\w+"//gi' | \
     grep -iPo '(?<=<title>)(.*)(?=</title>)'
this is a \n title

O lang=texto acima localiza a sequência que não diferencia maiúsculas de minúsculas, seguida por uma sequência de palavras ( \w+). É então retirado.

Um analisador de HTML / XML real - usando Ruby

Em algum momento, o regex falhará na solução desse tipo de problema. Se isso ocorrer, é provável que você queira usar um analisador de HTML / XML real. Um desses analisadores é Nokogiri . Está disponível no Ruby como uma jóia e pode ser usado assim:

$ curl 'http://www.jake8us.org/~sam/multi-line.html' -so - | \
    ruby -rnokogiri -e \
     'puts Nokogiri::HTML(readlines.join).xpath("//title").map { |e| e.content }'

this is a \n title

A descrição acima está analisando os dados que vêm via curlHTML ( Nokogiri::HTML). O método xpathprocura por nós (tags) no HTML que são nós folha ( //) com o nome title. Para cada encontrado, queremos retornar seu conteúdo ( e.content). Em putsseguida, os imprime.

Um analisador de HTML / XML real - usando Perl

Você também pode fazer algo semelhante com o Perl e o módulo HTML :: TreeBuilder :: XPath .

$ cat title_getter.pl
#!/usr/bin/perl

use HTML::TreeBuilder::XPath;

$tree = HTML::TreeBuilder::XPath->new_from_url($ARGV[0]); 
($title = $tree->findvalue('//title')) =~ s/^\s+//;
print $title . "\n";

Você pode executar este script da seguinte maneira:

$ ./title_getter.pl http://www.jake8us.org/~sam/multi-line.html
this is a \n title 
slm
fonte
11
Solução pura! :)
coffemug
3
Analisar HTML com expressões regulares não é tão simples. Tags escritas como "<TITLE>", "<title lang = en>", "<title \ n>" não corresponderão à sua expressão. Problema ainda maior, nem "<title> \ noops \ n </title>" será.
manatwork
4
Tentar analisar html usando regex tende a ser desaprovado aqui.
precisa saber é o seguinte
11
@ SLM, <title>Unix\nLinux</title>é para ser Unix Linux, não UnixLinux.
Stéphane Chazelas
11
+1 Para ruby ​​+ nokogiri. Eu usei para todos os tipos de raspagem na web, é incrível!
Rob
7

Usar regex simples para analisar HTML é ingênuo. Por exemplo, com novas linhas e ignorando a codificação de caracteres especiais especificada no arquivo. Faça a coisa certa e realmente analise a página usando qualquer um dos outros analisadores reais mencionados nas outras respostas ou use o seguinte liner:

python -c "import bs4, urllib2; print bs4.BeautifulSoup(urllib2.urlopen('http://www.crummy.com/software/BeautifulSoup/bs4/doc/')).title.text"

(O exemplo acima inclui um caractere Unicode).

O BeautifulSoup também lida com um monte de HTML incorreto (por exemplo, falta de tags de fechamento), o que geraria completamente um regexing simplista. Você pode instalá-lo em um python padrão usando:

pip install beautifulsoup4

ou se você não tiver pip, com

easy_install beautifulsoup4

Alguns sistemas operacionais como o Debian / Ubuntu também o têm ( python-bs4pacote no Debian / Ubuntu).

Zelda
fonte
2
bs4não está na biblioteca padrão do python. Você precisa instalá-lo usando easy_install beautfulsoup4(não easyinstall bs4).
Anthon
@Anthon incluiu suas informações
Zelda
5

Talvez esteja "trapaceando", mas uma opção é pup, um analisador de HTML de linha de comando .

Aqui estão duas maneiras de fazer isso:

Usando o metacampo com property="og:titleatributo

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'meta[property=og:title] attr{content}'
Why Are Bad Words Bad?

e outra maneira de usar o titlecampo diretamente (e depois cortar a - YouTubestring no final).

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'title text{}' | sed 's/ - YouTube$//'
Why Are Bad Words Bad?
abetusk
fonte
Para evitar entidades de personagem, os usuários podem querer usar a --plainopção de filhote .
peak
3

Parece ser possível com lynxeste truque ( zsh, bashsintaxe):

lynx -cfg=<(printf '%s\n' 'PRINTER:P:printf "%0s\\n" "$LYNX_PRINT_TITLE">&3:TRUE'
  ) lynx 3>&1 > /dev/null -nopause -noprint -accept_all_cookies -cmd_script <(
    printf '%s\n' "key p" "key Select key" "key ^J" exit
  ) 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc'

Por ser um navegador da vida real, ele não sofre muitas das limitações mencionadas na minha outra resposta .

Aqui, estamos usando o fato de que lynxdefine a $LYNX_PRINT_TITLEvariável de ambiente como o título da página atual ao imprimir a página.

Acima, estamos fornecendo um arquivo de configuração (como um canal) que define uma "impressora" do lynx chamada Pque apenas gera o conteúdo dessa variável para o descritor de arquivos 3(esse descritor de arquivo é redirecionado para lynxo stdout com 3>&1enquanto o lynx stdout é redirecionado para / dev / null).

Em seguida, usamos lynxo recurso de script para simular o pressionamento do usuário pe End(aka select) e Enter( ^J).

-accept_all_cookies caso contrário, o lynx solicitaria ao usuário a confirmação de cada cookie.

Stéphane Chazelas
fonte
3

Maneira simples:

curl -s example.com | grep -o "<title>[^<]*" | tail -c+8

Poucas alternativas:

curl -s example.com | grep -o "<title>[^<]*" | cut -d'>' -f2-
wget -qO- example.com | grep -o "<title>[^<]*" | sed -e 's/<[^>]*>//g'
kenorb
fonte
11
Estes são os únicos que funcionaram para mim!
Ahmad Awais
1

Gostei da ideia de Stéphane Chazelas de usar Lynx e LYNX_PRINT_TITLE, mas esse script não funcionou para mim no Ubuntu 14.04.5.

Fiz uma versão simplificada usando o Lynx e arquivos pré-configurados com antecedência.

Adicione a seguinte linha ao /etc/lynx-cur/lynx.cfg (ou onde quer que seu lynx.cfg esteja):

PRINTER:P:printenv LYNX_PRINT_TITLE>/home/account/title.txt:TRUE:1000

Esta linha instrui a salvar o título, durante a impressão, em "/home/account/title.txt" - você pode escolher qualquer nome de arquivo que desejar. Você solicita MUITAS páginas grandes, aumenta o valor acima de "1000" para qualquer número de linhas por página que desejar, caso contrário, o Lynx fará um prompt adicional "ao imprimir documentos contendo um número muito grande de páginas".

Em seguida, crie o arquivo /home/account/lynx-script.txt com o seguinte conteúdo:

key p
key Select key
key ^J
exit

Em seguida, execute o Lynx usando as seguintes opções de linha de comando:

lynx -term=vt100 -display_charset=utf-8 -nopause -noprint -accept_all_cookies -cmd_script=/home/account/lynx-script.txt "http://www.youtube.com/watch?v=Dd7dQh8u4Hc" >/dev/nul

Após a conclusão deste comando, o arquivo /home/account/title.txt será criado com o título da sua página.

Para encurtar a história, aqui está uma função PHP que retorna um título de página com base no URL fornecido ou falso em caso de erro.

function GetUrlTitle($url)
{
  $title_file_name = "/home/account/title.txt";
  if (file_exists($title_file_name)) unlink($title_file_name); // delete the file if exists
  $cmd = '/usr/bin/lynx -cfg=/etc/lynx-cur/lynx.cfg -term=vt100 -display_charset=utf-8 -nopause -noprint -accept_all_cookies -cmd_script=/home/account/lynx-script.txt "'.$url.'"';
  exec($cmd, $output, $retval);
  if (file_exists($title_file_name))
  {
    $title = file_get_contents($title_file_name);
    unlink($title_file_name); // delete the file after reading
    return $title;
  } else
  {
    return false;
  }
}

print GetUrlTitle("http://www.youtube.com/watch?v=Dd7dQh8u4Hc");
Maxim Masiutin
fonte
0

Usando nokogiri, é possível usar uma consulta simples baseada em CSS para extrair o texto interno da tag:

 $ nokogiri -e 'puts $_.at_css("title").content'
 Why Are Bad Words Bad? - YouTube

Da mesma forma, para extrair o valor do atributo "content" da tag:

$ nokogiri -e 'puts $_.at_css("meta[name=title]").attr("content")'
Why Are Bad Words Bad?
pico
fonte