python多執行緒爬取桌布 媽媽再也不擔心我沒桌布了!

2020-10-20 12:00:53

基於上次的簡單爬蟲之後,這次的爬蟲新增了多執行緒的新元素,使爬取的速度在原來的基礎上快了N倍,話不多說,來看程式碼
首先我們選擇的網站還是上次的H128桌布,不知道具體流程的可以看下上次寫的入門程式碼
傳送門!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
開啟網站,這裡我選擇的是動漫專區的桌布,我們的目的是把所有動漫桌布爬下來,我們發現一共有98頁圖片
在這裡插入圖片描述

所以我們要做的是觀察每頁圖片連結的關係,我們開啟第二頁圖片觀察
發現兩頁圖片的連結分別是
https://www.h128.com/pc/anime/0/2/1920x1080/t/1.html
https://www.h128.com/pc/anime/0/2/1920x1080/t/2.html

我們發現兩個網頁只有t/後面的資料不同由此我們觀察後面幾頁,最終我們發現/t/後面的數位就是代表頁數,所以在最開始我們建立一個函數來存放我們需要的網頁連結
如下:

page_links_list = ['https://www.h128.com/pc/anime/0/2/1920x1080/t/1.html']
def GetUrls(page_links_list):
    pages = int(input("請輸入你想爬取的頁數:"))
    if pages > 1:
        for page in range(2, pages + 1):
            url = 'https://www.h128.com/pc/anime/0/2/1920x1080/t/' + str(page) + '.html'
            page_links_list.append(url)
    else:
        page_links_list = page_links_list

然後就是我們多執行緒的應用了,我們要用的是python的threading模組首先需要匯入threading

import threading

首先建立一個glock 用來控制

gLock = threading.Lock()

**threading 提供了 Lock 類,該類能夠在某個執行緒存取某個變數的時候對變數加鎖,此時其它執行緒就不能存取該變數,直到該 Lock 被釋放其它執行緒才能夠存取該變數
**
我們爬蟲需要生產者程序和消費者程序,生產者的執行緒專門用來生產一些資料,然後存放到一箇中間的變數中。消費者再從這個中間的變數中取出資料進行消費。但是因為要使用中間變數,中間變數經常是一些全域性變數,因此需要使用鎖來保證資料完整性。在這個程式碼中生產者程序負責來獲取我們圖片的url,而消費者程序的目的是下載圖片。
生產者程式碼如下:

class Generant(threading.Thread):
    def run(self):
        while len(page_links_list) > 0:
            gLock.acquire()  #上鎖
            page_url = page_links_list.pop()
            gLock.release() #釋放鎖
            r = requests.get(page_url,headers = headers)
            r.raise_for_status()
            r.encoding = r.apparent_encoding
            a = re.findall('<img src="https:(.*?)" alt',r.text)
            gLock.acquire() #上鎖
            for i in a :
                x = 'https:' + i
                x = x.replace('w_487', 'w_1421').replace('h_274', 'h_799')
                img_links_list.append(x)
            gLock.release() #釋放鎖

消費者程式碼如下

class Consumer(threading.Thread,):
    def run(self):
        while True:
            gLock.acquire()
            if len(img_links_list) == 0:
                gLock.release()
                continue
            else:
                img_url = img_links_list.pop()
                gLock.release()
                filename = img_url.split('?')[0].split('/')[-1]
                r = requests.get(img_url)
                print('正在下載:', filename)
                path = './picture/' + filename
                with open(path,'wb') as f:
                    f.write(r.content)
                    f.close()
                if len(img_links_list) == 0:
                    end = time.time()
                    print("消耗的時間為:", (end - start))
                    exit()

最後的程式碼就是啟動執行緒

    for x in range(5):
        Generant().start()
    for x in range(5):
        Consumer().start()

觀看執行結果:

在這裡插入圖片描述
這裡是下載了50頁圖片的時間,比起單執行緒還是很快的。
在這裡插入圖片描述
最後附上完整程式碼
下面展示一些 內聯程式碼片

import threading
import requests
import re
import  time
import  os
page_links_list = ['https://www.h128.com/pc/anime/0/2/1920x1080/t/1.html']
img_links_list = []
headers = {
        "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
}
def GetUrls(page_links_list):
    pages = int(input("請輸入你想爬取的頁數:"))
    if pages > 1:
        for page in range(2, pages + 1):
            url = 'https://www.h128.com/pc/anime/0/2/1920x1080/t/' + str(page) + '.html'
            page_links_list.append(url)
    else:
        page_links_list = page_links_list
gLock = threading.Lock()
class Generant(threading.Thread):
    def run(self):
        while len(page_links_list) > 0:
            gLock.acquire()  #上鎖
            page_url = page_links_list.pop()
            gLock.release() #釋放鎖
            r = requests.get(page_url,headers = headers)
            r.raise_for_status()
            r.encoding = r.apparent_encoding
            a = re.findall('<img src="https:(.*?)" alt',r.text)
            gLock.acquire() #上鎖
            for i in a :
                x = 'https:' + i
                x = x.replace('w_487', 'w_1421').replace('h_274', 'h_799')
                img_links_list.append(x)
            gLock.release() #釋放鎖
class Consumer(threading.Thread,):
    def run(self):
        while True:
            gLock.acquire()
            if len(img_links_list) == 0:
                gLock.release()
                continue
            else:
                img_url = img_links_list.pop()
                gLock.release()
                filename = img_url.split('?')[0].split('/')[-1]
                r = requests.get(img_url)
                print('正在下載:', filename)
                path = './picture/' + filename
                with open(path,'wb') as f:
                    f.write(r.content)
                    f.close()
                if len(img_links_list) == 0:
                    end = time.time()
                    print("消耗的時間為:", (end - start))
                    exit()
if __name__ == '__main__':
    GetUrls(page_links_list)
    if os.path.exists('./picture'):
        print("檔案已存在")
    else:
        os.mkdir('./picture')
    start = time.time()
    for x in range(5):
        Generant().start()
    for x in range(5):
        Consumer().start()

最後如果想要全站的圖片只要把連結改一下就OK