python批量下載馬拉松照片
目前學習python幾個月了,由於自己比較喜歡跑馬拉松,已經跑過了很多場比賽,前些天就寫了個簡單的爬蟲爬取了網上三千多場馬拉松比賽的報名資訊。
今年5月27日,我又參加了巴圖魯關門山壹佰越野50公里組的比賽,這裡的“巴圖魯”源自蒙古語“英雄”的意思,這場比賽也是出了名的虐,地點在遼寧省本溪市 · 關門山國家森林公園,累計爬升3655m。當天早上六點準時出發,剛跑沒多久就來了很長一段陡峭的臺階……此處省略一萬字……最終經過很多小時的艱苦奮戰完成了比賽。
賽後去官網想找幾張好看的圖片發朋友圈,開啟官網賽事圖片連結到了愛運動的一個網頁 ofollow,noindex">http://runnerbar.com/yd_runnerbar/album/pc?type=3&activity_id=10712 ,這是個單頁面的網頁,不斷滾動會自動載入更多的圖片,我把頁面一點點滾動找了很長一段時間根本找不到我的照片,重新整理一下頁面照片又從頭開始了,實在不能忍。於是,我想要不把圖片全部下載到本地檢視吧,想幹就幹。
1. 分析
1.1 Chrome除錯
在chrome瀏覽器裡輸入快捷鍵Cmd + Opt + I(Windows上是F12,或Ctrl + Shift + I),將除錯選項切到Network,如下

一個個觀察此網頁傳送的請求,找到和圖片相關的請求

這是一個get請求,初步分析裡面的引數,activity_id代表賽事id,page和pageSize分別代表頁數和每頁大小,接著將請求放在postman上印證

1.2請求分析
在postman里加了三個引數成功返回了一個json格式的值,第一階段很順利,接著分析裡面的返回值,下面取了其中的一個元素
{ "album": { "activity_photo_count": 6984, "searchResultList": [ { "id": "32926651", "uid": 50392, "name": "巴圖魯關東越野", "user_name": null, "user_img": "http://oss.runnerbar.com/img/user_upload/origin/20180526/1527305285356_fb59065d_18ce_478b_a3aa_259783f4cd5b.jpg", "create_time": 1527313780000, "image_height": 3648, "image_width": 5472, "orientation": 1, "url_hq": "http://oss.runnerbar.com/img/watermark/user_upload/origin/20180526/1527313783392_235c5cea_5d0c_4cd7_afc6_0ba37cdc7c1d.jpg?quality=h", "url_lq": "http://oss.runnerbar.com/img/watermark/user_upload/origin/20180526/1527313783867_7d986351_fde4_418a_8fb3_1723dcb38aec.jpg", "content": null, "is_like": 0, "like_count": null, "comment_count": 0 }}
這是個json格式,最外層裡有個album元素,album裡包含了圖片總數量activity_photo_count和圖片資訊的陣列searchResultList。每張圖片包含了id、uid、user_img、create_time等等,和圖片路徑相關的有三個值分別是user_img、url_hq、url_lq,其中的user_img開啟後發現是賽事的宣傳logo,剩下的url_hq、url_lq根據命名就很容易猜想到這是對應的兩種尺寸的圖片,用瀏覽器分別開啟,果不其然正是想要的圖片路徑。
2.程式碼
2.1
上面已經知道了請求url和引數,下面就是需要將這些用程式碼實現出來。首先是發請求
url='http://m.yundong.runnerbar.com/yd_mobile/share/album.json' para = {'activity_id':id,'page':page,'pageSize':100} header = {} r = requests.post(url,data=para,headers= header)
請求的返回值是json,json內容在上面已經貼出來了這裡就不再重複,接著解析這個json
json_r = r.json() parsed_json = json_r['album']['searchResultList'] activity = {} items = [] count = json_r['album']['activity_photo_count']
這裡就取到了圖片總數量和圖片資訊的陣列,這個請求引數是page和pageSize,一個請求只能取到一部分圖片資訊並不能把所有的圖片都取出來。那能不能把所有圖片分成一頁返回呢?於是在postman上做了實驗,將page=1,pageSize=10000傳送,結果並不是想要的,真正返回的圖片數量是100。說明這個介面做了校驗,每個分頁最大數量是100。看來投機取巧是不行了,分頁還是要做的。
首先將單個請求封裝成方法,傳入page返回對應page的圖片資訊陣列
def getRaceInfo(id,page): url='http://m.yundong.runnerbar.com/yd_mobile/share/album.json' para = {'activity_id':id,'page':page,'pageSize':100} header = {} r = requests.post(url,data=para,headers= header) json_r = r.json() parsed_json = json_r['album']['searchResultList'] activity = {} items = [] count = json_r['album']['activity_photo_count'] for item in parsed_json: # print(item['user_img']) items.append(item) activity['items'] = items activity['count'] = count return activity
圖片的做數量是count,每頁分100張圖片,起點是第1頁,那麼總的分頁數量就是count/100+2,分頁的程式碼就應該是這樣的
for i in range(1,int(count/100+2)): data = getRaceInfo(id,i)['items']
這裡只是貼了一小段程式碼,完整程式碼可以參見上面的github地址
2.2 下載
有了圖片在url,下載圖片就更簡單了,直接上程式碼
def save_img(img_url,file_name,file_path='book'): #儲存圖片到磁碟資料夾 file_path中,預設為當前指令碼執行目錄下的 book\img資料夾 try: if not os.path.exists(file_path): print('資料夾',file_path,'不存在,重新建立') #os.mkdir(file_path) os.makedirs(file_path) #獲得圖片字尾 file_suffix = os.path.splitext(img_url)[1] #拼接圖片名(包含路徑) filename = '{}{}{}{}'.format(file_path,os.sep,file_name,file_suffix) #下載圖片,並儲存到資料夾中 urllib.request.urlretrieve(img_url,filename=filename) except IOError as e: print('檔案操作失敗',e) except HTTPError as e: print('Error code: ', e.code) except Exception as e: print('錯誤 :',e)
執行python,檢視本地檔案
幾千張圖片很快下載到了本地

這時又有了新的想法,既然可以下載關門山越野的圖片,是不是可以把愛運動裡所有的圖片都下載下來,說幹就幹。於是我將賽事id定義成引數,寫個方法遍歷id。改動了幾行從新執行,幾個小時後程式還在執行但是圖片佔用的大小已經超過了7G,

開啟檔案裡面包含了各個賽事的圖片,眼看圖片越來越多加上我的mac儲存空間有限最終停止了下載,但是這個思路應該是可行的。
原始碼地址: https://github.com/halibobo/runnerbar-image
最後
整個過程從開始到結束都在一天內完成的,程式碼裡也沒有什麼複雜的邏輯,但完成之後心裡還是有很多的滿足感。
最後貼上一句我很喜歡的賽事宣傳語
和一群志同道合的人,一起奔跑在理想的路上, 回頭有一路的故事,低頭有堅定的腳步,抬頭有清晰的遠方。 —— 致巴圖魯er