Jak uzyskać tytuł strony internetowej za pomocą wiersza polecenia?


50

Chcę program wiersza polecenia, który wypisuje tytuł strony internetowej. Na przykład:

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

powinien dać:

Why Are Bad Words Bad? 

Dajesz mu adres URL i wypisuje tytuł.


2
Kiedy pobieram ten tytuł, otrzymuję: „Dlaczego złe słowa są złe? - Youtube”, czy chcesz, aby „- Youtube” również zostało obcięte?
slm

Odpowiedzi:


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

Możesz przesłać go do GNU, recodejeśli są &lt;w nim takie rzeczy :

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

Aby usunąć - youtubeczęść:

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

Aby wskazać niektóre ograniczenia:

ruchliwość

Nie ma standardowego / przenośnego polecenia do wykonywania zapytań HTTP. Kilka dekad temu poleciłbym lynx -sourcezamiast tego tutaj. Ale obecnie wgetjest bardziej przenośny, ponieważ można go domyślnie znaleźć w większości systemów GNU (w tym w większości systemów operacyjnych Linux / stacjonarnych / laptopów). Inne dość przenośne obejmują GETpolecenie, które jest dostarczane z perllibwww, które jest często instalowane lynx -source, i w mniejszym stopniu curl. Inne popularne z nich to links -source, elinks -source, w3m -dump_source, lftp -c cat...

Protokół HTTP i obsługa przekierowań

wgetmoże nie uzyskać tej samej strony, co na przykład firefoxwyświetlana strona. Powodem jest to, że serwery HTTP mogą wybrać wysłanie innej strony na podstawie informacji podanych w żądaniu przesłanym przez klienta.

Żądanie wysłane przez wget / w3m / GET ... będzie inne niż żądanie wysłane przez firefox. Jeśli to jest problem, możesz zmienić wgetzachowanie, aby zmienić sposób, w jaki wysyła żądanie, z opcjami.

Najważniejszymi tutaj w tym zakresie są:

  • Accepti Accept-language: informuje serwer, w którym języku i zestawie znaków klient chce uzyskać odpowiedź. wgetDomyślnie nie wysyła żadnego, więc serwer zwykle wysyła ustawienia z domyślnymi ustawieniami. firefoxna drugim końcu jest prawdopodobnie skonfigurowany do żądania twojego języka.
  • User-Agent: która identyfikuje aplikację kliencką na serwerze. Niektóre witryny wysyłają różne treści w zależności od klienta (chociaż są to głównie różnice między interpretacjami języka javascript) i mogą odmówić obsługi, jeśli używasz agenta użytkownika typu robotwget .
  • Cookie: jeśli odwiedziłeś tę stronę wcześniej, Twoja przeglądarka może mieć do niej trwałe pliki cookie. wgetnie będzie.

wgetbędzie podążał za przekierowaniami, gdy zostaną wykonane na poziomie protokołu HTTP, ale ponieważ nie patrzy on na zawartość strony, nie na te wykonane przez javascript lub coś podobnego <meta http-equiv="refresh" content="0; url=http://example.com/">.

Wydajność / wydajność

Tutaj, z lenistwa, perlprzeczytaliśmy całą zawartość w pamięci, zanim zaczęliśmy szukać <title>tagu. Biorąc pod uwagę, że tytuł znajduje się w <head>sekcji znajdującej się w pierwszych kilku bajtach pliku, nie jest to optymalne. Lepszym podejściem, jeśli GNU awkjest dostępny w twoim systemie, może być:

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

W ten sposób awk przestaje czytać po pierwszym </title, a po wyjściu powoduje wgetzatrzymanie pobierania.

Analiza kodu HTML

Tutaj wgetzapisuje stronę podczas jej pobierania. W tym samym czasie perlslurps swoje wyjście ( -0777 -n) w całości, a następnie drukuje kod HTML znaleziony między pierwszymi wystąpieniami <title...>i </title.

Będzie to działać na większości stron HTML z <title>tagiem, ale są przypadki, w których nie będzie działać.

Dla kontrastu rozwiązanie coffeeMug parsuje stronę HTML jako XML i zwraca odpowiednią wartość dla title. Bardziej poprawne jest, jeśli strona ma gwarancję poprawności XML . Jednak HTML nie musi być poprawnym XML (starsze wersje języka nie były), a ponieważ większość przeglądarek jest łagodna i akceptuje niepoprawny kod HTML, istnieje nawet wiele niepoprawnych kodów HTML.

Zarówno moje rozwiązanie, jak i CoffeeMug's zawiodą w różnych przypadkach narożnych, czasem takich samych, a czasem nie.

Na przykład mój nie powiedzie się:

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

lub:

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

Podczas gdy jego zawiedzie:

<TITLE>foo</TITLE>

