最近看了女神的新劇《逃避雖然可恥但有用》,同樣男主也是一名程序員,所以很有共鳴
被大只蘿莉萌的一臉一臉的,我們來爬一爬女神的皂片。
百度搜索結果:新恆結衣
本文主要分為4個部分:
1.下載簡單頁面
2.爬取多張圖片
3.頁面解碼
4.爬取過程排錯處理
1.下載簡單頁面
通過查看網頁的html源碼,分析得出,同一張圖片共有4種鏈接:
{"thumbURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=23&gp=0.jpg", "middleURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=21&gp=0.jpg", "hoverURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=23&gp=0.jpg", "objURL":"http://attachments.gfan.com/attachments2/day_110111/1101112033d77a4a8eb2b00eb1.jpg"}
主要區別是分辨率不同,objURL是圖片的源也是最清楚的一張。經測試,前三種都有反爬蟲措施,用瀏覽器可以打開,但是刷新一次就403 Forbidden。用爬蟲獲取不到圖片
第四種objURL是指圖片的源網址,獲取該網址會出現三種情況:
- 正常。繼續下載
- 403 Forbidden。用continue跳過。
- 出現異常。用try except處理。
1 #coding: utf-8 2 import os 3 import re 4 import urllib 5 import urllib2 6 7 def getHtml(url): 8 page=urllib.urlopen(url) 9 html=page.read() 10 return html 11 12 def getImg(html): 13 reg=r'"objURL":"(.*?)"' #正則 14 # 括號表示分組,將括號的內容捕獲到分組當中 15 # 這個括號也就可以匹配網頁中圖片的url了 16 imgre=re.compile(reg) 17 print imgre 18 imglist=re.findall(imgre,html) 19 l=len(imglist) 20 print l 21 return imglist 22 23 def downLoad(urls,path): 24 index = 1 25 for url in urls: 26 print("Downloading:", url) 27 try: 28 res = urllib2.Request(url) 29 if str(res.status_code)[0] == "4": 30 print("未下載成功:", url) 31 continue 32 except Exception as e: 33 print("未下載成功:", url) 34 filename = os.path.join(path, str(index) + ".jpg") 35 urllib.urlretrieve(url,filename) #直接將遠程數據下載到本地。 36 index += 1 37 html =getHtml("http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&s" 38 "f=1&fmq=1484296421424_R&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&" 39 "word=新垣結衣&f=3&oq=xinyuanj&rsp=0") 40 Savepath="D:\TestDownload" 41 downLoad(getImg(html),Savepath)
其中urlretrieve方法
直接將遠程數據下載到本地。
urllib.urlretrieve(url[, filename[, reporthook[, data]]])
參數說明:
url:外部或者本地url
filename:指定了保存到本地的路徑(如果未指定該參數,urllib會生成一個臨時文件來保存數據);
reporthook:是一個回調函數,當連接上服務器、以及相應的數據塊傳輸完畢的時候會觸發該回調。我們可以利用這個回調函數來顯示當前的下載進度。
data:指post到服務器的數據。該方法返回一個包含兩個元素的元組(filename, headers),filename表示保存到本地的路徑,header表示服務器的響應頭。
女神的照片就這么被爬下來了
2.爬取更多圖片
等等,這個方法不過癮呀,百度圖片的頁面是動態加載的,需要你往下滑才能繼續加載后面的照片,我們這才完成了第一步,只爬到了30張圖。
打開瀏覽器,按F12,切換到Network標簽,然后將網頁向下拉。這時瀏覽器地址欄的網址並沒有改變,而網頁中的圖片卻一張張增加,說明網頁在后台與服務器交互數據。好的發現了就是這個家伙
XHR英文全名XmlHttpRequest,中文可以解釋為可擴展超文本傳輸請求。Xml可擴展標記語言,Http超文本傳輸協議,Request請求。XMLHttpRequest對象可以在不向服務器提交整個頁面的情況下,實現局部更新網頁。當頁面全部加載完畢后,客戶端通過該對象向服務器請求數據,服務器端接受數據並處理后,向客戶端反饋數據。
點開對比Request的URL,可以發現,基本上是一樣的,除了末尾一點點。
只是pn的值不一樣,測試發現,pn應該是表示當前請求的圖片序號,rn表示更新顯示圖片的數量。
pn=90&rn=30&gsm=5a&1484307466221= pn=120&rn=30&gsm=5a&1484307466223= pn=150&rn=30&gsm=78&1484307469213= pn=180&rn=30&gsm=78&1484307469214= pn=210&rn=30&gsm=b4&1484307553244=
推薦一款Chrom的插件,Json handle。很方便。
百度雲下載地址。鏈接:http://pan.baidu.com/s/1qXZEjaW 密碼:nxr5
現在知道了,其實我們只要不斷訪問這個Request URL,改變他的pn值理論上就可以實現下載多張圖片了。
1 def getMoreURL(word): 2 word = urllib.quote(word) 3 url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}" \ 4 r"&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=60" 5 urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=30)) 6 #itertools.count 0開始,步長30,迭代 7 return urls
其中,urllib.quote的作用是,對url就行處理。
所以 URL 中使用其他字符就需要進行 URL 編碼。
URL 中傳參數的部分(query String),格式是:
name1=value1&name2=value2&name3=value3
URL編碼的方式是把需要編碼的字符轉化為 %xx 的形式。通常 URL 編碼是基於 UTF-8 的(當然這和瀏覽器平台有關)。
例子:
比如『我』,unicode 為 0x6211, UTF-8 編碼為 0xE6 0x88 0x91,URL 編碼就是
%E6%88%91
itertools.count(start,step)的作用是一個迭代器,2個參數是開始和步長,這樣就得到一個等差數列,這個數列填入url中,就得到了我們的urls。
試了下,轉到解析得到的數據,objURL居然不是一個http的圖片,查資料,顯示下一步要做的就是解碼。
"objURL":"ippr_z2C$qAzdH3FAzdH3Fta_z&e3Bi1fsk_z&e3Bv54AzdH3FkufAzdH3Fw6vitejAzdH3F99lwbdublldlnvjaavmc8c01ba8a8m1mu0vjwama_z&e3B3r2"
3.頁面解碼
參考百度圖片頁面解碼,發現其實就是一個table,key和value對應就可以解碼了,簡直就是明文密碼0.0.。。。
所以在我們的程序里加個字典,多個解碼的過程。
期間解碼過程一度出錯,經過仔細排查,是url.translate(char_table)出了問題,
str 的translate方法需要用單個字符的十進制unicode編碼作為key
value 中的數字會被當成十進制unicode編碼轉換成字符
也可以直接用字符串作為value
所以需要加上一行
char_table = {ord(key): ord(value) for key, value in char_table.items()}
然而試了還是不行,提示translate方法有問題,去查了一下官方的文檔
里面的例子是maketrans()方法一起用的,例子是替換元音字母
#!/usr/bin/python from string import maketrans # 引用 maketrans 函數。 intab = "aeiou" outtab = "12345" trantab = maketrans(intab, outtab) str = "this is string example....wow!!!"; print str.translate(trantab);
輸出結果:
1 th3s 3s str3ng 2x1mpl2....w4w!!!
所以對我們的chartable做處理,將key和value 通過maketrans關聯:
intab="wkv1ju2it3hs4g5rq6fp7eo8dn9cm0bla" outtab="abcdefghijklmnopqrstuvw1234567890" trantab = maketrans(intab, outtab)
到此為止,成功解析圖片url。
4.爬取過程排錯處理
真正在爬取的時候,會有一些錯誤,什么404,403啊,這樣的話,程序就爬不到圖片,這就比較尷尬了。
所以在下載的時候,需要做處理。
開始的時候,我用的是這種:
1 try: 2 res = requests.get(imgUrl, timeout=10) #超時 3 if str(res.status_code)[0] == "4": 4 print(str(res.status_code), ":" , imgUrl) 5 return False 6 except Exception as e: 7 print("拋出異常:", imgUrl) 8 print(e)
但是爬着爬着發現,有的時候status_code不存在,又調試了一番,通過對比,發現404,403這種屬於HTTPErro,但是request的時候,還可能產生URLError,如下圖。
所以拋出異常的過程就需要進行分開處理了。
10060 因為目標主機主動拒絕,連接不能建立。這通常是因為試圖連接到一個遠程主機上不活動的服務,如沒有服務器應用程序處於執行狀態
1 res = urllib2.Request(url) 2 try: 3 response = urllib2.urlopen(res ,data=None, timeout=5) #超時處理 4 except urllib2.URLError, e: 5 if hasattr(e,'code'): 6 error_status = e.code 7 print(error_status, "未下載成功:", url) 8 continue 9 elif hasattr(e,'reason'): 10 print( "time out", url) 11 continue
測試成功,可以通過自己設置下載圖片數量,然后無線爬了,爬的速度差不多2分鍾爬了300多張,這和圖片的源和大小都有一定關系。
All Code:

