¿Cómo obtengo el título de un sitio web usando la línea de comando?

50

Quiero un programa de línea de comando que imprima el título de un sitio web. Por ejemplo:

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

debería dar:

Why Are Bad Words Bad? 

Le das la url e imprime el Título.

Ufoguy
fuente
2
Cuando descargo ese título me sale: "¿Por qué son malas las palabras malas? - Youtube", ¿quieres que también se trunca el "- Youtube"?
slm

Respuestas:

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

Puede canalizarlo a GNU recodesi hay cosas como &lt;en él:

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

Para quitar la - youtubeparte:

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

Para señalar algunas de las limitaciones:

portabilidad

No hay un comando estándar / portátil para hacer consultas HTTP. Hace unas décadas, hubiera recomendado lynx -sourceaquí. Pero hoy en día, wgetes más portátil, ya que se puede encontrar por defecto en la mayoría de los sistemas GNU (incluidos la mayoría de los sistemas operativos de escritorio / portátiles basados ​​en Linux). Otros que son bastante portátiles incluyen el GETcomando que viene con perlla libwww que a menudo se instala lynx -source, y en menor medida curl. Otros comunes los incluyen links -source, elinks -source, w3m -dump_source, lftp -c cat...

Protocolo HTTP y manejo de redireccionamiento

wgetes posible que no obtenga la misma página que la que, por ejemplo, firefoxse mostrará. La razón es que los servidores HTTP pueden elegir enviar una página diferente en función de la información proporcionada en la solicitud enviada por el cliente.

La solicitud enviada por wget / w3m / GET ... será diferente de la enviada por firefox. Si eso es un problema, puede modificar el wgetcomportamiento para cambiar la forma en que envía la solicitud, aunque con opciones.

Los más importantes aquí a este respecto son:

  • Accepty Accept-language: eso le dice al servidor en qué idioma y conjunto de caracteres le gustaría al cliente obtener la respuesta. wgetno envía ninguno de manera predeterminada, por lo que el servidor generalmente enviará con su configuración predeterminada. firefoxen el otro extremo es probable que esté configurado para solicitar su idioma.
  • User-Agent: que identifica la aplicación del cliente para el servidor. Algunos sitios envían contenido diferente basado en el cliente (aunque eso es principalmente por diferencias entre las interpretaciones del lenguaje javascript) y pueden negarse a servirle si está utilizando un agente de usuario de tipo robotwget .
  • Cookie: si ha visitado este sitio anteriormente, su navegador puede tener cookies permanentes. wgetNo lo haré.

wgetseguirá las redirecciones cuando se realicen en el nivel de protocolo HTTP, pero dado que no analiza el contenido de la página, no las realizadas por javascript o cosas por el estilo <meta http-equiv="refresh" content="0; url=http://example.com/">.

Eficiencia en el desempeño

Aquí, por flojera, hemos perlleído todo el contenido en la memoria antes de comenzar a buscar la <title>etiqueta. Dado que el título se encuentra en la <head>sección que se encuentra en los primeros bytes del archivo, eso no es óptimo. Un mejor enfoque, si GNU awkestá disponible en su sistema podría ser:

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

De esa manera, awk deja de leer después de la primera </titley, al salir, hace wgetque se detenga la descarga.

Análisis del HTML

Aquí, wgetescribe la página a medida que la descarga. Al mismo tiempo, perlsorbe su salida ( -0777 -n) completa en la memoria y luego imprime el código HTML que se encuentra entre las primeras apariciones de <title...>y </title.

Eso funcionará para la mayoría de las páginas HTML que tienen una <title>etiqueta, pero hay casos en los que no funcionará.

Por el contrario, la solución de coffeeMug analizará la página HTML como XML y devolverá el valor correspondiente para title. Es más correcto si se garantiza que la página sea XML válido . Sin embargo, no se requiere que HTML sea XML válido (las versiones anteriores del lenguaje no lo eran), y debido a que la mayoría de los navegadores son indulgentes y aceptarán códigos HTML incorrectos, incluso hay muchos códigos HTML incorrectos.

Tanto mi solución como coffeeMug fallarán en una variedad de casos de esquina, a veces lo mismo, a veces no.

Por ejemplo, el mío fallará en:

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

o:

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

Mientras que su fallará:

<TITLE>foo</TITLE>

(html válido, no xml) o:

o:

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

