最近由於公司業務的需要,我開始做了一些簡單的爬蟲的工作。學了爬蟲之後,就在想,能不能用爬蟲做一些有趣的事情呢?想來想去,還是教大家寫個簡單的爬蟲,用來爬取一組美女圖。
因為只有美女圖才能吸引你們這些紳士過來圍觀。。。。。
最後附上源碼,拿去直接就可以用了。
目標
我在 bing 上搜索 美女圖,出來了這個。
那我們就用這個來開刀吧。
額。。。。。。。。
這尺度,我們還是選個保守一點的來做例子吧。。。。。。
配置環境
由於我們是使用 python 來做爬蟲的,所以我們要先裝好 python 環境,這個比較簡單,直接到官網下載安裝就可以了,安裝的過程也是一直下一步就行。
下載地址: https://www.python.org/downloads/
我們選擇相應的操作系統平臺安裝就可以了。
還有一個是 python 的網絡請求庫 requests ,我們後面將要使用這個庫進行網絡請求,來爬取數據。
這個庫的安裝也很簡單,在命令行下執行以下命令就可以了
pip3 install requests
還有一個是用來解析 html 結構,和提取元素內容的一個庫。安裝也很簡單
pip3 install beautifulsoup4
開始分析
在 Chrome(谷歌瀏覽器) 下,打開我們要爬取的地址 https://www.walltu.com/tuku/201801/212736.html , 然後按 F12 ,在網絡這一欄,我們可以看到這個頁面的請求。
然後,再切到 XHR 下,發現這個頁面並沒有發 ajax ,也就是說,我們要爬取的圖片地址和頁面可能是在一起的。
我們看一下這個 html 頁面裡面是什麼東西。
然後我們在使用元素選擇器,看一下我們要爬取的圖片是哪個元素,然後我們再到 html 代碼裡面找一下,找到這個圖片的元素
找到了,然後我們再到網絡請求到頁面裡面看一下,看能不能找到這個元素,果然找到了。
我們再瀏覽器直接打開這個地址:
那就說明可以通過地址直接獲取到圖片,那我們就開始了。
開始幹活
到這裡,我們就要開始正式寫代碼了,但是不要擔心,很簡單的。老司機帶路肯定是穩的!
先從下載圖片開始
我們先試一下,看能不能直接請求到圖片。打印一下請求的狀態碼和內容。
import requests response = requests.get('https://img.walltu.com/d/file/shutu/2018-01/20150813115821611.jpg!800') print(response.status_code) print(response.content)
打印結果如下:
403 b'{"code":"40310014","msg":"invalid Referer header"}'
請求失敗了,但是提示我們 referer 非法。說起來也是挺好笑的,居然會提示我們錯誤的地方,這是怕我們爬不到數據嗎。。。。。。
知道問題在哪就好搞了。我們找一下這個信息要填什麼。
切換之前頁面的調試界面,選中 img ,看一下頁面上請求時用的是什麼,照抄過來就行了。
然後把這個東西加到請求頭試一下
import requests headers = { 'Referer':'https://www.walltu.com/tuku/201801/212736.html' } response = requests.get('https://img.walltu.com/d/file/shutu/2018-01/20150813115821611.jpg!800',headers=headers) print(response.status_code) print(response.content)
上面的代碼應該很容易看懂吧,即使沒有學過 python 。就是在請求的時候多添加了一個請求頭的參數
打印內容如下:
200 b'\xff\xd8\\xea\xe3o\x88...............1\x12\xc1\x1b\x19)\xbcw\x139u\x0eLD\xd8l\x02o\x81\x12c&\xc4\xc9\xf2MT\x7f\xff\xd9'
我們看到狀態碼是 200 ,說明成功了,後面打印的是圖片的二進制數據,由於圖片轉化成二進制內容很多,中間的部分信息被我省略了。
保存圖片
剛才我們通過 requests 請求到了圖片,但是二進制的圖片給我們沒有用啊,我們要看圖片,真正的圖片!
那我們就要把圖片保存起來。我們先打開一個文件,然後把請求到的內容寫入到文件裡面就行了。
import requests headers = { 'Referer':'https://www.walltu.com/tuku/201801/212736.html' } response = requests.get('https://img.walltu.com/d/file/shutu/2018-01/20150813115821611.jpg!800',headers=headers) # 打開一個文件,第一個參數是文件路徑,第二參數是打開方式,'wb' 表示以二進制寫入的方式打開 img_file = open('img.jpg','wb') # 然後往文件裡面寫內容 img_file.write(response.content)
這段代碼執行完,應該就可以在同個目錄下找到下載好的 img.jpg 圖片,打開就能看見下載好的圖片的了。是不是很簡單呢。
目標是爬取一組圖
我們搞半天,只是把一張圖片下載到本地而已,這麼麻煩,還不如直接在頁面上右擊保存圖片來得快呢。。。。。。
我們的目標可不是下載一張圖片,而是一整組圖片!
從第一張圖片開始爬取
第一張圖片剛才不是下載完了?
不不不,我們要是輸入這一組圖片的網頁地址,就可以下載這一組圖片的效果。也就是輸入 https://www.walltu.com/tuku/201801/212736.html 就可以下載一整組圖的效果。
要實現這個效果,可就不能像之前那樣手動去找圖片的地址了,要通過代碼找到圖片的地址。那我們就需要使用一些工具來幫我們找到這個地址了,我這裡使用的是 BeautifulSoup 來進行元素查找。
我們先看一下剛才查找的那個圖
觀察這個結構,發現比較靠近目標並且有特點的元素是 ,我們只要找到這個元素,再順藤摸瓜,往下找到下面的
然後再往裡面就可以找到我們要的 這個元素了。
代碼上,我們可以這麼寫:
import requests from bs4 import BeautifulSoup # 請求這個頁面 response = requests.get('https://www.walltu.com/tuku/201801/212736.html') # BeautifulSoup 的構造函數最少需要兩個參數,第一個是要解析的內容,第二個是解析器,這裡我們使用的是 'html.parser' content = BeautifulSoup(response.content, 'html.parser') target_parent = content.find('dd', class_='p') target = target_parent.p.img img_address = target['src'] print(img_address)
打印結果如下:
https://img.walltu.com/d/file/shutu/2018-01/20150813115821611.jpg!800
我們這樣就拿到了第一個頁面的圖片地址。
查找下一個
我們的最終目標的是爬取一整組圖。找到了第一個圖片,那就要準備找下一個了。
我想很多人想到的是圖片右邊的 下一圖 這個按鈕,但是有個問題,就是到這組圖的最後一張後,這個按鈕的鏈接就會變成下一組圖的地址了,這樣就會一直循環下去,這可不行。
我把目標轉到了下面。
我們發現當前的圖片是有個選中的樣式的,我們應該可以找到這個元素,然後找到下一個元素,也就是下一個圖片的頁面的鏈接。這個會不會有最後一張的地址變成下一組的地址 這個問題呢?
我們看一下:
好像沒有,最後一個還是最後一個,沒有多出一個是跳下一組圖的元素。我們還得看一下 html 。
確實沒有一些多餘的東西,而且我們還發現了選中的樣式是 class="c",那就開始幹活!
import requests from bs4 import BeautifulSoup # 請求這個頁面 response = requests.get('https://www.walltu.com/tuku/201801/212736.html') # BeautifulSoup 的構造函數最少需要兩個參數,第一個是要解析的內容,第二個是解析器,這裡我們使用的是 'html.parser' content = BeautifulSoup(response.content, 'html.parser') # 找到當前選中的圖片 index = content.find('a', class_='c') # 找到下一個圖片的元素 next_target = index.next_sibling if next_target is None: # 沒有下一個了 print('沒有下一個了') else: # 找到了下一個,我們就打印出來 print('下一個') next_addr = 'https://www.walltu.com' + next_target['href'] print(next_addr)
打印結果:
下一個 https://www.walltu.com/tuku/201801/212736_2.html
如果是最後一個呢?我們也看一下,我們把地址換成 https://www.walltu.com/tuku/201801/212736_7.html
再執行一次,打印結果
沒有下一個了
再配合上之前下載圖片的代碼,應該就可以把一整組圖給下載下來了。我們還需要一個文件夾把這些圖片給整理放好,其實還有剩下的兩步工作,就是新建一個文件夾,用來存放圖片,然後下載圖片的時候再給每個圖片命名,否則重複的文件名會把之前的圖片給覆蓋掉。
新建文件夾
新建文件夾很簡單
import os # 創建文件夾 os.makedirs('文件夾的名稱')
用來存圖片的目錄名就用第一個頁面的標題吧。
給圖片命名
要給圖片命名,就要考慮唯一性,從圖片的下載地址上看,這些名稱確實是唯一的,但是都是一些隨機的名稱,也就是沒有順序,感覺也不太好,因為這些圖片實際上是有一定順序的。
實在不行,那就用頁面的地址吧。因為一個圖片對應的也是一個頁面,而且我們看一下這些頁面地址。
https://www.walltu.com/tuku/201801/212736.html https://www.walltu.com/tuku/201801/212736_2.html https://www.walltu.com/tuku/201801/212736_3.html https://www.walltu.com/tuku/201801/212736_4.html
我們發現,這些還是有順序的,剛好滿足我們的需求。
在下載圖片的時候,傳入名稱就行了。這裡就不上代碼了。
整理一下思路
- 根據第一個頁面的標題新建一個文件夾,用來存放圖片。
- 解析頁面,下載第一張圖片,獲取下一個頁面的地址
- 循環第二步,直到下載完最後一個圖片。
整理一下代碼
我們最後整理一下代碼,把一些操作抽取成方法。
import os import requests from bs4 import BeautifulSoup # 下載所有圖片 def downloadAllImg(address): # 解析頁面 content = getContent(address) # 獲取頁面標題 title = content.title.string # 新建文件夾 os.makedirs(title) # 獲取圖片的地址 download_img_addr = getDownloadImgAddr(content) # 獲取文件名 file_name = getFileName(address) print('開始下載:'+title) while download_img_addr is not None: # 下載圖片 downloadImg(download_img_addr,address,title,file_name) # 獲取下一個頁面的地址 next_addr = getNextAddr(content) if next_addr is None: download_img_addr = None else: # 獲取下一個圖片的名稱,並賦值給 變量 file_name = getFileName(next_addr) # 解析下一個頁面,並賦值給 變量 content = getContent(next_addr) # 獲取下一個圖片的下載地址,並賦值給 變量 download_img_addr = getDownloadImgAddr(content) print('新下載地址:'+download_img_addr) print('已全部下載完成') # 解析頁面 def getContent(address): response = requests.get(address) return BeautifulSoup(response.content, 'html.parser') # 獲取圖片的地址 def getDownloadImgAddr(content): target_parent = content.find('dd', class_='p') target = target_parent.p.img img_address = target['src'] return img_address # 獲取下一個頁面的地址 def getNextAddr(content): index = content.find('a', class_='c') next_target = index.next_sibling if next_target is None: print('沒有下一個了') return None else: print('下一個') next_addr = 'https://www.walltu.com' + next_target['href'] print(next_addr) return next_addr # 獲取文件的名稱 def getFileName(address): end_index = address.rfind('.') start_index = address.rfind('/') return address[start_index + 1:end_index] # 下載圖片 def downloadImg(imgAddress,fromAddr,dir,file_name): print('正在下載:'+file_name) img = open(dir+'/'+file_name+'.jpg', 'wb') headers = { 'Referer': fromAddr } img_response = requests.get(imgAddress, headers=headers) img.write(img_response.content) img.close() print('下載完成:'+imgAddress) # 調用,傳入要下載的圖片的地址 downloadAllImg('https://www.walltu.com/tuku/201801/212736.html')
代碼到這裡就完成了,想要下載其他組圖,只要換一下,最後調用的地址就可以了。
最後,我們看一下效果吧。成功下載到了一整組圖。
大家快去試一下吧!