(ważny HTML, nie XML) lub:

lub:

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

(ponownie, poprawne html, brakujące <![CDATA[części, aby uczynić go prawidłowym XML).

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

(niepoprawny HTML, ale nadal się tam znajduje i obsługiwany przez większość przeglądarek)

interpretacja kodu wewnątrz tagów.

To rozwiązanie generuje nieprzetworzony tekst pomiędzy <title>i </title>. Zwykle nie powinno być tam żadnych tagów HTML, mogą być tam komentarze (choć nie są obsługiwane przez niektóre przeglądarki, takie jak Firefox, więc jest to bardzo mało prawdopodobne). Nadal może być trochę kodowania 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

Tym zajmuje się 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

Ale klient WWW ma również na celu wykonanie większej liczby transformacji tego kodu podczas wyświetlania tytułu (np. Zagęszczenie niektórych odstępów, usunięcie wiodących i końcowych). Jest jednak mało prawdopodobne, że będzie to potrzebne. Tak jak w innych przypadkach, to Ty decydujesz, czy warto.

Zestaw znaków

Przed UTF-8, iso8859-1 był preferowanym zestawem znaków w sieci dla znaków spoza ASCII, choć ściśle mówiąc, musiały być napisane jako &eacute;. Nowsze wersje HTTP i języka HTML dodają możliwość określenia zestawu znaków w nagłówkach HTTP lub w nagłówkach HTML, a klient może określić akceptowane przez siebie zestawy znaków. UTF-8 jest obecnie domyślnym zestawem znaków.

Oznacza to, że na zewnątrz znajdziesz ézapisany jako &eacute;, jako &#233;, jako UTF-8 é, (0xc3 0xa9), jako iso-8859-1 (0xe9), dla dwóch ostatnich, czasem informacje o zestawie znaków w nagłówkach HTTP lub HTML (w różnych formatach), czasem nie.

wget pobiera tylko nieprzetworzone bajty, nie dba o ich znaczenie jako znaków i nie informuje serwera WWW o preferowanym zestawie znaków.

recode html..zadba o konwersję &eacute;lub &#233;na odpowiednią sekwencję bajtów dla zestawu znaków używanego w systemie, ale dla reszty jest to trudniejsze.

Jeśli twój systemowy zestaw znaków to utf-8, są szanse, że będzie w porządku przez większość czasu, ponieważ zwykle jest to domyślny zestaw znaków używany obecnie.

$ 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

Że éwyżej był UTF-8 é.

Ale jeśli chcesz ukryć inne zestawy znaków, po raz kolejny trzeba będzie się tym zająć.

Należy również zauważyć, że to rozwiązanie w ogóle nie będzie działać na stronach kodowanych w UTF-16 lub UTF-32.

Podsumowując

Idealnie, czego potrzebujesz tutaj, jest prawdziwa przeglądarka internetowa zapewniająca informacje. Oznacza to, że potrzebujesz czegoś, aby wykonać żądanie HTTP z odpowiednimi parametrami, poprawnie zinterpretować odpowiedź HTTP, w pełni zinterpretować kod HTML tak jak przeglądarka i zwrócić tytuł.

Ponieważ nie sądzę, że można tego dokonać w wierszu poleceń za pomocą przeglądarek, które znam (choć teraz widzę tę sztuczkęlynx ), musisz uciekać się do heurystyki i przybliżeń, a powyższa jest równie dobra jak każda inna.

Możesz również wziąć pod uwagę wydajność, bezpieczeństwo ... Na przykład, aby objąć wszystkie przypadki (na przykład stronę internetową, na której pobierany jest skrypt javascript z witryny innej firmy, która ustawia tytuł lub przekierowuje na inną stronę w onload hook), być może będziesz musiał wdrożyć prawdziwą przeglądarkę z jej silnikami dom i javascript, które mogą wymagać setek zapytań dla pojedynczej strony HTML, z których niektóre próbują wykorzystać luki ...

Podczas gdy używanie wyrażeń regularnych do analizowania HTML jest często odrzucane , tutaj jest typowy przypadek, w którym jest wystarczająco dobry do zadania (IMO).


Czy pobiera również obrazy ze stron? Czy pozostawi też niepotrzebne pliki HTML?
Ufoguy

2
Prawdopodobnie chcesz zakończyć tytuł w pierwszej kolejności, <ponieważ nie ma gwarancji, że tytuły zawierają znaczniki końcowe, a jakikolwiek inny znacznik powinien wymusić jego zakończenie. Możesz także chcieć usunąć nowe linie.
Brian Nickel

1
Nie jest zalecane używanie wyrażeń regularnych do analizowania HTML. Zawsze. Nawet w tym przypadku. To zły nawyk. Zamiast tego użyj prawdziwego parsera. Istnieje słynna humorystyczna odpowiedź Stackoverflow na ten temat ...
Robin Green,

4
@RobinGreen Ten post dotyczył używania wyrażenia regularnego do analizowania nieregularnego języka. Istnieją zastrzeżenia, ale problem ten można łatwo sprowadzić do zwykłego języka. Polecam użycie wyrażenia regularnego do parsowania HTML. Czasami. W tym przypadku.
Brian Nickel

2
A liczba wyrażeń regularnych, które działają na prawie wszystko, wynosi około 0.
Robin Green,

27

Możesz także spróbować hxselect(z HTML-XML-Utils ) wgetw następujący sposób:

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

Można zainstalować hxselectw Debianie dystrybucjach opartych przy użyciu:
sudo apt-get install html-xml-utils.

Przekierowanie STDERR ma na celu uniknięcie Input is not well-formed. (Maybe try normalize?)wiadomości.

Aby pozbyć się „- YouTube”, potokuj wyjście powyższego polecenia do awk '{print substr($0, 0, length($0)-10)}'.


Domyślnie „hxselect” nie jest instalowany na Ubuntu. Nie mogę nawet znaleźć tego w moich istniejących repozytoriach. Jak to zainstalować?
Ufoguy

7
sudo apt-get install html-xml-utils
coffeMug

Pojawia się ten błąd w systemie Ubuntu 12.10 „Dane wejściowe nie są poprawnie sformułowane. (Może spróbuj znormalizować?)”
slm

1
Nie znalazłem, co zrobić z msg. o normalizacji wyjścia. Brak takiego włączenia hxselect.
slm

1
W systemie Mac OS X Homebrew ma formułę z hxselect. Zainstaluj za pomocą brew install html-xml-utils.
Sukima

18

Możesz także użyć curli grepdo tego. Trzeba zaciągnąć użycie PCRE (pcre) w grepcelu uzyskania wyglądu tyłu i patrzeć w przyszłość obiektów tak, że możemy znaleźć <title>...</title>tagi.

Przykład

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

Detale

Te curlprzełączniki:

  • -s = cichy
  • -o - = wyślij wyjście do STDOUT

Te grepprzełączniki:

  • -i = niewrażliwość na wielkość liter
  • -o = Zwróć tylko część pasującą
  • -P = Tryb PCRE

Wzór do grep:

  • (?<=<title>) = poszukaj łańcucha rozpoczynającego się od tego po jego lewej stronie
  • (?=</title>) = poszukaj łańcucha, który kończy się tym po prawej stronie
  • (.*)= wszystko pomiędzy <title>..</title>.

Bardziej złożone sytuacje

Jeśli <title>...</titie>obejmuje wiele linii, powyższe go nie znajdzie. Możesz zaradzić tej sytuacji za pomocą tr, aby usunąć dowolne \nznaki, tj tr -d '\n'.

Przykład

Przykładowy plik.

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

I przykładowy przebieg:

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

lang = ...

Jeśli <title>jest ustawiony w ten sposób, <title lang="en">musisz go usunąć przed grepopublikowaniem. Do tego sedcelu można użyć narzędzia :

$ 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

Powyżej znajduje ciąg bez rozróżniania wielkości liter, lang=po którym następuje sekwencja słów ( \w+). Następnie jest usuwany.

Prawdziwy parser HTML / XML - używając Ruby

W pewnym momencie wyrażenie regularne nie powiedzie się w rozwiązaniu tego rodzaju problemu. Jeśli tak się stanie, prawdopodobnie będziesz chciał użyć prawdziwego parsera HTML / XML. Jednym z takich parserów jest Nokogiri . Jest dostępny w Ruby jako klejnot i może być używany w następujący sposób:

$ 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

Powyżej analizowane są dane przychodzące przez curlas HTML ( Nokogiri::HTML). Następnie metoda xpathszuka węzłów (znaczników) w kodzie HTML, które są węzłami liści ( //) o nazwie title. Dla każdego znalezionego chcemy zwrócić jego zawartość ( e.content). putsNastępnie drukuje je.

Prawdziwy parser HTML / XML - używając Perla

Możesz także zrobić coś podobnego z Perlem i modułem 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";

Następnie możesz uruchomić ten skrypt w następujący sposób:

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

1
Schludne rozwiązanie! :)
coffeMug

3
Analiza kodu HTML za pomocą wyrażeń regularnych nie jest taka prosta. Tagi napisane jako „<TITLE>”, „<title lang = en>”, „<title \ n>” nie będą pasować do wyrażenia. Jeszcze większy problem, ani „<title> \ noops \ n </title>” nie będzie.
manatwork

4
Próba parsowania html za pomocą wyrażenia regularnego zwykle jest marszczona .
user3490,

1
@slm, <title>Unix\nLinux</title>ma być Unix Linux, nie UnixLinux.
Stéphane Chazelas,

1
+1 za ruby ​​+ nokogiri. Użyłem go do wszelkiego rodzaju skrobania stron internetowych, to niesamowite!
Rob

7

Używanie prostego wyrażenia regularnego do analizowania HTML jest naiwne. Np. Z nowymi liniami i ignorowaniem kodowania znaków specjalnych określonych w pliku. Postępuj właściwie i naprawdę parsuj stronę, używając jednego z innych prawdziwych parserów wymienionych w innych odpowiedziach lub użyj następującego linera:

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

(Powyższe obejmuje znak Unicode).

BeautifulSoup obsługuje również wiele niepoprawnych plików HTML (np. Brakujących tagów zamykających), co całkowicie uprościłoby proste wyrażenia regularne. Możesz zainstalować go w standardowym pythonie, używając:

pip install beautifulsoup4

lub jeśli nie masz pip, z

easy_install beautifulsoup4

Niektóre systemy operacyjne, takie jak Debian / Ubuntu, również mają to w pakiecie ( python-bs4pakiet na Debian / Ubuntu).


2
bs4nie ma w standardowej bibliotece Pythona. Musisz go zainstalować przy użyciu easy_install beautfulsoup4(nie easyinstall bs4).
Anthon

@Anthon podał twoje informacje
Zelda

5

Może to „oszukiwanie”, ale jedną z opcji jest pup, parser HTML wiersza poleceń .

Oto dwa sposoby, aby to zrobić:

Używanie metapola z property="og:titleatrybutem

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

i inny sposób, używając titlepola bezpośrednio (a następnie odciąć - YouTubeciąg na końcu).

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

Aby uniknąć bytów postaci, użytkownicy mogą chcieć skorzystać z --plainopcji pup .
szczyt

3

To wydaje się być możliwe z lynxużyciem tego triku ( zsh, bashskładni):

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'

Ponieważ jest to prawdziwa przeglądarka internetowa, nie ma wielu ograniczeń, o których wspominam w innej odpowiedzi .

Korzystamy z faktu, że podczas drukowania strony lynxustawia $LYNX_PRINT_TITLEzmienną środowiskową na tytuł bieżącej strony.

Powyżej podajemy plik konfiguracyjny (jako potok), który definiuje wywoływaną „drukarkę” rysia, Pktóra po prostu przekazuje zawartość tej zmiennej do deskryptora pliku 3(ten deskryptor pliku jest przekierowywany na standardowe lynxwyjście, 3>&1podczas gdy sam Lynx jest przekierowywany na / dev / null).

Następnie używamy narzędzia lynxskryptowego do symulacji naciskania użytkownika poraz End(aka select) i Enter( ^J).

-accept_all_cookies w przeciwnym razie ryś poprosiłby użytkownika o potwierdzenie każdego pliku cookie.


3

Prosta droga:

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

Kilka alternatyw:

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

1
To są jedyne, które dla mnie działały!
Ahmad Awais

1

Podobał mi się pomysł Stéphane'a Chazelasa na używanie Lynxa i LYNX_PRINT_TITLE, ale ten skrypt nie działał dla mnie w Ubuntu 14.04.5.

Stworzyłem uproszczoną wersję, używając programu Lynx i plików wstępnie skonfigurowanych.

Dodaj następujący wiersz do /etc/lynx-cur/lynx.cfg (lub gdziekolwiek znajduje się plik lynx.cfg):

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

Ten wiersz nakazuje zapisanie tytułu podczas drukowania do „/home/account/title.txt” - możesz wybrać dowolną nazwę pliku. Żądasz BARDZO dużych stron, zwiększ powyższą wartość z „1000” do dowolnej liczby wierszy na żądanej stronie, w przeciwnym razie Lynx wyświetli dodatkowy monit „podczas drukowania dokumentu zawierającego bardzo dużą liczbę stron”.

Następnie utwórz plik /home/account/lynx-script.txt z następującą zawartością:

key p
key Select key
key ^J
exit

Następnie uruchom Lynx, używając następujących opcji wiersza polecenia:

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

Po zakończeniu tego polecenia zostanie utworzony plik /home/account/title.txt z tytułem strony.

Krótko mówiąc, tutaj jest funkcja PHP, która zwraca tytuł strony na podstawie podanego adresu URL lub fałsz w przypadku błędu.

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

Używając nokogiri, można użyć prostego zapytania opartego na CSS, aby wyodrębnić wewnętrzny tekst znacznika:

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

Podobnie, aby wyodrębnić wartość atrybutu „content” tagu:

$ nokogiri -e 'puts $_.at_css("meta[name=title]").attr("content")'
Why Are Bad Words Bad?
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.