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.
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.
Respostas:
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 recode
se houver coisas como <
:
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 - youtube
peç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:
Não há comando padrão / portátil para fazer consultas HTTP. Algumas décadas atrás, eu recomendaria lynx -source
aqui. 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 GET
comando que vem com perl
a 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
...
wget
pode não ter a mesma página que a que, por exemplo, firefox
seria 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 wget
comportamento para alterar a maneira como ele envia a solicitação, com opções.
Os mais importantes aqui a esse respeito são:
Accept
e Accept-language
: informa ao servidor em que idioma e conjunto de caracteres o cliente gostaria de receber a resposta. wget
não envia nenhum por padrão; portanto, o servidor normalmente envia com suas configurações padrão. firefox
do 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. wget
não vou.wget
seguirá 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/">
.
Aqui, por preguiça, perl
lemos 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 awk
estiver 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 </title
e, ao sair, faz wget
com que o download seja interrompido.
Aqui, wget
escreve a página enquanto a baixa. Ao mesmo tempo, perl
inverte 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 html
ausentes 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)
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 & 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.
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 é
. 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 é
, como é
, 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 é
ou é
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'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.
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).
<
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.
Você também pode tentar hxselect
(do HTML-XML-Utils ) com wget
o seguinte:
wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' | hxselect -s '\n' -c 'title' 2>/dev/null
Você pode instalar hxselect
em 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)}'
.
sudo apt-get install html-xml-utils
hxselect
.
Você também pode usar curl
e grep
fazer isso. Você vai precisar de recorrer a utilização do PCRE (Perl Compatible Regular Expressions) em grep
para obter a aparência trás e instalações à frente olhar para que possamos encontrar as <title>...</title>
tags.
$ curl 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -so - | \
grep -iPo '(?<=<title>)(.*)(?=</title>)'
Why Are Bad Words Bad? - YouTube
Os curl
comutadores:
-s
= silencioso-o -
= envia a saída para STDOUTOs grep
comutadores:
-i
= insensibilidade ao caso-o
= Retorna apenas a parte que corresponde-P
= Modo PCREO 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>
.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 \n
caractere, ou seja tr -d '\n'
.
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
Se o <title>
conjunto estiver definido assim, <title lang="en">
você precisará removê-lo antes de grep
inseri-lo. A ferramenta sed
pode 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.
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 curl
HTML ( Nokogiri::HTML
). O método xpath
procura 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 puts
seguida, os imprime.
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
<title>Unix\nLinux</title>
é para ser Unix Linux
, não UnixLinux
.
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-bs4
pacote no Debian / Ubuntu).
bs4
não está na biblioteca padrão do python. Você precisa instalá-lo usando easy_install beautfulsoup4
(não easyinstall bs4
).
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 meta
campo com property="og:title
atributo
$ 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 title
campo diretamente (e depois cortar a - YouTube
string no final).
$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'title text{}' | sed 's/ - YouTube$//'
Why Are Bad Words Bad?
--plain
opção de filhote .
Parece ser possível com lynx
este truque ( zsh
, bash
sintaxe):
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 lynx
define a $LYNX_PRINT_TITLE
variá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 P
que apenas gera o conteúdo dessa variável para o descritor de arquivos 3
(esse descritor de arquivo é redirecionado para lynx
o stdout com 3>&1
enquanto o lynx stdout é redirecionado para / dev / null).
Em seguida, usamos lynx
o recurso de script para simular o pressionamento do usuário p
e End
(aka select) e Enter
( ^J
).
-accept_all_cookies
caso contrário, o lynx solicitaria ao usuário a confirmação de cada cookie.
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'
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");
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?