1 #coding: utf-8 2 import os 3 import re 4 import urllib 5 import urllib2 6 import itertools #迭代器 7 from string import maketrans 8 9 str_table = { 10 '_z2C$q': ':', 11 '_z&e3B': '.', 12 'AzdH3F': '/' 13 } 14 15 char_table = { 16 'w': 'a', 17 'k': 'b', 18 'v': 'c', 19 '1': 'd', 20 'j': 'e', 21 'u': 'f', 22 '2': 'g', 23 'i': 'h', 24 't': 'i', 25 '3': 'j', 26 'h': 'k', 27 's': 'l', 28 '4': 'm', 29 'g': 'n', 30 '5': 'o', 31 'r': 'p', 32 'q': 'q', 33 '6': 'r', 34 'f': 's', 35 'p': 't', 36 '7': 'u', 37 'e': 'v', 38 'o': 'w', 39 '8': '1', 40 'd': '2', 41 'n': '3', 42 '9': '4', 43 'c': '5', 44 'm': '6', 45 '0': '7', 46 'b': '8', 47 'l': '9', 48 'a': '0' 49 } 50 51 intab="wkv1ju2it3hs4g5rq6fp7eo8dn9cm0bla" 52 outtab="abcdefghijklmnopqrstuvw1234567890" 53 trantab = maketrans(intab, outtab) 54 55 char_table = {ord(key): ord(value) for key, value in char_table.items()} 56 def deCode(url): 57 # 先替換字符串 58 for key, value in str_table.items(): 59 url = url.replace(key, value) 60 # 再替換剩下的字符 61 d=url.translate(trantab) 62 return d 63 64 def getMoreURL(word): 65 word = urllib.quote(word) 66 url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}" \ 67 r"&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=30" 68 urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=30)) 69 #itertools.count 0開始,步長30,迭代 70 return urls 71 72 def getHtml(url): 73 page=urllib.urlopen(url) 74 html=page.read() 75 return html 76 77 #解析圖片url解碼 78 def getImg(html): 79 reg=r'"objURL":"(.*?)"' #正則 80 # 括號表示分組,將括號的內容捕獲到分組當中 81 # 這個括號也就可以匹配網頁中圖片的url了 82 imgre=re.compile(reg) 83 imageList=re.findall(imgre, html) 84 imgUrls=[] 85 86 for image in imageList: 87 imgUrls.append(deCode(image)) 88 89 l=len(imgUrls) 90 print l 91 return imgUrls 92 93 def downLoad(urls,path): 94 global index 95 for url in urls: 96 print("Downloading:", url) 97 res = urllib2.Request(url) 98 try: 99 response = urllib2.urlopen(res ,data=None, timeout=5) #超時處理 100 except urllib2.URLError, e: 101 if hasattr(e,'code'): 102 error_status = e.code 103 print(error_status, "未下載成功:", url) 104 continue 105 elif hasattr(e,'reason'): 106 print( "time out", url) 107 continue 108 109 continue 110 111 filename = os.path.join(path, str(index) + ".jpg") 112 urllib.urlretrieve(url,filename) #直接將遠程數據下載到本地。 113 index += 1 114 # urllib.urlretrieve(url[, filename[, reporthook[, data]]]) 115 # 參數說明: 116 # url:外部或者本地url 117 # filename:指定了保存到本地的路徑(如果未指定該參數,urllib會生成一個臨時文件來保存數據); 118 # reporthook:是一個回調函數,當連接上服務器、以及相應的數據塊傳輸完畢的時候會觸發該回調。我們可以利用這個回調函數來顯示當前的下載進度。 119 # data:指post到服務器的數據。該方法返回一個包含兩個元素的元組(filename, headers),filename表示保存到本地的路徑,header表示服務器的響應頭。 120 if index-1==1000: 121 break 122 123 if __name__ == '__main__': 124 keyWord="新垣結衣" 125 index = 1 126 Savepath = "D:\TestDownload" 127 urls=getMoreURL(keyWord) 128 129 for url in urls: 130 downLoad(getImg(getHtml(url)),Savepath) 131 if index-1==1000: 132 break