web-dev-qa-db-fra.com

Comment obtenir un titre de site Web en utilisant la ligne de commande?

Je veux un programme en ligne de commande qui imprime le titre d'un site Web. Par exemple:

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

devrait donner:

Why Are Bad Words Bad? 

Vous lui donnez l'URL et il imprime le titre.

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

Vous pouvez le diriger vers GNU recode s'il contient des éléments comme &lt;:

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

Pour supprimer la partie - youtube:

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

Pour souligner certaines des limitations:

portabilité

Il n'y a pas de commande standard/portable pour effectuer des requêtes HTTP. Il y a quelques décennies, j'aurais plutôt recommandé lynx -source Ici. Mais de nos jours, wget est plus portable car il peut être trouvé par défaut sur la plupart des systèmes GNU (y compris la plupart des systèmes d'exploitation pour ordinateurs de bureau/portables basés sur Linux). D'autres systèmes assez portables incluent la commande GET fournie avec le libwww de Perl qui est souvent installé, lynx -source, et dans une moindre mesure curl. Autre commun ceux-ci incluent links -source, elinks -source, w3m -dump_source, lftp -c cat ...

Protocole HTTP et gestion de la redirection

wget peut ne pas obtenir la même page que celle que par exemple firefox afficherait. La raison étant que les serveurs HTTP peuvent choisir d'envoyer une page différente en fonction des informations fournies dans la demande envoyée par le client.

La requête envoyée par wget/w3m/GET ... va être différente de celle envoyée par firefox. Si c'est un problème, vous pouvez modifier le comportement de wget pour changer la façon dont il envoie la demande avec des options.

Les plus importants ici à cet égard sont:

  • Accept et Accept-language: qui indique au serveur dans quelle langue et quel jeu de caractères le client souhaite obtenir la réponse. wget n'en envoie pas par défaut, donc le serveur envoyer généralement avec ses paramètres par défaut. firefox de l'autre côté est probablement configuré pour demander votre langue.
  • User-Agent: Qui identifie l'application cliente sur le serveur. Certains sites envoient un contenu différent en fonction du client (bien que ce soit principalement pour les différences entre les interprétations du langage javascript) et peuvent refuser de vous servir si vous utilisez un robot - type agent utilisateur comme wget.
  • Cookie: si vous avez déjà visité ce site, votre navigateur peut avoir des cookies permanents pour celui-ci. wget ne le sera pas.

wget suivra les redirections lorsqu'elles seront effectuées au niveau du protocole HTTP, mais comme il ne regarde pas le contenu de la page, pas celles effectuées par javascript ou des choses comme <meta http-equiv="refresh" content="0; url=http://example.com/">.

Performance/efficacité

Ici, par paresse, nous avons Perl lu tout le contenu en mémoire avant de commencer à chercher la balise <title>. Étant donné que le titre se trouve dans la section <head> Qui se trouve dans les premiers octets du fichier, ce n'est pas optimal. Une meilleure approche, si GNU awk est disponible sur votre système pourrait être:

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

De cette façon, awk arrête la lecture après le premier </title, Et en quittant, wget arrête le téléchargement.

Analyse du HTML

Ici, wget écrit la page lors du téléchargement. Dans le même temps, Perl, ralentit sa sortie (-0777 -n) Entière en mémoire, puis imprime le code HTML qui se trouve entre les premières occurrences de <title...> Et </title.

Cela fonctionnera pour la plupart des pages HTML qui ont une balise <title>, Mais il y a des cas où cela ne fonctionnera pas.

En revanche la solution de coffeeMug analysera la page HTML en XML et renverra la valeur correspondante pour title. Il est plus correct si la page est garantie d'être XML valide. Cependant, il n'est pas nécessaire que HTML soit un XML valide (les anciennes versions de la langue ne l'étaient pas), et parce que la plupart des navigateurs sont indulgents et acceptent du code HTML incorrect, il y a même beaucoup de code HTML incorrect.

Ma solution et coffeeMug's échoueront pour une variété de cas d'angle, parfois les mêmes, parfois non.

Par exemple, le mien échouera sur:

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

ou:

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

Alors que sa volonté échouera:

<TITLE>foo</TITLE>

(html valide, pas xml) ou:

ou:

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

(encore une fois, html valides, parties <![CDATA[ manquantes pour rendre XML valide).

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

(HTML incorrect, mais toujours trouvé et pris en charge par la plupart des navigateurs)

interprétation du code à l'intérieur des balises.

Cette solution génère le texte brut entre <title> Et </title>. Normalement, il ne devrait pas y avoir de balises HTML, il peut éventuellement y avoir des commentaires (bien que non géré par certains navigateurs comme firefox donc très peu probable). Il peut toujours y avoir du codage 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

Ce qui est pris en charge par 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

Mais un client Web est également censé effectuer plus de transformations sur ce code lors de l'affichage du titre (comme condenser certains des blancs, supprimer les premiers et les derniers). Cependant, il est peu probable que cela soit nécessaire. Donc, comme dans les autres cas, c'est à vous de décider si cela en vaut la peine.

Character set

Avant UTF-8, iso8859-1 était le jeu de caractères préféré sur le Web pour les caractères non ASCII, mais à proprement parler, ils devaient être écrits comme &eacute;. Des versions plus récentes de HTTP et du langage HTML ont ajouté la possibilité de spécifier le jeu de caractères dans les en-têtes HTTP ou dans les en-têtes HTML, et un client peut spécifier les jeux de caractères qu'il accepte. UTF-8 a tendance à être le jeu de caractères par défaut de nos jours.

Donc, cela signifie que là-bas, vous trouverez é Écrit comme &eacute;, Comme &#233;, Comme UTF-8 é, (0xc3 0xa9) , comme iso-8859-1 (0xe9), avec pour les 2 derniers, parfois les informations sur le jeu de caractères dans les en-têtes HTTP ou les en-têtes HTML (dans différents formats), parfois non.

wget n'obtient que les octets bruts, il ne se soucie pas de leur signification en tant que caractères et n'indique pas au serveur Web le jeu de caractères préféré.

recode html.. Veillera à convertir le &eacute; Ou &#233; En la séquence d'octets appropriée pour le jeu de caractères utilisé sur votre système, mais pour le reste, c'est plus délicat.

Si le jeu de caractères de votre système est utf-8, il y a de fortes chances que ce soit correct la plupart du temps car il s'agit généralement du jeu de caractères par défaut utilisé de nos jours.

$ 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

Ce é Ci-dessus était un UTF-8 é.

Mais si vous voulez couvrir d'autres jeux de caractères, encore une fois, il faudrait en prendre soin.

Il convient également de noter que cette solution ne fonctionnera pas du tout pour les pages encodées UTF-16 ou UTF-32.

Pour résumer

Idéalement, ce dont vous avez besoin ici, c'est d'un véritable navigateur Web pour vous donner les informations. Autrement dit, vous avez besoin de quelque chose pour effectuer la requête HTTP avec les paramètres appropriés, interpréter correctement la réponse HTTP, interpréter complètement le code HTML comme le ferait un navigateur et renvoyer le titre.

Comme je ne pense pas que cela puisse être fait sur la ligne de commande avec les navigateurs que je connais (bien que voir maintenant cette astuce avec lynx ), vous devez recourir à des heuristiques et des approximations, et celui ci-dessus est aussi bon que tout.

Vous pouvez également prendre en considération les performances, la sécurité ... Par exemple, pour couvrir tous les cas (par exemple, une page Web qui a du javascript extrait d'un site tiers qui définit le titre ou redirige vers une autre page dans un onload hook), vous devrez peut-être implémenter un navigateur réel avec ses moteurs dom et javascript qui devront peut-être effectuer des centaines de requêtes pour une seule page HTML, dont certaines tentent d'exploiter les vulnérabilités ...

Alors que tiliser des expressions rationnelles pour analyser HTML est souvent mal v , voici un cas typique où il est assez bon pour la tâche (IMO).

47
Stéphane Chazelas

Vous pouvez également essayer hxselect (de HTML-XML-Utils ) avec wget comme suit:

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

Vous pouvez installer hxselect dans les distributions basées sur Debian en utilisant:
Sudo apt-get install html-xml-utils.

La redirection STDERR consiste à éviter le message Input is not well-formed. (Maybe try normalize?).

Afin de se débarrasser de "- YouTube", dirigez la sortie de la commande ci-dessus vers awk '{print substr($0, 0, length($0)-10)}'.

28
coffeMug

Vous pouvez également utiliser curl et grep pour ce faire. Vous aurez besoin d'enrôler l'utilisation de PCRE (expressions régulières compatibles avec Perl) dans grep pour obtenir le look derrière et regarder vers l'avant afin que nous puissions trouver le <title>...</title> Mots clés.

Exemple

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

Détails

Les commutateurs curl:

  • -s = silencieux
  • -o - = envoyer la sortie à STDOUT

Les commutateurs grep:

  • -i = insensibilité à la casse
  • -o = Renvoyer uniquement la partie qui correspond
  • -P = Mode PCRE

Le modèle à grep:

  • (?<=<title>) = recherchez une chaîne commençant par celle-ci à sa gauche
  • (?=</title>) = recherchez une chaîne qui se termine par ceci à sa droite
  • (.*) = tout le reste <title>..</title>.

Situations plus complexes

Si <title>...</titie> s'étend sur plusieurs lignes, alors ce qui précède ne le trouvera pas. Vous pouvez atténuer cette situation en utilisant tr, pour supprimer tout \n caractères, c'est-à-dire tr -d '\n'.

Exemple

Exemple de fichier.

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

Et un exemple d'exécution:

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

lang = ...

Si la <title> est défini comme ceci, <title lang="en"> alors vous devrez le supprimer avant de greper. L'outil sed peut être utilisé pour cela:

$ 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

Ce qui précède trouve la chaîne insensible à la casse lang= suivi d'une séquence de mots (\w+). Il est ensuite retiré.

Un véritable analyseur HTML/XML - utilisant Ruby

À un certain point, l'expression régulière ne parviendra pas à résoudre ce type de problème. Si cela se produit, vous voudrez probablement utiliser un véritable analyseur HTML/XML. Un tel analyseur est Nokogiri . Il est disponible en Ruby comme gemme et peut être utilisé comme ceci:

$ 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

Ce qui précède analyse les données provenant du curl au format HTML (Nokogiri::HTML). La méthode xpath recherche ensuite des nœuds (balises) dans le HTML qui sont des nœuds feuilles, (//) avec le nom title. Pour chaque élément trouvé, nous voulons renvoyer son contenu (e.content). puts les imprime ensuite.

Un véritable analyseur HTML/XML - en utilisant Perl

Vous pouvez également faire quelque chose de similaire avec Perl et le module 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";

Vous pouvez ensuite exécuter ce script comme ceci:

$ ./title_getter.pl http://www.jake8us.org/~sam/multi-line.html
this is a \n title 
18
slm

Utiliser une expression rationnelle simple pour analyser HTML est naïf. Par exemple. avec des retours à la ligne et en ignorant l'encodage des caractères spéciaux spécifié dans le fichier. Faites la bonne chose et analysez vraiment la page en utilisant l'un des autres vrais analyseurs mentionnés dans les autres réponses ou utilisez le liner suivant:

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

(Ce qui précède comprend un caractère Unicode).

BeautifulSoup gère également beaucoup de code HTML incorrect (par exemple, des balises de fermeture manquantes), ce qui entraînerait complètement une expression rationnelle simpliste. Vous pouvez l'installer dans un standard python en utilisant:

pip install beautifulsoup4

ou si vous n'avez pas pip, avec

easy_install beautifulsoup4

Certains systèmes d'exploitation comme Debian/Ubuntu l'ont également empaqueté (python-bs4 package sur Debian/Ubuntu).

7
Zelda

C'est peut-être de la "triche" mais une option est pup, un analyseur HTML en ligne de commande .

Voici deux façons de procéder:

Utilisation du champ meta avec property="og:title attribut

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

et d'une autre manière en utilisant directement le champ title (puis en supprimant le - YouTube chaîne à la fin).

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'title text{}' | sed 's/ - YouTube$//'
Why Are Bad Words Bad?
6
abetusk

Cela semble possible avec lynx en utilisant cette astuce:

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

Parce que c'est un navigateur Web réel, il ne souffre pas de nombreuses limitations que je mentionne dans mon autre réponse .

Ici, nous utilisons le fait que lynx définit le $LYNX_PRINT_TITLE variable d'environnement au titre de la page en cours lors de l'impression de la page.

Ci-dessus, nous utilisons la fonction de script lynx (avec le script transmis à stdin via un document hérité) pour:

  1. définir une "imprimante" lynx appelée P qui sort simplement le contenu de cette variable dans le descripteur de fichier 3 (ce descripteur de fichier est redirigé vers la sortie standard de lynx avec 3>&1 tandis que lynx stdout est lui-même redirigé vers/dev/null).
  2. simuler l'utilisateur en appuyant sur p, et le End (alias Select), et Enter (^J).
4
Stéphane Chazelas

Manière simple:

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

Peu d'alternatives:

curl -s example.com | grep -o "<title>[^<]*" | cut -d'>' -f2-
wget -qO- example.com | grep -o "<title>[^<]*" | sed -e 's/<[^>]*>//g'
3
kenorb

J'ai aimé l'idée de Stéphane Chazelas d'utiliser Lynx et LYNX_PRINT_TITLE, mais ce script ne fonctionnait pas pour moi sous Ubuntu 14.04.5.

J'en ai fait une version simplifiée en utilisant Lynx et en utilisant des fichiers pré-configurés à l'avance.

Ajoutez la ligne suivante à /etc/lynx-cur/lynx.cfg (ou là où réside votre lynx.cfg):

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

Cette ligne indique d'enregistrer le titre, lors de l'impression, dans "/home/account/title.txt" - vous pouvez choisir le nom de fichier que vous souhaitez. Vous demandez de TRÈS grandes pages, augmentez la valeur ci-dessus de "1000" à n'importe quel nombre de lignes par page, sinon Lynx fera une invite supplémentaire "lors de l'impression d'un document contenant un très grand nombre de pages".

Créez ensuite le fichier /home/account/lynx-script.txt avec le contenu suivant:

key p
key Select key
key ^J
exit

Exécutez ensuite Lynx à l'aide des options de ligne de commande suivantes:

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

Une fois cette commande terminée, le fichier /home/account/title.txt sera créé avec le titre de votre page.

Pour faire court, voici une fonction PHP qui retourne un titre de page basé sur l'URL donnée, ou false en cas d'erreur.

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");
1
Maxim Masiutin

En utilisant nokogiri, on peut utiliser une simple requête basée sur CSS pour extraire le texte interne de la balise:

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

De même, pour extraire la valeur de l'attribut "content" de la balise:

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

Un exemple de python3 + beautifulsoup pourrait être

python3 -c "import bs4, requests; print(bs4.BeautifulSoup(requests.get('http://www.crummy.com/software/BeautifulSoup/bs4/doc/').content).title.text)"
0
Nik

Utilisation de xidel:

$ xidel -s http://www.youtube.com/watch?v=Dd7dQh8u4Hc --css title
Why Are Bad Words Bad? - YouTube

Si nécessaire, apt install xidel ou similaire.

0
JJoao