(de nuevo, válido html, faltan <![CDATA[partes para que sea válido XML).

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

(HTML incorrecto, pero aún se encuentra allí y es compatible con la mayoría de los navegadores)

interpretación del código dentro de las etiquetas.

Esa solución genera el texto sin formato entre <title>y </title>. Normalmente, no debe haber ninguna etiqueta HTML allí, posiblemente puede haber comentarios (aunque algunos navegadores como Firefox no los manejan, por lo que es muy poco probable). Todavía puede haber algo de codificación 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

De lo que se ocupa 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

Pero un cliente web también está destinado a hacer más transformaciones en ese código al mostrar el título (como condensar algunos de los espacios en blanco, eliminar los iniciales y finales). Sin embargo, es poco probable que sea necesario. Entonces, como en los otros casos, depende de usted decidir si vale la pena el esfuerzo.

Conjunto de caracteres

Antes de UTF-8, iso8859-1 solía ser el juego de caracteres preferido en la web para caracteres que no son ASCII, aunque estrictamente hablando tenían que escribirse como &eacute;. Las versiones más recientes de HTTP y el lenguaje HTML han agregado la posibilidad de especificar el conjunto de caracteres en los encabezados HTTP o en los encabezados HTML, y un cliente puede especificar los conjuntos de caracteres que acepta. UTF-8 tiende a ser el juego de caracteres predeterminado en la actualidad.

Entonces, eso significa que, por ahí, encontrará éescrito como &eacute;, como &#233;, como UTF-8 é, (0xc3 0xa9), como iso-8859-1 (0xe9), con los 2 últimos, a veces la información en el juego de caracteres en los encabezados HTTP o los encabezados HTML (en diferentes formatos), a veces no.

wget solo obtiene los bytes sin procesar, no le importa su significado como caracteres y no le dice al servidor web sobre el juego de caracteres preferido.

recode html..se encargará de convertir el &eacute;o &#233;en la secuencia adecuada de bytes para el conjunto de caracteres utilizado en su sistema, pero para el resto, eso es más complicado.

Si el conjunto de caracteres de su sistema es utf-8, es probable que esté bien la mayor parte del tiempo, ya que tiende a ser el conjunto de caracteres predeterminado que se utiliza actualmente.

$ 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

Eso de éarriba era un UTF-8 é.

Pero si quieres cubrir otros charsets, una vez más, habrá que cuidarlo.

También debe tenerse en cuenta que esta solución no funcionará en absoluto para las páginas codificadas UTF-16 o UTF-32.

Para resumir

Idealmente, lo que necesita aquí es un navegador web real para brindarle la información. Es decir, necesita algo para hacer la solicitud HTTP con los parámetros adecuados, interpretar la respuesta HTTP correctamente, interpretar completamente el código HTML como lo haría un navegador y devolver el título.

Como no creo que se pueda hacer en la línea de comandos con los navegadores que conozco (aunque ahora vea este trucolynx ), debe recurrir a la heurística y las aproximaciones, y la anterior es tan buena como cualquiera.

También es posible que desee tener en cuenta el rendimiento, la seguridad ... Por ejemplo, para cubrir todos los casos (por ejemplo, una página web que tiene algunos javascript extraídos de un sitio de terceros que establece el título o redirige a otra página en un onload hook), puede que tenga que implementar un navegador de la vida real con sus motores dom y javascript que pueden tener que hacer cientos de consultas para una sola página HTML, algunas de las cuales intentan explotar vulnerabilidades ...

Si bien el uso de expresiones regulares para analizar HTML a menudo está mal visto , este es un caso típico en el que es lo suficientemente bueno para la tarea (IMO).

rev. Stéphane Chazelas
fuente
¿Descarga también las imágenes de las páginas? ¿También dejará archivos html basura?
Ufoguy
2
Probablemente desee terminar el título en primera instancia, <ya que no se garantiza que los títulos tengan etiquetas finales y cualquier otra etiqueta debería forzar su terminación. También es posible que desee quitar nuevas líneas.
Brian Nickel
1
No se recomienda utilizar expresiones regulares para analizar HTML. Siempre. Ni siquiera en este caso. Es un mal habito. Utilice un analizador real en su lugar. Hay una famosa respuesta humorística de Stackoverflow sobre esto ...
Robin Green
44
@RobinGreen Esa publicación fue sobre el uso de expresiones regulares para analizar un lenguaje no regular. Hay advertencias, pero este es un problema que se reduce fácilmente a un idioma normal. Recomiendo usar regex para analizar HTML. A veces. En este caso.
Brian Nickel
2
Y el número de expresiones regulares que funcionan para casi todo es aproximadamente 0.
Robin Green
27

También puede probar hxselect(desde HTML-XML-Utils ) con wgetlo siguiente:

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

Se puede instalar hxselecten distribuciones basadas en Debian usando:
sudo apt-get install html-xml-utils.

La redirección de STDERR es para evitar el Input is not well-formed. (Maybe try normalize?)mensaje.

Para deshacerse de "- YouTube", canalice la salida del comando anterior a awk '{print substr($0, 0, length($0)-10)}'.

taza de cafe
fuente
"hxselect" no parece estar instalado en Ubuntu por defecto. Incluso no puedo encontrarlo en mis repositorios existentes. ¿Como lo instalo?
Ufoguy 01 de
77
sudo apt-get install html-xml-utils
coffeMug 01 de
Recibo este error en Ubuntu 12.10 "La entrada no está bien formada. (¿Quizás intente normalizar?)"
slm
1
No he encontrado qué hacer con el msg. sobre normalizar la salida. No hay tal encendido hxselect.
slm
1
Para la gente de Mac OS X, Homebrew tiene una fórmula con hxselect. Instalar con brew install html-xml-utils.
Sukima
18

También puede usar curly greppara hacer esto. Tendrá que recurrir a la utilización de PCRE (Perl Compatible Regular Expressions) en grepconseguir la mirada detrás de las instalaciones y mirar hacia adelante para que podamos encontrar las <title>...</title>etiquetas.

Ejemplo

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

Detalles

Los curlinterruptores:

  • -s = silencioso
  • -o - = enviar salida a STDOUT

Los grepinterruptores:

  • -i = insensibilidad a mayúsculas y minúsculas
  • -o = Devuelve solo la porción que coincide
  • -P = Modo PCRE

El patrón para grep:

  • (?<=<title>) = busca una cadena que comience con esto a la izquierda de ella
  • (?=</title>) = busca una cadena que termine con esto a la derecha
  • (.*)= todo en el medio <title>..</title>.

Situaciones más complejas

Si <title>...</titie>abarca varias líneas, entonces lo anterior no lo encontrará. Puede mitigar esta situación utilizando tr, para eliminar cualquier \ncarácter, es decir tr -d '\n'.

Ejemplo

Archivo de muestra

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

Y una muestra de ejecución:

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

lang = ...

Si <title>se configura así, <title lang="en">entonces deberá eliminarlo antes de grepusarlo. La herramienta sedse puede usar para hacer esto:

$ 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

Lo anterior encuentra la cadena que no distingue entre mayúsculas y minúsculas lang=seguida de una secuencia de palabras ( \w+). Luego es despojado.

Un analizador HTML / XML real - usando Ruby

En algún momento, la expresión regular fallará al resolver este tipo de problema. Si eso ocurre, es probable que desee utilizar un analizador HTML / XML real. Uno de esos analizadores es Nokogiri . Está disponible en Ruby as a Gem y se puede usar así:

$ 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

Lo anterior está analizando los datos que vienen a través de curlcomo HTML ( Nokogiri::HTML). El método xpathluego busca nodos (etiquetas) en el HTML que son nodos hoja, ( //) con el nombre title. Para cada encontrado, queremos devolver su contenido ( e.content). El putsluego los imprime.

Un analizador HTML / XML real - usando Perl

También puede hacer algo similar con Perl y el 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";

Luego puede ejecutar este script de la siguiente manera:

$ ./title_getter.pl http://www.jake8us.org/~sam/multi-line.html
this is a \n title 
slm
fuente
1
Solución ordenada! :)
coffeMug 01 de
3
Analizar HTML con expresiones regulares no es tan simple. Las etiquetas escritas como "<TITLE>", "<title lang = es>", "<title \ n>" no coincidirán con su expresión. Un problema aún mayor, ni "<title> \ noops \ n </title>" lo será.
manatwork el
44
Intentar analizar html usando regex tiende a ser mal visto por aquí.
user3490
1
@slm, <title>Unix\nLinux</title>está destinado a ser Unix Linux, no UnixLinux.
Stéphane Chazelas
1
+1 Para rubí + nokogiri. Lo he usado para todo tipo de raspado web, ¡es increíble!
Rob
7

Usar expresiones regulares simples para analizar HTML es ingenuo. Por ejemplo, con líneas nuevas e ignorando la codificación de caracteres especiales especificada en el archivo. Haga lo correcto y analice realmente la página utilizando cualquiera de los otros analizadores reales mencionados en las otras respuestas o utilice el siguiente delineador:

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

(Lo anterior incluye un carácter Unicode).

BeautifulSoup también maneja una gran cantidad de HTML incorrecto (por ejemplo, faltan etiquetas de cierre), que arrojaría por completo expresiones regulares simplistas. Puede instalarlo en una python estándar usando:

pip install beautifulsoup4

o si no tienes pip, con

easy_install beautifulsoup4

Algunos sistemas operativos como Debian / Ubuntu también lo tienen empaquetado ( python-bs4paquete en Debian / Ubuntu).

Zelda
fuente
2
bs4no está en la biblioteca estándar de python. Tienes que instalarlo usando easy_install beautfulsoup4(no easyinstall bs4).
Anthon
@Anthon incluyó su información
Zelda
5

Tal vez sea "trampa", pero una opción es pup, un analizador HTML de línea de comandos .

Aquí hay dos formas de hacerlo:

Usando el metacampo con 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?

y otra forma de usar el titlecampo directamente (y luego cortar la - YouTubecadena al final).

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'title text{}' | sed 's/ - YouTube$//'
Why Are Bad Words Bad?
abetusk
fuente
Para evitar entidades de personajes, los usuarios pueden desear usar la --plainopción de cachorro .
pico
3

Parece posible con el lynxuso de este truco ( zsh, bashsintaxis):

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'

Debido a que es un navegador web de la vida real, no sufre muchas de las limitaciones que menciono en mi otra respuesta .

Aquí, estamos usando el hecho de que lynxestablece la $LYNX_PRINT_TITLEvariable de entorno al título de la página actual al imprimir la página.

Por encima, estamos dando un archivo de configuración (como un tubo) que define una "impresora" lince llamada Pque simplemente da salida al contenido de esa variable de descriptor de archivo 3(que descriptor de archivo se redirige a lynx's stdout con 3>&1mientras que la salida estándar lince es en sí redirigidos a / dev / null).

Luego usamos la función de lynxsecuencias de comandos para simular que el usuario presiona p, y End(también conocido como select) y Enter( ^J).

-accept_all_cookies de lo contrario, Lynx solicitaría al usuario la confirmación de cada cookie.

Stéphane Chazelas
fuente
3

Manera simple:

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

Pocas alternativas:

curl -s example.com | grep -o "<title>[^<]*" | cut -d'>' -f2-
wget -qO- example.com | grep -o "<title>[^<]*" | sed -e 's/<[^>]*>//g'
kenorb
fuente
1
¡Estos son los únicos que me funcionaron!
Ahmad Awais
1

Me gustó la idea de Stéphane Chazelas de usar Lynx y LYNX_PRINT_TITLE, pero ese script no me funcionó en Ubuntu 14.04.5.

He creado una versión simplificada usando Lynx y archivos preconfigurados de antemano.

Agregue la siguiente línea a /etc/lynx-cur/lynx.cfg (o donde sea que resida su lynx.cfg):

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

Esta línea le indica que guarde el título, mientras imprime, en "/home/account/title.txt"; puede elegir el nombre de archivo que desee. Solicita páginas MUY grandes, aumente el valor anterior de "1000" a cualquier número de líneas por página que desee, de lo contrario Lynx hará un mensaje adicional "al imprimir documentos que contengan un número muy grande de páginas".

Luego cree el archivo /home/account/lynx-script.txt con el siguiente contenido:

key p
key Select key
key ^J
exit

Luego ejecute Lynx usando las siguientes opciones de línea de comandos:

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

Al completar este comando, el archivo /home/account/title.txt se creará con el título de su página.

Para resumir, aquí hay una función PHP que devuelve un título de página basado en la URL dada, o falso en caso de error.

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
fuente
0

Usando nokogiri, uno puede usar una consulta simple basada en CSS para extraer el texto interno de la etiqueta:

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

Del mismo modo, para extraer el valor del atributo "contenido" de la etiqueta:

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