Как получить заголовок веб-сайта с помощью командной строки?

50

Я хочу программу командной строки, которая печатает заголовок веб-сайта. Например:

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

должен дать:

Why Are Bad Words Bad? 

Вы даете ему URL, и он печатает название.

Ufoguy
источник
2
Когда я загружаю этот заголовок, я получаю: «Почему плохие слова плохи? - Youtube», вы хотите, чтобы «- Youtube» тоже обрезали?
SLM

Ответы:

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

Вы можете передать его в GNU, recodeесли есть такие вещи &lt;:

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

Чтобы удалить - youtubeчасть:

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

Чтобы указать на некоторые из ограничений:

портативность

Не существует стандартной / переносимой команды для выполнения HTTP-запросов. Несколько десятилетий назад я бы рекомендовал lynx -sourceвместо этого здесь. Но в настоящее время wgetон более переносим, ​​поскольку его можно найти по умолчанию в большинстве систем GNU (включая большинство операционных систем для настольных компьютеров / ноутбуков на базе Linux). Другие довольно переносимые включают GETкоманду, которая поставляется с perllibwww, которая часто устанавливается lynx -source, и в меньшей степени curl. Другие распространенные из них включают в себя links -source, elinks -source, w3m -dump_source, lftp -c cat...

HTTP протокол и обработка перенаправления

wgetможет не получить ту же страницу, что и та, которая, например, firefoxбудет отображаться. Причина в том, что HTTP-серверы могут выбрать отправку другой страницы на основе информации, предоставленной в запросе, отправленном клиентом.

Запрос, отправленный wget / w3m / GET ..., будет отличаться от запроса, отправленного firefox. Если это проблема, вы можете изменить wgetповедение, чтобы изменить способ отправки запроса с помощью параметров.

Наиболее важными здесь в этом отношении являются:

  • Acceptи Accept-language: сообщает серверу, на каком языке и кодировке клиент хотел бы получить ответ. wgetПо умолчанию он не отправляется, поэтому сервер обычно отправляет с настройками по умолчанию. firefoxна другом конце, скорее всего, настроен на запрос вашего языка.
  • User-Agent: идентифицирует клиентское приложение на сервере. Некоторые сайты отправляют разный контент на основе клиента (хотя это в основном из-за различий между интерпретациями языка javascript) и могут отказаться обслуживать вас, если вы используете пользовательский агент типа роботаwget .
  • Cookie: если вы посещали этот сайт раньше, в вашем браузере могут быть постоянные файлы cookie для него. wgetне буду.

wgetбудет следовать перенаправлениям, когда они будут выполнены на уровне протокола HTTP, но так как он не смотрит на содержимое страницы, а не на содержимое javascript или тому подобное <meta http-equiv="refresh" content="0; url=http://example.com/">.

Производительность / эффективность

Здесь, из-за лени, мы perlпрочитали весь контент в памяти, прежде чем начать искать <title>тег. Учитывая, что заголовок находится в <head>разделе, который находится в первых нескольких байтах файла, это не оптимально. Лучший подход, если GNU awkдоступен в вашей системе, может быть:

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

Таким образом, awk прекращает чтение после первого </titleи, выйдя, вызывает wgetостановку загрузки.

Разбор HTML

Здесь wgetпишет страницу, как она загружает его. В то же время, perlslus свой output ( -0777 -n) целиком в памяти, а затем печатает HTML-код, найденный между первыми вхождениями <title...>и </title.

Это будет работать для большинства HTML-страниц, имеющих <title>тег, но в некоторых случаях это не сработает.

В отличие от этого решение coffeeMug будет анализировать HTML-страницу как XML и возвращать соответствующее значение для title. Более правильно, если на странице гарантированно указан правильный XML . Тем не менее, HTML не обязательно должен быть действительным XML (более ранние версии языка не были), и поскольку большинство браузеров проявляют снисходительность и будут принимать неправильный HTML-код, существует даже много неправильного HTML-кода.

И моё решение, и CoffeeMug не удастся для разных угловых случаев, иногда одинаковых, иногда нет.

Например, у меня не получится:

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

или же:

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

Пока его не получится

<TITLE>foo</TITLE>

(действительный HTML, а не XML) или:

или же:

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

