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

Ufo*_*guy 62 command-line web http

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

Alan:~ titlefetcher http://www.youtube.com/watch?v=Dd7dQh8u4Hc
Run Code Online (Sandbox Code Playgroud)

应该给:

Why Are Bad Words Bad? 
Run Code Online (Sandbox Code Playgroud)

你给它网址,它会打印出标题。

Sté*_*las 54

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

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

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

要移除 - youtube零件:

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

指出一些限制:

可移植性

没有标准/可移植命令来执行 HTTP 查询。几十年前,我会推荐lynx -source这里。但是现在,wget它更易于移植,因为它可以在大多数 GNU 系统(包括大多数基于 Linux 的桌面/笔记本电脑操作系统)上找到。其他相当便携的GET命令包括perl经常安装的 libwww附带的命令lynx -source,以及在较小程度上curl。其他常见的包括links -source, elinks -source, w3m -dump_source, lftp -c cat...

HTTP 协议和重定向处理

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

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

在这方面最重要的是:

  • AcceptAccept-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>文件前几个字节的部分中,这不是最佳选择。如果 GNUawk在您的系统上可用,则更好的方法可能是:

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

这样,awk 在第一个之后停止读取</title,并通过退出导致wget停止下载。

解析 HTML

在这里,wget在下载页面时写入页面。同时,perl, 将其输出 ( -0777 -n) 全部存储在内存中,然后打印在第一次出现<title...>和之间找到的 HTML 代码</title

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

相比之下,coffeeMug ​​的解决方案会将 HTML 页面解析为 XML 并返回对应的值title如果页面保证是有效的 XML则更正确。然而,HTML 不需要是有效的 XML(旧版本的语言不是),并且因为大多数浏览器都比较宽松并且会接受不正确的 HTML 代码,所以甚至有很多不正确的 HTML 代码。

我的解决方案和咖啡杯的解决方案在各种极端情况下都会失败,有时相同,有时不同。

例如,我的将失败:

<html><head foo="<title>"><title>blah</title></head></html>
Run Code Online (Sandbox Code Playgroud)

或者:

<!-- <title>old</title> --><title>new</title>
Run Code Online (Sandbox Code Playgroud)

虽然他的遗嘱失败了:

<TITLE>foo</TITLE>
Run Code Online (Sandbox Code Playgroud)

(有效的 html,而不是 xml)或:

或者:

<title>...</title>
...
<script>a='<title>'; b='</title>';</script>
Run Code Online (Sandbox Code Playgroud)

