最近喜欢听王玥波的《雍正剑侠图》,搜到某网站上有全套,可惜,只能一回一回地手动下载,现在出到了第六部,每一部都有上百回,手动点鼠标下载得下到猴年马月去了。古人说:“Where there is a shell, there is a way”,用shell脚本来做这件事吧。
Where there is a shell, there is a way.shell脚本由linux命令组合而成,由于linux现有的大量命令工具都经过了长期的优化和标准化,其执行效率和移植性都很高。
shell爬虫的核心是curl
,curl
可以下载网页,解析http response
头信息,也可以指定http request
头信息,且可处理cookie,具备web浏览器的基本功能,支持HTTPS、FTP、FTPS、TELNET、LDAP等协议。
首先,是解析每一回音频的url,每一回对应一个链接页面,地址的编排很简单,如第一回就是:
http://xxx.com/play/5359/1.html
想当然地使用chrome查看音频的加载过程,结果发现音频文件的url直接写在了源码里:
<a href="http://xxx.com/%E7%8E%8B%E7%8E%A5%E6%B3%A2/%E9%9B%8D%E6%AD%A3%E5%89%91%E4%BE%A0%E5%9B%BE%E7%AC%AC%E4%BA%94%E9%83%A8%2832kbps%29%28121%E5%9B%9E%29/03BEE21D25.mp3" id='down'><img src="//xxx.com/e/data/images/download.jpg" alt="下载雍正剑侠图第五部 001回"></a>
而且该网页中只有这一行出现了.mp3
,不过文件名是随机生成的。
每一回的url有了,这就好办了,先把核心功能完成:
#!/usr/bin/env bash
page_url='http://xxx.com/down/5359/'
for ((i=1; i<=121; i++))
do
page_i="${page_url}$i.html"
mp3_i_url=$(curl ${page_i} | iconv -c -f gb2312 -t utf-8 | grep '\.mp3' | awk -F\" '{print $2}')
curl -o "$i.mp3" ${mp3_i_url}
done
exit 0
下面来逐行分析一下for
循环究竟做了什么。
$page_i
是第i回的下载页面,不过并不是下载链接,下载链接$mp3_i_url
需要解析出来。
curl ${page_i}
获取该页面的html代码,该页面编码是GB2312,标准输出的汉字是乱码,如不进行处理可能会导致脚本执行中报错退出,因此使用iconv
进行转码:
iconv -c -f gb2312 -t utf-8
将网页编码从gb2312转为utf-8,选项-c
表示忽略转码过程中的报错,以避免脚本意外中止。
grep '\.mp3'
使用grep
查找含有'.mp3'的行,这里使用了转义符'\'。
awk -F\" '{print $2}'
这是截取mp3文件的下载链接,awk
是一个强大的工具,可以将一行文本分解为多列进行处理,这里指定"
为分割符,将这行原代码:
<a href="http://xxx.com/%E7%8E%8B%E7%8E%A5%E6%B3%A2/%E9%9B%8D%E6%AD%A3%E5%89%91%E4%BE%A0%E5%9B%BE%E7%AC%AC%E4%BA%94%E9%83%A8%2832kbps%29%28121%E5%9B%9E%29/03BEE21D25.mp3" id='down'><img src="//xxx.com/e/data/images/download.jpg" alt="下载雍正剑侠图第五部 001回"></a>
分解为7列:
<a href=
http://xxx.com/%E7%8E%8B%E7%8E%A5%E6%B3%A2/%E9%9B%8D%E6%AD%A3%E5%89%91%E4%BE%A0%E5%9B%BE%E7%AC%AC%E4%BA%94%E9%83%A8%2832kbps%29%28121%E5%9B%9E%29/03BEE21D25.mp3
id='down'><img src=
//xxx.com/e/data/images/download.jpg
alt=
下载雍正剑侠图第五部 001回
></a>
我们需要的链接是第二列,使用{print $2}
将该列赋值给$mp3_i_url
。
curl -o "$i.mp3" ${mp3_i_url}
再次使用curl
,下载mp3文件,并重命名为$i.mp3
,完成第i
回的下载任务。
以上代码已经达到了我们的目的,不过考虑到网站可能采取的反爬措施,再加几行:
#!/usr/bin/env bash
page_url='http://xxx.com/down/5359/'
user_agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36'
for ((i=1; i<=121; i++))
do
page_i="${page_url}$i.html"
mp3_i_url=$(curl -A ${user_agent} ${page_i} | iconv -c -f gb2312 -t utf-8 | grep '\.mp3' | awk -F\" '{print $2}')
curl -A ${user_agent} -o "$i.mp3" ${mp3_i_url}
sleep 30
done
exit 0
主要做了两种反反爬措施,一是下完一回后延时30s,这个比较好理解。
二是使用curl
的-A
选项,在request
中指定User-Agent
字段,用于模拟客户端设备和浏览器:
curl -A ${user_agent} ${page_i}
不过后来发现,该网站的反爬措施好像并不完善。
到此为止,脚本已经完成,丢到Raspberry pi上去跑了一晚,第二天早上,成功收获了热热乎乎的《雍正剑侠图》。
运行效果对于简单的爬虫功能,和python相比,shell的代码量显然更少,写起来也更快,毕竟这东西基本上只是一次性的,杀鸡还是不要动牛刀了。
网友评论