【Python網絡爬蟲四】經過關鍵字爬取多張百度圖片的圖片

最近看了女神的新劇《逃避雖然可恥但有用》,一樣男主也是一名程序員,因此頗有共鳴html

被大隻蘿莉萌的一臉一臉的,咱們來爬一爬女神的皁片。python

百度搜索結果:新恆結衣程序員

 

本文主要分爲4個部分:json

  1.下載簡單頁面瀏覽器

  2.爬取多張圖片服務器

  3.頁面解碼app

  4.爬取過程排錯處理ide

 

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。用爬蟲獲取不到圖片post

第四種objURL是指圖片的源網址,獲取該網址會出現三種狀況:

  1. 正常。繼續下載
  2. 403 Forbidden。用continue跳過。
  3. 出現異常。用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 只容許一部分 ASCII 字符(數字字母和部分符號),其餘的字符(如漢字)是不符合 URL 標準的。
因此 URL 中使用其餘字符就須要進行 URL 編碼。

URL 中傳參數的部分(query String),格式是:
name1=value1&name2=value2&name3=value3
假如你的 name 或者 value 值中有『&』或者『=』等符號,就固然會有問題。因此URL中的參數字符串也須要把『&=』等符號進行編碼。

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
View Code