python——圖片爬蟲:爬取愛女神網站(www.znzhi.net)上的妹子圖 進階篇
阿新 • • 發佈:2019-02-07
我講解了圖片爬蟲的基本步驟,並實現了爬蟲程式碼
在本篇中,我將帶領大家對基礎篇中的程式碼進行改善,加入多執行緒,提高爬取效率。
首先我們明確一個改進的思路,就是在函式downloadAlbum(url)中:
將for迴圈體中,呼叫downloadOnePic()的操作放到子執行緒中,然後迴圈建立子執行緒,即有n個圖片,迴圈建立n個子執行緒,相當於n個圖片同時處於下載狀態# 迴圈下載專輯中各個圖片 for num in range(1, pic_count+1): pic_url = url+"/"+str(num)+'.html' i=0 while downloadOnePic(path, pic_url) == False: print 'redownload' i=i+1 if i > 5: print "timeout's time too much" break
一、新建子執行緒類
# 迴圈下載專輯中各個圖片 for num in range(1, pic_count+1): pic_url = url+"/"+str(num)+'.html' i=0 while downloadOnePic(path, pic_url) == False: print 'redownload' i=i+1 if i > 5: print "timeout's time too much" break
二、迴圈建立子執行緒
# 迴圈下載專輯中各個圖片,對每個圖片的下載各開啟一個執行緒 for num in range(1, pic_count+1): pic_url = url+"/"+str(num)+'.html' # 建立新的子執行緒 threadD = threadDownload(path,pic_url) # 開啟子執行緒 threadD.start() # 在主執行緒中迴圈查詢當前正在活動的執行緒數量 while threading.active_count() != 0: # 當正在活動的執行緒數量為1,即只剩主執行緒時,表示所有子執行緒都已關閉,即所有圖片下載完畢 if threading.active_count() == 1: print ' all pic has downloaded of this page:' + url return True
在原本for迴圈體呼叫downloadOnePic()的地方改為建立新的子執行緒,並啟動
然後用while迴圈查詢當前正在活動的執行緒數量,並在迴圈體中判斷當前的執行緒數量,當活動執行緒數量等於1,即只剩主執行緒時,表示所有子執行緒已關閉,所有圖片下載完畢,此時return退出
此爬蟲已經改進完成,通過使用多執行緒提高爬取效率。若還有其他可改進的地方,歡迎交流學習。
完整程式碼如下:
# coding:utf8
# python環境2.7
# 爬取網站:http://www.znzhi.net/
# author:CodeZ
import os
import re
import threading
import urllib2
import time
from bs4 import BeautifulSoup
BASE_PATH = 'picture'
HOST_HOT = 'http://www.znzhi.net/hot'
HOST_ALBUM = 'http://www.znzhi.net/p'
MAX_PAGE_NUM = 387
class threadDownload(threading.Thread):
def __init__(self,path,url):
threading.Thread.__init__(self)
# 路徑引數
self.path = path
# url引數
self.url = url
def run(self):
i=0
while downloadOnePic(self.path, self.url) == False:
print 'redownload'
i=i+1
if i > 5:
print "timeout's time too much"
break
def downloadUrl(url):
# 捕獲異常(超時)
try:
# 開啟網頁
response = urllib2.urlopen(url, timeout=10)
# 設定編碼方式
response.encoding = 'utf-8'
# 判斷http請求的狀態
if response.getcode() == 200:
# 狀態正常(200),返回頁面資料
return response.read()
else:
# 失敗,列印訊息,返回空資料
print "error:url visit failed"
return ''
except Exception, e:
# 列印異常
print "exception:"+e.message
print "reopen"
# 重新下載
return downloadUrl(url)
# 獲取相簿名稱
def getPicName(picUrl) :
# 擷取地址中最後一個/後面的字元,即圖片名
picName = os.path.basename(picUrl)
if '.jpg' in picName:
return picName
return 'error.jpg'
# 下載單張照片
def downloadOnePic(path,url):
soup = BeautifulSoup(downloadUrl(url),
'html.parser',
from_encoding='utf-8')
# 獲取存有img節點
img_node = soup.find('div', class_='main-image').find('img')
# 獲取img的src值,即圖片地址
pic_url = img_node.get('src')
# 呼叫getPicName()獲取圖片名稱
pic_name = getPicName(pic_url).encode('utf-8')
try:
# 訪問圖片地址,獲取資料
content = urllib2.urlopen(pic_url, timeout=10).read()
# 儲存圖片到本地
with open(path + '/' + pic_name, 'wb') as code:
code.write(content)
print ' -> ' + pic_name + " download success"
#捕獲異常
except Exception, e:
print "exception:"+e.message
return False
return True
def downloadAlbum(url):
print "album:"+url
# 獲取當前頁面資料
content = downloadUrl(url)
# 傳入頁面資料content,建立beautifulsoup物件soup
soup = BeautifulSoup(content,
'html.parser',
from_encoding='utf-8')
# 獲取存有圖片專輯標題的h2標籤
title = soup.find('div', class_='content').find('h2')
# 檢查是否有內容,在實際爬取中,有遇到過空圖片專輯的情況,
if title == None:
print "error:web content has lost"
return
# 通過正則篩選出標題中含有的總圖片數值
title_num = re.findall(r'\d+', title.get_text())
pic_count = int(title_num[-1])
# 將(1/num)擷取去除,並新增總圖片數 [num]
title_split = title.get_text().split(' (', 1)
album_title = title_split[0]+'['+str(pic_count)+']'
# 刪去標題中的'/'字元,防止在用標題作為名稱建圖片資料夾時報錯
album_title = album_title.replace('/', ' ')
# 拼接本地資料夾路徑,並檢查路徑是否存在,防止重複下載
path = BASE_PATH + "/" + album_title
if os.path.exists(path):
print ' -> ' + album_title + ' has exists'
return True
# 新建存放當前專輯的圖片資料夾
checkDocuments(path)
print path
# 新建一個html,存有此圖片專輯相關資訊
with open(path+'/source.html','w') as fout:
fout.write("<html>")
fout.write("<body>")
fout.write("<p>"+album_title.encode('utf-8')+"-["+str(pic_count)+"p]"+"</p>")
fout.write("<a href=\""+url.encode('utf-8')+"\">來源網址:"+url.encode('utf-8')+"</a>")
fout.write("</body>")
fout.write("</html>")
# 迴圈下載專輯中各個圖片,對每個圖片的下載各開啟一個執行緒
for num in range(1, pic_count+1):
pic_url = url+"/"+str(num)+'.html'
# 建立新的子執行緒
threadD = threadDownload(path,pic_url)
# 開啟子執行緒
threadD.start()
# 在主執行緒中迴圈查詢當前正在活動的執行緒數量
while threading.active_count() != 0:
# 當正在活動的執行緒數量為1,即只剩主執行緒時,表示所有子執行緒都已關閉,即所有圖片下載完畢
if threading.active_count() == 1:
print ' all pic has downloaded of this page:' + url
return True
def downloadPage(url):
print "page:"+url
# 獲取當前頁面資料
content = downloadUrl(url)
# 傳入頁面資料content,建立beautifulsoup物件soup
soup = BeautifulSoup(content,
'html.parser',
from_encoding='utf-8')
# 獲取單頁中18個圖片專輯的父節點
album_block = soup.find('ul', id='images')
# 獲取父節點下圖片專輯地址的a節點集
album_nodes = album_block.findAll('a', href=re.compile(r'http://www.znzhi.net/p/'))
# 由於每個專輯的a標籤有兩個,用[::2]獲取a節點集中的偶數項,迴圈下載圖片專輯
for album_node in album_nodes[::2]:
# 呼叫downloadAlbum
# 傳入album_node.get('href')獲取a節點的href值,即專輯地址
downloadAlbum(album_node.get('href'))
# 若執行中想終止爬蟲程式,可在同父目錄下新建stop.txt檔案
if os.path.exists('stop.txt'):
exit(0)
# 設定圖片專輯下載間隙休眠,防止因訪問頻繁,被網站拉黑
time.sleep(4)
# 檢查本地檔案路徑是否存在,不存在則建立
def checkDocuments(path):
if os.path.exists(path) == False:
os.mkdir(path)
# main函式
if __name__ == "__main__":
# 檢查本地下載路徑是否存在
checkDocuments(BASE_PATH)
# 迴圈訪問
for i in range(1, MAX_PAGE_NUM+1):
# 拼接頁地址,格式為:http://www.znzhi.net/hot/頁碼.html
page_url = HOST_HOT+'/'+str(i)+'.html'
# 儲存當前頁碼,供檢視下載進度
with open('cur_page.txt', 'w') as fpage:
fpage.write(str(i))
# 以頁為單位進行下載
downloadPage(page_url)