(再次, valid html,缺少<![CDATA[部分使其成为有效的 XML )。

<title>foo <<<bar>>> baz</title>
Run Code Online (Sandbox Code Playgroud)

(不正确的 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
Run Code Online (Sandbox Code Playgroud)

这是由 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
Run Code Online (Sandbox Code Playgroud)

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

字符集

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

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

wget 只获取原始字节,它不关心它们作为字符的含义,也不告诉 Web 服务器有关首选字符集的信息。

recode html..将注意将&eacute;or&#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
Run Code Online (Sandbox Code Playgroud)

é以上是UTF-8 é

但是,如果您想覆盖其他字符集,则必须再次处理。

还应注意,此解决方案对于 UTF-16 或 UTF-32 编码的页面根本不起作用。

总结

理想情况下,您在这里需要的是一个真正的网络浏览器来为您提供信息。也就是说,您需要使用正确的参数来处理 HTTP 请求,正确解释 HTTP 响应,像浏览器一样完全解释 HTML 代码,并返回标题。

因为我不认为可以使用我知道的浏览器在命令行上完成(尽管现在看到这个技巧lynx),你必须求助于启发式和近似,上面的方法和任何方法一样好。

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

虽然使用正则表达式来解析 HTML 经常被人反对,但这里是一个典型案例,它足以完成任务 (IMO)。

  • @RobinGreen 那篇文章是关于使用正则表达式来解析非常规语言的。有一些警告,但这是一个很容易简化为常规语言的问题。我建议使用正则表达式来解析 HTML。有时。在这种情况下。 (5认同)
  • 您可能希望在第一个 `&lt;` 实例处终止标题,因为标题不能保证有结束标签,任何其他标签都应该强制终止。您可能还想删除新行。 (2认同)
  • 几乎适用于所有事物的正则表达式数量大约为 0。 (2认同)

cof*_*Mug 28

您也可以尝试hxselect(来自HTML-XML-Utilswget如下:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' | hxselect -s '\n' -c  'title' 2>/dev/null
Run Code Online (Sandbox Code Playgroud)

您可以安装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)}'.

  • `sudo apt-get install html-xml-utils` (8认同)
  • 对于 Mac OS X 的人来说,[Homebrew](http://brew.sh/) 有一个带有 hxselect 的公式。使用 `brew install html-xml-utils` 安装。 (3认同)

slm*_*slm 20

您也可以使用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
Run Code Online (Sandbox Code Playgroud)

细节

curl开关:

  • -s = 沉默
  • -o - = 将输出发送到 STDOUT

grep开关:

  • -i = 不区分大小写
  • -o = 只返回匹配的部分
  • -P = PCRE 模式

模式为grep

  • (?<=<title>) = 在它的左边寻找以 this 开头的字符串
  • (?=</title>) = 在它的右边寻找一个以 this 结尾的字符串
  • (.*)= 之间的一切<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>
Run Code Online (Sandbox Code Playgroud)

和一个示例运行:

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

朗=...

如果<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
Run Code Online (Sandbox Code Playgroud)

上面找到不区分大小写的字符串lang=后跟一个单词序列 ( \w+)。然后将其剥离。

真正的 HTML/XML 解析器 - 使用 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
Run Code Online (Sandbox Code Playgroud)

以上是解析通过curlas HTML ( 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";
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样运行这个脚本:

$ ./title_getter.pl http://www.jake8us.org/~sam/multi-line.html
this is a \n title 
Run Code Online (Sandbox Code Playgroud)

  • 尝试使用正则表达式解析 html [往往不受欢迎](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags) 在这里。 (5认同)
  • 用正则表达式解析 HTML 并不是那么简单。写为“&lt;TITLE&gt;”、“&lt;title lang=en&gt;”、“&lt;title\n&gt;”的标签不会与您的表达式匹配。更大的问题是,“&lt;title&gt;\noops\n&lt;/title&gt;” 都不会。 (4认同)
  • @slm,“&lt;title&gt;Unix\nLinux&lt;/title&gt;”的意思是“Unix Linux”,而不是“UnixLinux”。 (2认同)

Zel*_*lda 7

使用简单的正则表达式来解析 HTML 是幼稚的。例如,使用换行符并忽略文件中指定的特殊字符编码。做正确的事情并使用其他答案中提到的任何其他真实解析器真正解析页面或使用以下一个班轮:

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

(以上包括一个Unicode字符)。

BeautifulSoup 还处理许多不正确的 HTML(例如缺少结束标记),这将完全抛弃简单的正则表达式。您可以使用以下命令将其安装在标准 python 中:

pip install beautifulsoup4
Run Code Online (Sandbox Code Playgroud)

或者如果你没有pip,与

easy_install beautifulsoup4
Run Code Online (Sandbox Code Playgroud)

一些操作系统如 Debian/Ubuntu 也将它打包(python-bs4Debian/Ubuntu 上的包)。

  • `bs4` 不在 python 标准库中。你必须使用`easy_install beautfulsoup4`(不是`easyinstall bs4`)安装它。 (2认同)

ken*_*orb 6

简单的方法:

curl -s example.com | grep -o "<title>[^<]*" | tail -c+8
Run Code Online (Sandbox Code Playgroud)

几个替代方案:

curl -s example.com | grep -o "<title>[^<]*" | cut -d'>' -f2-
wget -qO- example.com | grep -o "<title>[^<]*" | sed -e 's/<[^>]*>//g'
Run Code Online (Sandbox Code Playgroud)

  • 这些是唯一对我有用的! (2认同)

小智 6

也许它是“作弊”,但一个选项是pup,一个命令行 HTML 解析器

这里有两种方法:

使用metaproperty="og:title属性的字段

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

以及title直接使用该字段的另一种方式(然后- YouTube在最后剪掉字符串)。

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