如何使用命令行获取网站标题?


50

我想要一个打印网站标题的命令行程序。例如:

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

应该给:

Why Are Bad Words Bad? 

您给它提供URL,它会打印出标题。


2
当我下载该标题时,我得到:“为什么坏词不好?-Youtube”,您是否也想将“-Youtube”删节?
slm

Answers:


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

recode如果其中包含类似内容&lt;,则可以将其通过管道传输到GNU :

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命令包括perl经常安装的libwww 附带的命令lynx -source,并且程度较小curl。其他常见的包括links -sourceelinks -sourcew3m -dump_sourcelftp -c cat...

HTTP协议和重定向处理

wget可能无法获得与例如firefox将显示的页面相同的页面。原因是HTTP服务器可以根据客户端发送的请求中提供的信息选择发送其他页面。

wget / w3m / GET ...发送的请求将与firefox发送的请求不同。如果这是一个问题,则可以wget通过选项更改行为,以更改其发送请求的方式。

在这方面最重要的是:

  • Acceptand 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>文件前几个字节中的部分中,因此并非最佳选择。如果awk您的系统上有GNU,则更好的方法是:

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下载页面时将其写入。同时perl,,将其输出(-0777 -n)整体粘贴到内存中,然后打印在第一次出现的<title...>和之间找到的HTML代码</title

这对于具有<title>标签的大多数HTML页面均适用,但在某些情况下将不起作用。

相比之下,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

但是,Web客户端还应在显示标题时对该代码进行更多转换(例如,压缩一些空格,删除前导和尾随的空格)。但是,不太可能需要这样做。因此,与其他情况一样,由您决定是否值得付出努力。

字符集

在UTF-8之前,iso8859-1曾经是Web上非ASCII字符的首选字符集,尽管严格说来,它们必须写为&eacute;。HTTP和HTML语言的最新版本增加了在HTTP标头或HTML标头中指定字符集的可能性,并且客户端可以指定其接受的字符集。如今,UTF-8往往是默认的字符集。

因此,这意味着在那里,您会发现é写为&eacute;,as &#233;,UTF-8 é,(0xc3 0xa9),iso-8859-1(0xe9),最后两个是,有时是字符集上的信息在HTTP标头或HTML标头(以不同的格式)中,有时不是。

wget 仅获取原始字节,不关心它们作为字符的含义,也不向Web服务器告知首选字符集。

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编码的页面根本不起作用。

总结一下

理想情况下,这里您需要的是真正的Web浏览器来为您提供信息。也就是说,您需要执行一些操作以使用适当的参数执行HTTP请求,正确解释HTTP响应,像浏览器一样完全解释HTML代码并返回标题。

由于我认为无法使用已知的浏览器在命令行上完成此操作lynx(尽管现在可以使用来找到此技巧),因此您必须求助于启发式和近似式,并且上面的方法和其他方法一样好。

您可能还需要考虑性能,安全性...例如,要涵盖所有情况(例如,从第三方网站提取具有某些javascript的网页的网页,该网页设置了标题或将其重定向到onload hook),您可能必须使用dom和javascript引擎来实现一个现实生活的浏览器,而dom和javascript引擎可能必须对单个HTML页面执行数百个查询,其中一些试图利用漏洞...

虽然通常不赞成使用正则表达式来解析HTML,但这是一种典型的情况,它足以胜任任务(IMO)。


它也从页面下载图像吗?还会留下垃圾的html文件吗?
Ufoguy 2013年

2
您可能想在第一个实例处终止标题,<因为不能保证标题具有结束标签,并且任何其他标签都应强制终止标题。您可能还希望删除新行。
Brian Nickel

1
不建议使用正则表达式来解析HTML。曾经 即使在这种情况下也不行。这是个坏习惯。请使用真实的解析器。关于这个有一个著名的幽默Stackoverflow答案...
罗宾·格林

4
@RobinGreen那个帖子是关于使用正则表达式解析非常规语言的。有一些警告,但这是一个很容易变成普通语言的问题。我建议使用正则表达式来解析HTML。有时。在这种情况下。
Brian Nickel

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)}'


默认情况下,似乎没有在Ubuntu上安装“ hxselect”。我什至无法在现有存储库中找到它。如何安装?
Ufoguy 2013年

7
sudo apt-get install html-xml-utils
coffeMug ​​2013年

我在Ubuntu 12.10上收到此错误“输入格式不正确。(也许尝试规范化?)”
slm

1
我还没有发现该味精怎么办。关于标准化输出。没有这样的开关hxselect
slm

1
对于Mac OS X,Homebrew的公式中带有hxselect。使用安装brew install html-xml-utils
Sukima 2014年

18

您也可以使用curlgrep执行此操作。你需要争取使用PCRE(Perl兼容正则表达式)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

lang = ...

如果<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=后接单词序列(\w+)。然后将其剥离。

真正的HTML / XML解析器-使用Ruby

regex有时会无法解决此类问题。如果发生这种情况,那么您可能希望使用真正的HTML / XML解析器。这样的解析器就是Nokogiri。它可以作为宝石在Ruby中使用,并且可以这样使用:

$ 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

上面是解析通过curlHTML(Nokogiri::HTML)传入的数据。xpath然后,该方法会在HTML中寻找名称为的叶节点(//)的节点(标签)title。对于每个找到的内容,我们都想返回其内容(e.content)。在puts随后打印出来。

真正的HTML / XML解析器-使用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 

1
干净的解决方案!:)
coffeMug ​​2013年

3
用正则表达式解析HTML并不是那么简单。表达式将不匹配标记为“ <TITLE>”,“ <title lang = zh-CN>”,“ <title \ n>”的标签。更大的问题是,“ <title> \ noops \ n </ title>”都不会。
manatwork 2013年

4
尝试使用正则表达式来解析html的做法在这里常常被皱眉
user3490 2013年

1
@slm <title>Unix\nLinux</title>Unix Linux,不是UnixLinux
斯特凡Chazelas

1
+1代表红宝石+ nogogiri。我已经将它用于各种Web抓取,这太神奇了!
罗布2013年

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-bs4Debian / Ubuntu上的软件包)。


2
bs4不在python标准库中。您必须使用easy_install beautfulsoup4(不是easyinstall bs4)安装它。
Anthon 2013年

@Anthon包含了您的信息
塞尔达传说

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?

为了避免角色实体,用户可能希望使用pup的--plain选项。
高峰

3

lynx使用这个技巧(zshbash语法)似乎是可能的:

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'

因为这是一个现实生活中的Web浏览器,所以它不受我在其他答案中提到的许多限制的困扰。

在这里,我们使用的事实是在打印页面时lynx$LYNX_PRINT_TITLE环境变量设置为当前页面的标题。

上面,我们提供了一个配置文件(作为管道),该文件定义了一个lynx“打印机” P,该打印机仅将变量的内容输出到文件描述符3(该文件描述符lynx通过3>&1lynx stdout自身重定向到的stdout。到/ dev / null)。

然后,我们使用lynx脚本编制工具来模拟用户按下pEnd(aka 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'

1
这些是唯一为我工作的!
艾哈迈德·阿威斯

1

我喜欢StéphaneChazelas使用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?
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.