(опять же, допустимые html, недостающие <![CDATA[части, чтобы сделать его действительным XML).

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

(неверный html, но все еще обнаруживается и поддерживается большинством браузеров)

интерпретация кода внутри тегов.

Это решение выводит необработанный текст между <title>и </title>. Обычно там не должно быть никаких HTML-тэгов, там могут быть комментарии (хотя они не обрабатываются некоторыми браузерами, такими как Firefox, что очень маловероятно). Там все еще может быть некоторая кодировка 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

О чем позаботится 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

Но веб-клиент также предназначен для выполнения большего количества преобразований в этом коде при отображении заголовка (например, сжатие некоторых пробелов, удаление начальных и конечных). Однако вряд ли в этом будет необходимость. Так что, как и в других случаях, вам решать, стоит ли это усилий.

Набор символов

До UTF-8 iso8859-1 раньше был предпочтительным набором символов в сети для символов, не относящихся к ASCII, хотя, строго говоря, они должны были быть записаны как &eacute;. Более поздние версии HTTP и язык HTML добавили возможность указывать набор символов в заголовках HTTP или в заголовках HTML, и клиент может указывать кодировки, которые он принимает. UTF-8 сегодня является набором символов по умолчанию.

Таким образом, это означает , что там, вы найдете éнаписанные , как &eacute;, так &#233;как UTF-8 é, (0xC3 0xa9), в ISO-8859-1 (0xe9), с за 2 последних из них, иногда информацию о кодировке в заголовках HTTP или в заголовках HTML (в разных форматах), иногда нет.

wget он получает только необработанные байты, не заботится об их значении в качестве символов и не сообщает веб-серверу о предпочтительной кодировке.

recode html..позаботится о том, чтобы преобразовать &eacute;или &#233;в правильную последовательность байтов для набора символов, используемого в вашей системе, но в остальном это сложнее.

Если ваша системная кодировка UTF-8, скорее всего, в большинстве случаев она будет в порядке, так как обычно она используется по умолчанию.

$ 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

Это éвыше было UTF-8 é.

Но если вы хотите прикрыть другие кодировки, еще раз об этом нужно позаботиться.

Следует также отметить, что это решение не будет работать вообще для страниц в кодировке UTF-16 или UTF-32.

Подводить итоги

В идеале, то, что вам нужно здесь, это настоящий веб-браузер, чтобы дать вам информацию. То есть вам нужно что-то сделать для HTTP-запроса с правильными параметрами, правильно интерпретировать HTTP-ответ, полностью интерпретировать HTML-код, как браузер, и вернуть заголовок.

Поскольку я не думаю, что это можно сделать в командной строке с браузерами, которых я знаю (хотя теперь посмотрите на этот трюкlynx ), вам придется прибегнуть к эвристике и аппроксимациям, и приведенный выше вариант хорош как любой.

Вы также можете принять во внимание производительность, безопасность ... Например, чтобы охватить все случаи (например, веб-страницу, на которой есть некоторый JavaScript, извлеченный из стороннего сайта, который устанавливает заголовок или перенаправляет на другую страницу в onload hook), вам, возможно, придется реализовать реальный браузер с его механизмами dom и javascript, которым, возможно, придется выполнять сотни запросов для одной HTML-страницы, некоторые из которых пытаются использовать уязвимости ...

Хотя использование регулярных выражений для анализа HTML часто вызывает недовольство , здесь приведен типичный случай, когда этого достаточно для выполнения задачи (IMO).

Стефан Шазелас
источник
Это загружает изображения со страниц тоже? Также это оставит ненужные файлы HTML?
Уфогуй
2
Возможно, вы захотите завершить заголовок в первом случае, <поскольку заголовки не обязательно имеют конечные теги, а любой другой тег должен принудительно завершать его. Вы также можете удалить новые строки.
Брайан Никель
1
Не рекомендуется использовать регулярные выражения для разбора HTML. Когда-либо. Даже в этом случае. Это плохая привычка. Вместо этого используйте настоящий парсер. Есть известный юмористический ответ Stackoverflow об этом ...
Робин Грин
4
@RobinGreen В этом посте речь шла об использовании регулярных выражений для анализа нерегулярного языка. Есть предостережения, но эта проблема легко сводится к обычному языку. Я рекомендую использовать регулярные выражения для разбора HTML. Иногда. В этом случае.
Брайан Никель
2
И количество регулярных выражений, которые работают почти для всего, составляет примерно 0.
Робин Грин
27

Вы также можете попробовать hxselect(из HTML-XML-Utils ) wgetследующим образом:

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

Вы можете установить hxselectв дистрибутивах на основе Debian с помощью:
sudo apt-get install html-xml-utils.

STDERR перенаправление, чтобы избежать Input is not well-formed. (Maybe try normalize?)сообщения.

Чтобы избавиться от «- YouTube», перенаправьте вывод команды выше awk '{print substr($0, 0, length($0)-10)}'.

coffeMug
источник
"hxselect", по-видимому, не установлен в Ubuntu по умолчанию. Я даже не могу найти его в моих существующих хранилищах. Как мне это установить?
Уфогуй
7
sudo apt-get install html-xml-utils
coffeMug
Я получаю эту ошибку в Ubuntu 12.10 «Ввод неправильно сформирован. (Может, попробуйте нормализовать?)»
slm
1
Я не нашел, что делать с сообщением. о нормализации выхода. Нет такого включения hxselect.
СЛМ
1
Для Mac OS X ребята, у Homebrew есть формула с hxselect. Установить с brew install html-xml-utils.
Сукима
18

Вы также можете использовать curlи grepсделать это. Вам необходимо заручиться использование PCRE (Perl Compatible Regular Expressions) в , grepчтобы получить вид сзади и вид впереди объектов , так что мы можем найти <title>...</title>метки.

пример

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

подробности

В curlвыключателях:

  • -s = тихий
  • -o - = отправить вывод в STDOUT

В grepвыключателях:

  • -i = нечувствительность к регистру
  • -o = Вернуть только ту часть, которая соответствует
  • -P = Режим PCRE

Шаблон для grep:

  • (?<=<title>) = искать строку, которая начинается с этого слева от него
  • (?=</title>) = искать строку, которая заканчивается этим справа от него
  • (.*)= Все между ними <title>..</title>.

Более сложные ситуации

Если <title>...</titie>охватывает несколько строк, то выше не найдет его. Вы можете смягчить эту ситуацию с помощью tr, чтобы удалить любые \nсимволы, то есть tr -d '\n'.

пример

Образец файла.

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

И пример прогона:

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

языки = ...

Если <title>установлено так, <title lang="en">то вам нужно удалить это перед grepего использованием. Инструмент sedможет быть использован для этого:

$ 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

Выше находит строку без учета регистра, lang=за которой следует слово sequence ( \w+). Это тогда раздето.

Настоящий HTML / XML Parser - с использованием Ruby

В какой-то момент регулярное выражение не сможет решить эту проблему. Если это произойдет, вы, вероятно, захотите использовать настоящий анализатор HTML / XML. Одним из таких парсеров является Nokogiri . Он доступен в Ruby как Gem и может использоваться следующим образом:

$ 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

Выше анализирует данные, которые поступают через curlкак HTML ( Nokogiri::HTML). Затем метод xpathищет узлы (теги) в HTML, которые являются конечными узлами ( //) с именем title. Для каждого найденного мы хотим вернуть его содержимое ( e.content). putsЗатем печатает их.

Настоящий HTML / XML Parser - с использованием Perl

Вы также можете сделать нечто подобное с Perl и модулем 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";

Затем вы можете запустить этот скрипт так:

$ ./title_getter.pl http://www.jake8us.org/~sam/multi-line.html
this is a \n title 
SLM
источник
1
Аккуратное решение! :)
coffeMug
3
Разбор HTML с помощью регулярных выражений не так прост. Теги, написанные как «<TITLE>», «<title lang = en>», «<title \ n>», не будут соответствовать вашему выражению. Еще большая проблема, ни «<title> \ noops \ n </ title>» не будет.
Манатворк
4
Попытка разобрать html с помощью регулярных выражений обычно не одобряется .
user3490
1
@slm, <title>Unix\nLinux</title>должно быть Unix Linux, нет UnixLinux.
Стефан Шазелас
1
+1 Для рубина + нокогири. Я использовал это для всех видов веб-царапин, это удивительно!
Роб
7

Использование простого регулярного выражения для разбора HTML наивно. Например, с символами новой строки и игнорированием специальной кодировки символов, указанной в файле. Делайте правильные вещи и действительно анализируйте страницу, используя любой из других реальных парсеров, упомянутых в других ответах, или используйте следующий вкладыш:

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

(Выше приведен символ Unicode).

BeautifulSoup также обрабатывает много неправильных HTML (например, отсутствующих закрывающих тегов), что полностью исключает упрощенное регулярное выражение. Вы можете установить его в стандартном Python, используя:

pip install beautifulsoup4

или, если у вас нет pip, с

easy_install beautifulsoup4

Некоторые операционные системы, такие как Debian / Ubuntu, также имеют python-bs4пакет ( пакет в Debian / Ubuntu).

Zelda
источник
2
bs4отсутствует в стандартной библиотеке Python. Вы должны установить его с помощью easy_install beautfulsoup4(не easyinstall bs4).
Энтон
@Anthon включил вашу информацию
Zelda
5

Может быть, это «обман», но один из вариантов - pup, анализатор HTML командной строки .

Вот два способа сделать это:

Использование metaполя с property="og:titleатрибутом

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

и другой способ, используя titleполе напрямую (а затем обрезая - YouTubeстроку в конце).

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'title text{}' | sed 's/ - YouTube$//'
Why Are Bad Words Bad?
abetusk
источник
Чтобы избежать персонажей, пользователи могут захотеть использовать --plainопцию щенка .
пик
3

Кажется, это возможно с lynxиспользованием этого трюка ( zsh, 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'

Поскольку это реальный веб-браузер, он не страдает от многих ограничений, которые я упоминал в своем другом ответе .

Здесь мы используем тот факт , что lynxустанавливает $LYNX_PRINT_TITLEпеременную среды в заголовок текущей страницы при печати страницы.

Выше мы даем файл конфигурации (как канал), который определяет вызываемый lynx «принтер», Pкоторый просто выводит содержимое этой переменной в файловый дескриптор 3(этот файловый дескриптор перенаправляется в стандартный lynxвывод с, в 3>&1то время как сам lynx stdout перенаправляется сам по себе). в / dev / null).

Затем мы используем средства lynxсценариев для имитации нажатия клавиш пользователем p, а также End(он же select) и Enter( ^J).

-accept_all_cookies в противном случае lynx будет запрашивать у пользователя подтверждение для каждого файла cookie.

Стефан Шазелас
источник
3

Простой способ:

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

Несколько альтернатив:

curl -s example.com | grep -o "<title>[^<]*" | cut -d'>' -f2-
wget -qO- example.com | grep -o "<title>[^<]*" | sed -e 's/<[^>]*>//g'
kenorb
источник
1
Это единственные, которые работали на меня!
Ахмад Авайс
1

Мне понравилась идея Стефана Шазеласа использовать Lynx и LYNX_PRINT_TITLE, но этот скрипт не работал для меня под Ubuntu 14.04.5.

Я сделал его упрощенную версию, используя Lynx и предварительно настроенные файлы.

Добавьте следующую строку в /etc/lynx-cur/lynx.cfg (или там, где находится ваш lynx.cfg):

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

Эта строка указывает сохранить заголовок во время печати в «/home/account/title.txt» - вы можете выбрать любое имя файла, которое пожелаете. Вы запрашиваете ОЧЕНЬ большие страницы, увеличьте указанное выше значение с «1000» до любого количества строк на странице, которое хотите, в противном случае Lynx выдаст дополнительный запрос «при печати документа, содержащего очень большое количество страниц».

Затем создайте файл /home/account/lynx-script.txt со следующим содержимым:

key p
key Select key
key ^J
exit

Затем запустите Lynx, используя следующие параметры командной строки:

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

После выполнения этой команды будет создан файл /home/account/title.txt с заголовком вашей страницы.

Короче говоря, здесь есть функция PHP, которая возвращает заголовок страницы на основе заданного URL-адреса или false в случае ошибки.

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");
Максим Масютин
источник
0

Используя nokogiri, можно использовать простой запрос на основе CSS для извлечения внутреннего текста тега:

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

Аналогично, чтобы извлечь значение атрибута «content» тега:

$ nokogiri -e 'puts $_.at_css("meta[name=title]").attr("content")'
Why Are Bad Words Bad?
вершина горы
источник