最近喜歡聽王玥波的《雍正劍俠圖》,搜到某網(wǎng)站上有全套,可惜,只能一回一回地手動(dòng)下載,現(xiàn)在出到了第六部,每一部都有上百回,手動(dòng)點(diǎn)鼠標(biāo)下載得下到猴年馬月去了。古人說(shuō):“Where there is a shell, there is a way”,用shell腳本來(lái)做這件事吧。

shell腳本由linux命令組合而成,由于linux現(xiàn)有的大量命令工具都經(jīng)過了長(zhǎng)期的優(yōu)化和標(biāo)準(zhǔn)化,其執(zhí)行效率和移植性都很高。
shell爬蟲的核心是curl,curl可以下載網(wǎng)頁(yè),解析http response頭信息,也可以指定http request頭信息,且可處理cookie,具備web瀏覽器的基本功能,支持HTTPS、FTP、FTPS、TELNET、LDAP等協(xié)議。

首先,是解析每一回音頻的url,每一回對(duì)應(yīng)一個(gè)鏈接頁(yè)面,地址的編排很簡(jiǎn)單,如第一回就是:
http://xxx.com/play/5359/1.html
想當(dāng)然地使用chrome查看音頻的加載過程,結(jié)果發(fā)現(xiàn)音頻文件的url直接寫在了源碼里:
<a id='down'><img src="http://xxx.com/e/data/images/download.jpg" alt="下載雍正劍俠圖第五部 001回"></a>
而且該網(wǎng)頁(yè)中只有這一行出現(xiàn)了.mp3,不過文件名是隨機(jī)生成的。
每一回的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
下面來(lái)逐行分析一下for循環(huán)究竟做了什么。
$page_i是第i回的下載頁(yè)面,不過并不是下載鏈接,下載鏈接$mp3_i_url需要解析出來(lái)。
curl ${page_i}
獲取該頁(yè)面的html代碼,該頁(yè)面編碼是GB2312,標(biāo)準(zhǔn)輸出的漢字是亂碼,如不進(jìn)行處理可能會(huì)導(dǎo)致腳本執(zhí)行中報(bào)錯(cuò)退出,因此使用iconv進(jìn)行轉(zhuǎn)碼:
iconv -c -f gb2312 -t utf-8
將網(wǎng)頁(yè)編碼從gb2312轉(zhuǎn)為utf-8,選項(xiàng)-c表示忽略轉(zhuǎn)碼過程中的報(bào)錯(cuò),以避免腳本意外中止。
grep '\.mp3'
使用grep查找含有'.mp3'的行,這里使用了轉(zhuǎn)義符'\'。
awk -F\" '{print $2}'
這是截取mp3文件的下載鏈接,awk是一個(gè)強(qiáng)大的工具,可以將一行文本分解為多列進(jìn)行處理,這里指定"為分割符,將這行原代碼:
<a id='down'><img src="http://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回的下載任務(wù)。
以上代碼已經(jīng)達(dá)到了我們的目的,不過考慮到網(wǎng)站可能采取的反爬措施,再加幾行:
#!/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
主要做了兩種反反爬措施,一是下完一回后延時(shí)30s,這個(gè)比較好理解。
二是使用curl的-A選項(xiàng),在request中指定User-Agent字段,用于模擬客戶端設(shè)備和瀏覽器:
curl -A ${user_agent} ${page_i}
不過后來(lái)發(fā)現(xiàn),該網(wǎng)站的反爬措施好像并不完善。
到此為止,腳本已經(jīng)完成,丟到Raspberry pi上去跑了一晚,第二天早上,成功收獲了熱熱乎乎的《雍正劍俠圖》。

對(duì)于簡(jiǎn)單的爬蟲功能,和python相比,shell的代碼量顯然更少,寫起來(lái)也更快,畢竟這東西基本上只是一次性的,殺雞還是不要?jiǎng)优5读恕?/p>