Shell 脚本应用 - 从简单脚本到复杂 HTML 表单
上个月,我们探讨了如何将页面上的 HTML 表单转换为 shell 脚本,通过命令标志和变量,您可以访问搜索框的所有功能。我们利用了 Yahoo Movies,并正在构建一个脚本,该脚本提供了 movies.yahoo.com/mv/advsearch 搜索表单上的主要功能。
我们构建的脚本最终得到了以下用法说明
USAGE: findmovie -g genre -k keywords -nrst title
因此,这让您了解了我们尝试做的事情。上个月,我们完成了一个脚本,该脚本提供了上述功能,并且可以使用 open 命令打开 Web 浏览器并显示搜索结果。
现在,让我们从一个警告开始:open 是一个 Mac OS X 命令行脚本,可让您启动 GUI 应用程序。几乎所有其他 Linux/UNIX 版本都有类似的功能,包括如果您运行 X Window 系统。实际上,对于大多数系统,它甚至更容易。“使用此 URL 加载打开 Web 浏览器”的典型 Linux 版本可能很简单,例如
firefox https://linuxjournal.cn/ &
这很容易做到,即使在 shell 脚本中也是如此。
实际上,如果您要通过调用特定命令来结束脚本,最好的方法是“exec”该命令,这基本上会将脚本替换为您指定的应用程序,因此它不会继续运行,甚至不需要退出。所以在这种情况下,它可能看起来像exec firefox "$url"作为脚本的最后一行。
本月,我想回顾一下,让我们的脚本做更多有趣的事情。目前,像这样的调用
./findmovie.sh -g act evil
会从脚本的最后几行生成一个命令
echo $baseurl${params}\&p=$pattern exec open -a safari "$baseurl${params}\&p=$pattern"
最终会输出这个
http://movies.yahoo.com/mv/ ↪search?yr=all&syn_match=all&adv=y&type=feature&gen=act&p=evil
这非常复杂!
如果用户希望选择将数据转储到命令行而不是启动浏览器怎么办?我们可以通过在 getopt 块中添加 -d dump 命令标志来解决这个问题
while getopts "dg:k:nrst" arg do case "$arg" in d ) dump=1 ;; g ) params="${params:+$params&}gen=$OPTARG" ;;
为了转储数据,我们将像过去一样,使用强大的 curl 命令。该程序有无数选项,但由于我们只对原始输出感兴趣,我们可以忽略所有选项(幸运的是),除了 --silent,它隐藏状态更新,留下条件
if [ $dump -eq 1 ] ; then exec /usr/bin/curl --silent "$baseurl${params}\&p=$pattern" else exec open -a safari "$baseurl${params}\&p=$pattern" fi
但是,这会生成大量数据,包括生成相关页面所需的所有 HTML。让我们花一点时间仔细查看该输出,看看是否有办法至少稍微修剪一下。
事实证明,每个匹配的电影标题都包含一个链接,指向 Yahoo Movies 网站上的电影信息。这些链接看起来像
<a href="http://movies.yahoo.com/movie/1809697875/info">Resident Evil
因此,这很容易检测。更好的是,我们可以使用带有 grep 的正则表达式,并跳过很多多余的数据
cmd | grep '/movie/.*info'
这已经接近于只保留匹配单个电影的行,但为了更进一步,让我们删除 dvdinfo 的错误匹配,因为我们对 DVD 发行信息的链接不感兴趣。这是一个grep -v:
cmd | grep '/movie/.*info' | grep -v dvdinfo
现在,让我们快速看一下标题中带有“funny”一词的喜剧片
./findmovie.sh -d -g com funny | grep '/movie/.*info' ↪| grep -v dvdinfo | head -3 <td><a href="http://movies.yahoo.com/movie/1810041785/info"> <b>Funny</b> People (2009)</a><br> <td><a href="http://movies.yahoo.com/movie/1809406735/info">What's So <b>Funny</b> About Me? (1997)</a><br> <td><a href="http://movies.yahoo.com/movie/1808565885/info">That <b>Funny</b> Feeling (1965)</a><br>
好的,那么 HTML 混乱中的前三部电影是 Funny People、What's So Funny About Me? 和 That Funny Feeling。
从这一点来看,您肯定可以四处摸索并编写更好的过滤器来提取您想要的特定信息。难点是什么?像大多数其他网站一样,Yahoo Movies 将结果分成多个页面,因此您真正想做的是确定有多少页结果,然后逐页抓取结果。这很乏味,但可以做到。
让我们看看一个更有趣的子集,通过添加 -c 标志,使其仅输出与指定条件匹配的电影数量的计数,而不是您给出的命令。
为此,我们不需要逐页浏览,只需识别并从页面上的匹配计数中提取值即可。对于标题中带有“funny”的喜剧片,页面上的行看起来像这样:“< Prev | 1 - 20 of 37 | Next 17 >”。
我们需要做的是破解 HTML 并查看指向“next 17”链接的源代码,看看它是否可提取(extractable 是一个词吗?)
./findmovie.sh -d -g com funny | grep -i "next 17" | head -1 <td align=right><font face=arial size="-2"><nobr> ↪< Prev | <b>1 - 20</b> ↪ of <b>37</b> | <span ↪class="yperlink"><ai href="/mv/search?p=funny&yr=all ↪&gen=com\&syn_match=all&adv=y&type=feature ↪&n=17&b=21&h=s">Next 17</a> > ↪ </nobr></span></span></font></td></tr>
嗯,这很糟糕。您会认为 Yahoo 不想让这件事变得容易!但事实证明,这是一个非常棘手的任务,因为如果没有匹配项,链接就不会显示,而是您会看到“Sorry, no matches were found”(抱歉,未找到匹配项)。如果匹配项少于 20 个,您会看到“Next >”(下一个 >),但它不是一个可点击的链接,所以这不会那么容易!
鉴于我没有空间了,让我们将这个主题推迟到下个月。同时,您自己查看各种搜索的源代码,看看是否有任何想法。否则,就只能是蛮力了!
Dave Taylor 编写 shell 脚本已经很长时间了,30 年。他是流行的 Wicked Cool Shell Scripts 的作者,可以在 Twitter 上通过 @DaveTaylor 找到他,更普遍的可以在 www.DaveTaylorOnline.com 找到他。