本例通過多線程爬取http://www.doutula.com網站來講解如何讓使用多線程進行爬取。
首先打開這個網站,點選最新表情:
然後我們隻需要找出最新表情相關的規律,比如我們點選一下第二頁,url就變成了http://www.doutula.com/photo/list/?page=2,是以第幾頁page就等于幾。
然後就要找出在一頁當中這些表情是存儲在哪裡,先來看一下不用多線程的程式:
import requests
import os
from bs4 import BeautifulSoup
def download_img(page):
response = requests.get('http://www.doutula.com/photo/list/?page={}'.format(page))
text = response.text
soup = BeautifulSoup(text,'lxml')
div = soup.find_all('div',class_ = "page-content text-center")[0]
imgs = div.find_all('img')
for img in imgs:
try:
name = img.attrs['alt']#擷取img标簽的alt屬性值,也就是名字
except KeyError:
pass
try :
img_url = img.attrs['data-backup']
except KeyError:
pass
try:
R = requests.get(img_url)
except UnboundLocalError:
pass
try:
with open('圖檔1/{}.jpg'.format(name),'wb') as f:
f.write(R.content)
except:
pass
if __name__ == '__main__':
if not os.path.exists('圖檔1'):
os.mkdir('圖檔1')
for i in range(2000):
download_img(i+1)
如果是用多線程,就用一下結構:
首先在主程式當中吧每一個待爬取頁面的url定義好,然後生産者就從url隊列當中去擷取每一個url,再提取出每一個圖檔的url,拿到這些url以後再把url添加到全局的隊列當中,這個隊列是專門用來存儲每個表情的url的,存儲完了以後再使用消費者從這個隊列當中去取出每個表情的url,然後下載下傳到本地。
下面是其完整代碼:
import requests
import os
from bs4 import BeautifulSoup
from queue import Queue
import threading
#定義生産者類,在建立生産者線程的時候就把兩個隊列傳到這裡面來
class Procuder(threading.Thread):
def __init__(self,page_queue,img_queue,*args,**kwargs):#*args,**kwargs代表包括任意參數
super(Procuder,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
#用于解析url
def run(self):
#因為每一個線程都需要不斷地去從頁面隊列當中去取url來解析
while True:
#為了避免生産者線程一直處于死循環狀态,也就是當把url解析完畢時就應該停止了。當隊列為空時退出循環
if self.page_queue.empty():
break
url = self.page_queue.get()
#拿到url後就擷取我們想要的圖檔的url
self.parse_page(url)#去解析每一頁的url
def parse_page(self,url):
response = requests.get(url)
text = response.text
soup = BeautifulSoup(text,'lxml')
div = soup.find_all('div',class_ = "page-content text-center")[0]
imgs = div.find_all('img')
for img in imgs:
try:
name = img.attrs['alt']#擷取img标簽的alt屬性值,也就是名字
except KeyError:
pass
try :
img_url = img.attrs['data-backup']
except KeyError:
pass
#拿到每個圖檔的名稱和連結以後就要添加到隊列當中
try:
self.img_queue.put((name,img_url))#傳一個元組進去
except:
pass
#消費者
class Consumer(threading.Thread):
def __init__(self,page_queue,img_queue,*args,**kwargs):#*args,**kwargs代表包括任意參數
super(Consumer,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
#不斷地從img_queue裡面取出url然後下載下傳下來
def run(self):
while True:
if self.img_queue.empty() and self.page_queue.empty():
break
filename,img_url = self.img_queue.get()#因為傳回的是一個元組
try:
R = requests.get(img_url)
except UnboundLocalError:
pass
try:
with open('圖檔2/{}.jpg'.format(filename), 'wb') as f:
f.write(R.content)
print('{}下載下傳完成'.format(filename))
except:
pass
if __name__ == '__main__':
#定義兩個隊列
page_queue = Queue(100)#100個頁面的url隊列
img_queue = Queue(1000)#圖檔的url
if not os.path.exists('圖檔2'):
os.mkdir('圖檔2')
for i in range(100):
url = 'http://www.doutula.com/photo/list/?page={}'.format(i+1)
page_queue.put(url)#将每一頁的url添加進去
#然後建立生産者,把頁面請求下來再擷取到每個表情的url
for x in range(5):#建立5個生産者
t = Procuder(page_queue,img_queue)
t.start()
for x in range(5):#建立5個消費者
t = Consumer(page_queue,img_queue)
t.start()