Comment get un titre de sites Web en utilisant la command line?

Je veux un programme de command line 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.

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

Vous pouvez le recopyr à GNU recode s'il y a des choses comme &lt; dedans:

 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 command standard / portable pour effectuer des requêtes HTTP. Il y a quelques décennies, j'aurais recommandé lynx -source ici. Mais de nos jours, wget est plus portable puisqu'il peut être trouvé par défaut sur la plupart des systèmes GNU (y compris la plupart des systèmes d'exploitation de bureau / portables basés sur Linux). D'autres commands relativement portables incluent la command GET fournie avec le libwww de perl qui est souvent installé, lynx -source , et dans une moindre mesure curl . D'autres communes incluent des links -source , elinks -source , w3m -dump_source , lftp -c cat

Protocole HTTP et gestion de la redirection

wget peut ne pas get la même page que celle que par exemple firefox afficherait. La raison en est que les servers HTTP peuvent choisir d'envoyer une page différente en fonction des informations fournies dans la request 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 modifier la façon dont il envoie la request avec des options.

Les plus importants ici à cet égard sont:

  • Accept et Accept-language : indique au server dans quelle langue et dans quel jeu le client souhaite recevoir la réponse. wget n'envoie pas par défaut, de sorte que le server enverra généralement ses parameters par défaut. firefox à l'autre extrémité est probablement configuré pour requestr votre langue.
  • User-Agent : identifie l'application cliente sur le server. Certains sites envoient un contenu différent basé sur le client (bien que ce soit principalement pour des différences entre les interprétations de langage javascript) et peuvent refuser de vous servir si vous utilisez un agent user de type robot comme wget .
  • Cookie : si vous avez déjà visité ce site, votre browser peut avoir des cookies permanents. wget ne sera pas.

wget suivra les redirections quand ils sont faits au niveau du protocole HTTP, mais comme il ne regarde pas le contenu de la page, pas ceux faits par javascript ou des choses comme <meta http-equiv="refresh" content="0; url=http://example.com/"> .

Performance / efficacité

Ici, par paresse, nous avons perl lire tout le contenu en memory avant de commencer à chercher la <title> . Étant donné que le titre se trouve dans la section <head> qui se trouve dans les premiers octets du file, 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 de lire après le premier </title , et en quittant, wget arrête de download.

Analyse du code HTML

Ici, wget écrit la page pendant qu'elle la télécharge. Dans le même time, perl , slurps sa sortie ( -0777 -n ) en memory, 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 <title> , mais il y a des cas où cela ne fonctionnera pas.

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

Ma solution et coffeeMug vont échouer pour une variété de cas de coin, parfois la même chose, parfois pas.

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 sur:

 <TITLE>foo</TITLE> 

(HTML valide, pas XML) ou:

ou:

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

(encore une fois, html valide, manquant <![CDATA[ parties pour le rendre XML valide].

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

(HTML incorrect, mais toujours trouvé là-bas et pris en charge par la plupart des browsers)

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

Cette solution génère le text brut entre <title> et </title> . Normalement, il ne devrait y avoir aucune balise HTML là-dedans, il y a peut-être des commentaires (mais pas traités par certains browsers comme firefox donc très peu probable). Il peut encore y avoir du encoding 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é faire plus de transformations sur ce code lors de l'affichage du titre (comme condenser certains des blancs, supprimer les principaux et les suivants). 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.

Jeu de caractères

Avant UTF-8, iso8859-1 était le jeu de caractères préféré sur le web pour les caractères non-ASCII, bien qu'à ssortingctement parler ils devaient être écrits comme &eacute; . Des versions plus récentes de HTTP et le 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 charsets 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 findez é é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 charset dans les en-têtes HTTP ou les en-têtes HTML ne pas.

wget ne reçoit que les octets bruts, il ne se soucie pas de leur signification en tant que caractères, et il ne dit pas le charset préféré au server Web.

recode html.. prendra soin de convertir le &eacute; ou &#233; dans la bonne séquence d'octets pour le jeu de caractères utilisé sur votre système, mais pour le rest, c'est plus compliqué.

Si votre charset système est utf-8, il y a de fortes chances qu'il soit correct la plupart du time car cela a tendance à être le 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 charsets, encore une fois, il faudrait en prendre soin.

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

Pour résumer

Idéalement, ce dont vous avez besoin ici, est un véritable browser Web pour vous donner les informations. C'est-à-dire que vous avez besoin de faire la requête HTTP avec les parameters appropriés, d'interpréter correctement la réponse HTTP, d'interpréter complètement le code HTML comme le ferait un browser et de returnner le titre.

Comme je ne pense pas que cela puisse être fait sur la command line avec les browsers que je connais (mais voyez maintenant cette astuce avec lynx ), vous devez recourir à des heuristiques et des approximations, et celle ci-dessus est aussi bonne que n'importe qui.

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

Bien que l' utilisation de regexps pour parsingr le HTML soit souvent mal vu , voici un cas typique où c'est assez bon pour la tâche (IMO).

Vous pouvez également essayer hxselect (à partir 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 dissortingbutions basées sur Debian en utilisant:
sudo apt-get install html-xml-utils .

La redirection STDERR est d'éviter que l' Input is not well-formed. (Maybe try normalize?) Input is not well-formed. (Maybe try normalize?) Message.

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

Vous pouvez également utiliser curl et grep pour le faire. Vous devrez faire appel à PCRE (Perl Compatible Regular Expressions) dans grep pour avoir le look derrière et explorer les possibilités afin que nous puissions find les balises <title>...</title> .

Exemple

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

Détails

Les curl :

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

Les commutateurs grep :

  • -i = insensibilité à la casse
  • -o = Ne renvoie que la partie qui correspond
  • -P = mode PCRE

Le model à grep :

  • (?<=<title>) = searchz une string commençant par celle-ci à gauche
  • (?=</title>) = searchz une string qui se termine par ceci à droite de celle-ci
  • (.*) = tout ce qui se trouve entre <title>..</title> .

Des situations plus complexes

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

Exemple

Exemple de file.

 $ 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 de course:

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

lang = …

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

 $ 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 string insensible à la casse lang= suivie d'une séquence de mots ( \w+ ). Il est ensuite dépouillé.

Un véritable parsingur HTML / XML – utilisant Ruby

À un certain point regex échouera dans la résolution de ce type de problème. Si cela se produit, vous souhaiterez probablement utiliser un parsingur HTML / XML réel. Un tel parsingur est Nokogiri . Il est disponible en Ruby comme un Gem 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 est l'parsing des données qui viennent par la curl comme HTML ( Nokogiri::HTML ). La méthode xpath search ensuite les nœuds (tags) dans le code HTML qui sont des nœuds feuille ( // ) avec le title du nom. Pour chaque trouvé nous voulons returnner son contenu ( e.content ). Les puts puis les imprime.

Un véritable parsingur HTML / XML – 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 

Utiliser une expression rationnelle simple pour parsingr le code HTML est naïf. Par exemple avec les returns à la ligne et en ignorant le encoding de caractères spéciaux spécifié dans le file. Faites la bonne chose et parsingz vraiment la page en utilisant l'un des autres parsingurs réels 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 inclut un caractère Unicode).

BeautifulSoup gère beaucoup de HTML incorrect (par exemple des balises fermantes manquantes), ce qui éliminerait complètement le regexing simplist. Vous pouvez l'installer dans un python standard en utilisant:

 pip install beautifulsoup4 

ou si vous n'avez pas de pip , avec

 easy_install beautifulsoup4 

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

Peut-être que c'est "sortingcher" mais une option est pup, un parsingur HTML en command line .

Voici deux façons de le faire:

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

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

et une autre façon d'utiliser le champ de title directement (puis de couper la string - YouTube à la fin).

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

Cela semble être possible avec lynx utilisant ce truc ( zsh , syntaxe bash ):

 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' 

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

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

Au-dessus, nous donnons un file de configuration (en tant que pipe) qui définit une «imprimante» lynx appelée P qui sort simplement le contenu de cette variable au descripteur de file 3 (ce descripteur de file est redirigé vers stdout de lynx avec 3>&1 tandis que lynx stdout est lui-même redirigé vers / dev / null).

Ensuite, nous utilisons la fonction de script lynx pour simuler l'user en appuyant sur p , et la End (alias select) et Enter ( ^J ).

-accept_all_cookies car autrement, lynx requestrait à l'user la confirmation de chaque cookie.

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' 

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

J'en ai fait une version simplifiée en utilisant Lynx en cours d'exécution et en utilisant des files préconfigurés à l'avance.

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

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

Cette ligne indique comment save le titre, lors de l'printing, dans "/home/account/title.txt" – vous pouvez choisir n'importe quel nom de file que vous souhaitez. Si vous requestz des pages TRÈS grandes, augmentez la valeur ci-dessus de «1000» à un nombre illimité de lignes par page, sinon Lynx fera une request supplémentaire «lors de l'printing d'un document contenant un très grand nombre de pages».

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

 key p key Select key key ^J exit 

Exécutez ensuite Lynx en utilisant les options de command line 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 command terminée, le file /home/account/title.txt sera créé avec le titre de votre page.

En résumé, voici une fonction PHP qui renvoie 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"); 

En utilisant nokogiri, on peut utiliser une simple requête CSS pour extraire le text 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'atsortingbut "content" de la balise:

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