不久前家里裝修,想搞個(gè)書架,看到豆瓣上有這么一個(gè)相冊(cè)收集了一些書房書架的圖片,簡(jiǎn)單寫了個(gè)shell全部抓回來參考。

分析該相冊(cè)首頁(yè)的源碼,可以看到相冊(cè)總頁(yè)數(shù)含有關(guān)鍵字data-total-page,每一頁(yè)有18張圖片,次頁(yè)到最后一頁(yè)的url,只要在首頁(yè)url后加?start=PAGENUMBER,其中PAGENUMBER為18*頁(yè)碼,通過字符串拼接即得到相冊(cè)每一頁(yè)完整的url。
get_page_url(){
num_pages=$(curl -A "${user_agent}" "${base_url}" | grep 'data-total-page' | head -n 1 | awk -F\" '{print $4}')
for ((i=0; i<=num_pages; i++))
do
page_no=$(( 18 * i))
page_url="${base_url}"?start=${page_no}
get_img_no ${page_url}
done
}
這里定義了一個(gè)函數(shù)get_page_url用于獲取相冊(cè)每一頁(yè)的url,并將該url傳遞給另一個(gè)函數(shù)get_img_no,這是一種shell中常用的參數(shù)傳遞方法。
需要注意,在(())中引用的變量不需要加前綴$。
函數(shù)get_img_no用于獲取相冊(cè)每一頁(yè)中目標(biāo)圖片的編號(hào),只要搜索關(guān)鍵詞photolst_photo即可。
get_img_no() {
curl -A "${user_agent}" "$1" | grep "photolst_photo" | awk -F\/ '{print $6}' >> /tmp/img_no.txt
}
$1就是從get_page_url傳遞過來的網(wǎng)址,從該網(wǎng)址中提取圖片的編號(hào),存儲(chǔ)到文件/tmp/img_no.txt中。
通過函數(shù)get_page_url中的for循環(huán),反復(fù)調(diào)用get_img_no,就把相冊(cè)每一頁(yè)中的圖片編號(hào)全部提取出來了。
圖片的url可以通過簡(jiǎn)單的字符串拼接得到,但是該頁(yè)面所展示的圖片只是一個(gè)縮略圖,為了得到原圖,需要解析“查看大圖”的鏈接。
get_img() {
while read line
do
if !(grep $line img_done.txt)
then
img_s_url="https://www.douban.com/photos/photo/""$line""/large"
img_url=$(curl -A "${user_agent}" "$img_s_url" | grep '/large/' | awk -F\" '{print $6}')
curl -A "${user_agent}" -O "$img_url"
echo $line >> img_done.txt
sleep 20
fi
done < /tmp/img_no.txt
rm /tmp/img_no.txt
}
函數(shù)get_img用于解析大圖鏈接并下載圖片。while循環(huán)按行提取圖片編號(hào),拼接為圖片所在網(wǎng)頁(yè)的鏈接img_s_url,該網(wǎng)頁(yè)中的“查看大圖”含有關(guān)鍵詞/large/,很容易解析出大圖鏈接img_url,下載完大圖后,將該圖編號(hào)追加到文件img_done.txt中,也就是說img_done.txt中存儲(chǔ)了所有已下載圖片的編號(hào)。
這里的if用于確認(rèn)某一張圖片是否已經(jīng)下載過,如果下載過則直接跳過,不再重復(fù)下載。如果每隔一段時(shí)間運(yùn)行一次該腳本,就可以保證只下載最新圖片。
豆瓣的反爬措施相對(duì)而言是比較完善的,不過只要稍微控制一下訪問頻率,爬取少量圖片還不足以觸發(fā)反爬程序,因此加入了一個(gè)延時(shí)sleep 20。
完整的代碼如下。
#!/usr/bin/env bash
base_url='https://www.douban.com/photos/album/84338335/'
user_agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36'
touch img_done.txt
get_img() {
while read line
do
if !(grep $line img_done.txt)
then
img_s_url="https://www.douban.com/photos/photo/""$line""/large"
img_url=$(curl -A "${user_agent}" "$img_s_url" | grep '/large/' | awk -F\" '{print $6}')
curl -A "${user_agent}" -O "$img_url"
echo $line >> img_done.txt
sleep 20
fi
done < /tmp/img_no.txt
rm /tmp/img_no.txt
}
get_img_no() {
curl -A "${user_agent}" "$1" | grep "photolst_photo" | awk -F\/ '{print $6}' >> /tmp/img_no.txt
}
get_page_url(){
num_pages=$(curl -A "${user_agent}" "${base_url}" | grep 'data-total-page' | head -n 1 | awk -F\" '{print $4}')
for ((i=0; i<=num_pages; i++))
do
page_no=$(( 18 * i))
page_url="${base_url}"?start=${page_no}
get_img_no ${page_url}
done
}
get_page_url
get_img
exit 0
由于首次運(yùn)行時(shí)還沒有img_done.txt文件,因此使用touch新建一個(gè)文件,如果該文件已經(jīng)存在,touch只更新文件的屬性信息。
將該文件存儲(chǔ)為get_img.sh,通過bash -x可在終端中實(shí)時(shí)看到腳本運(yùn)行的情況:
$ bash -x get_img.sh
如果把腳本扔到crontab中定時(shí)運(yùn)行,就可以追蹤該相冊(cè)的更新情況,并自動(dòng)下載最新圖片了。(不過這個(gè)相冊(cè)貌似很久都不更新了。)
搞明白這個(gè)簡(jiǎn)單的例子,抓取豆瓣上其它類似內(nèi)容都易如反掌了。
這個(gè)腳本充分體現(xiàn)了shell“糙、猛、快”的特點(diǎn),對(duì)于一次性的自用需求,簡(jiǎn)直不能再合適了。
豆瓣上有個(gè)“害羞組”,是很多python爬蟲初學(xué)者的試驗(yàn)?zāi)繕?biāo),何不試試shell呢?