用Python多線程爬取圖片並下載到本地

1、圖片信息的來源

彼岸桌面    網址爲:http://www.netbian.com/

2、分析網站

(1)構造頁面的url列表

我們需要做的是爬取網站上給定頁數的圖片,所以,我們首先需要的就是觀察各個頁面鏈接之間的關係,進而構造出需要爬取頁面的url列表。

第一頁的鏈接:http://www.netbian.com/

第二頁的鏈接:http://www.netbian.com/index_2.htm

......

可以看出,從第二頁開始之後的頁面鏈接只是後面的數字不同,我們可以寫個簡單的代碼,獲取頁面的url列表

# 頁面鏈接的初始化列表
page_links_list=['http://www.netbian.com/']

#獲取爬取的頁數和頁面鏈接

pages = int(input('請輸入你想爬取的頁數:'))
if pages > 1:
    for page in range(2, pages + 1):
        url = 'http://www.netbian.com/index_' + str(page) + '.htm'
        page_links_list.append(url)
else:
    page_links_list=page_links_list
print(page_links_list)

結果如下:

請輸入你想爬取的頁數:5
['http://www.netbian.com/', 'http://www.netbian.com/index_2.htm', 'http://www.netbian.com/index_3.htm', 'http://www.netbian.com/index_4.htm', 'http://www.netbian.com/index_5.htm']

(2)獲取一個頁面中所有的圖片的鏈接

我們已經獲取了所有頁面的鏈接,但是沒有獲取每張圖片的鏈接,所以,接下來我們需要做的就是獲取一個頁面中的所有圖片的鏈接。在這裏,我們以第一頁爲例,獲取每張圖片的鏈接,其他頁面類似。

首先在頁面中右鍵->查看元素,然後點擊查看器左邊的那個小光標,再把鼠標放在隨意一個圖片上,這樣就定位到這個圖片所在的代碼位置了;我們可以知道,每個頁面有18張圖片,接下來,我們需要採用標籤去定位頁面中的圖片的具體位置,如下圖所示,我們使用 div.list li a img 剛好定位到了18個img標籤。img標籤中就包含了我們需要的圖片鏈接。

接下來,我們以第一個頁面爲例,獲取每個圖片的鏈接。

import requests
from bs4 import BeautifulSoup

# 頁面鏈接的初始化列表
url='http://www.netbian.com/'
# 圖片鏈接列表
img_links_list = []

#獲取img標籤,在獲取圖片鏈接
html = requests.get(url).content.decode('gbk')
soup = BeautifulSoup(html, 'lxml')
imgs = soup.select("div.list li a img")
for img in imgs:
    img_link = img['src']
    img_links_list.append(img_link)

print(img_links_list)
print(len(img_links_list))

結果如下:

['http://img.netbian.com/file/2019/0817/smalle213d95e54c5b4fb355b710a473292ea1566035585.jpg', 'http://img.netbian.com/file/2019/0817/small15ca224d7c4c119affe2cfd2d811862e1566035332.jpg', 'http://img.netbian.com/file/2018/1225/604a688cd6f79161236e6250189bc25b.jpg', 'http://img.netbian.com/file/2019/0817/smallab7249d18e67c9336109e3bedc094f381566034907.jpg', 'http://img.netbian.com/file/2019/0817/small5816e940e6957f7db5e499de9978bda41566031298.jpg', 'http://img.netbian.com/file/2019/0817/smalladda3febb072e9103f8f06f27dcb19c21566031139.jpg', 'http://img.netbian.com/file/2019/0817/small0e9f43492debe6dc2ce7a3e6cc48c1ad1566030965.jpg', 'http://img.netbian.com/file/2019/0817/smallcfd5b4c6fa10ffcbcdcc8b1b9e6db91a1566030209.jpg', 'http://img.netbian.com/file/2019/0817/smalld1f07e215f0da059b44d27623ec6fa8f1566029835.jpg', 'http://img.netbian.com/file/2019/0817/small1674b7b97714672be3165bd31de418eb1566014363.jpg', 'http://img.netbian.com/file/2019/0814/small1a5c2fe49dec02929f219d0bdb680e9c1565786931.jpg', 'http://img.netbian.com/file/2019/0814/smalle333c0a8e9fe18324d793ce7258abbbf1565786718.jpg', 'http://img.netbian.com/file/2019/0814/smallb0c9494b4042ac9c9d25b6e4243facfd1565786402.jpg', 'http://img.netbian.com/file/2019/0814/small19dfd078dd820bb1598129bbe4542eff1565786204.jpg', 'http://img.netbian.com/file/2019/0808/smallea41cb48c796ffd3020514994fc3e8391565274057.jpg', 'http://img.netbian.com/file/2019/0808/small3998c40805ea6811d81b7c57d8d235fc1565273792.jpg', 'http://img.netbian.com/file/2019/0808/smallb505448b1318dbb2820dcb212eb39e191565273639.jpg', 'http://img.netbian.com/file/2019/0808/small0f04af422502a40b6c8dc19d53d1f3481565273554.jpg']
18

(3)將圖片下載到本地

有了圖片鏈接後,我們需要將圖片下載到本地,在這裏我們以第一張圖片爲例進行下載

url:http://img.netbian.com/file/2019/0817/smalle213d95e54c5b4fb355b710a473292ea1566035585.jpg

import urllib.request
url='http://img.netbian.com/file/2019/0817/smalle213d95e54c5b4fb355b710a473292ea1566035585.jpg'
urllib.request.urlretrieve(url, filename='test.jpg')

(4)獲取圖片的簡單爬蟲

結合以上三個部分,構造頁面的url列表、獲取一個頁面中的所有圖片鏈接和將圖片下載到本地。構造一個完整但效率不高的爬蟲。

import requests
from bs4 import BeautifulSoup
import lxml
import urllib
import os
import time

#獲取圖片並下載到本地
def GetImages(url):
    html=requests.get(url, timeout = 2).content.decode('gbk')
    soup=BeautifulSoup(html,'lxml')
    imgs=soup.select("div.list li a img")
    for img in imgs:
        link=img['src']
        display=link.split('/')[-1]
        print('正在下載:',display)
        filename='./images/'+display
        urllib.request.urlretrieve(link,filename)

#獲取爬取的頁數,返回鏈接數
def GetUrls(page_links_list):
    pages = int(input('請輸入你想爬取的頁數:'))
    if pages > 1:
        for page in range(2, pages + 1):
            url = 'http://www.netbian.com/index_' + str(page) + '.htm'
            page_links_list.append(url)
    else:
        page_links_list=page_links_list


if __name__ == '__main__':
    page_links_list=['http://www.netbian.com/']
    GetUrls(page_links_list)
    os.mkdir('./images')
    print("開始下載圖片!!!")
    start = time.time()
    for url in page_links_list:
        GetImages(url)
    print('圖片下載成功!!!')
    end = time.time() - start
    print('消耗時間爲:', end)

結果如下:

請輸入你想爬取的頁數:5
開始下載圖片!!!
正在下載: smalle213d95e54c5b4fb355b710a473292ea1566035585.jpg
正在下載: small15ca224d7c4c119affe2cfd2d811862e1566035332.jpg
正在下載: 604a688cd6f79161236e6250189bc25b.jpg
正在下載: smallab7249d18e67c9336109e3bedc094f381566034907.jpg
正在下載: small5816e940e6957f7db5e499de9978bda41566031298.jpg
正在下載: smalladda3febb072e9103f8f06f27dcb19c21566031139.jpg
正在下載: small0e9f43492debe6dc2ce7a3e6cc48c1ad1566030965.jpg
正在下載: smallcfd5b4c6fa10ffcbcdcc8b1b9e6db91a1566030209.jpg
。。。。。。
圖片下載成功!!!
消耗時間爲: 21.575999975204468

以上這部分代碼就可以完整的運行了,但是效率不高,因爲是下載圖片,需要一個個排隊進行下載。所以,爲了解決這個問題,下面的代碼我們就使用了多線程來實現圖片的爬取和下載。

(5)用Python多線程爬取圖片並下載到本地

多線程我們使用的是Python自帶的threading模塊。並且我們使用了一種叫做生產者和消費者的模式,生產者專門用來從每個頁面中獲取圖片的下載鏈接存儲到一個全局列表中。而消費者專門從這個全局列表中提取圖片鏈接進行下載。

需要注意的是,在多線程中使用全局變量要用鎖來保證數據的一致性。

import urllib
import threading
from bs4 import BeautifulSoup
import requests
import os
import time
import lxml

# 頁面鏈接的初始化列表
page_links_list=['http://www.netbian.com/']
# 圖片鏈接列表
img_links_list = []

#獲取爬取的頁數和頁面鏈接
def GetUrls(page_links_list):
    pages = int(input('請輸入你想爬取的頁數:'))
    if pages > 1:
        for page in range(2, pages + 1):
            url = 'http://www.netbian.com/index_' + str(page) + '.htm'
            page_links_list.append(url)
    else:
        page_links_list=page_links_list

#初始化鎖,創建一把鎖
gLock=threading.Lock()

#生產者,負責從每個頁面中獲取圖片的鏈接
class Producer(threading.Thread):
    def run(self):
        while len(page_links_list)>0:
            #上鎖
            gLock.acquire()
            #默認取出列表中的最後一個元素
            page_url=page_links_list.pop()
            #釋放鎖
            gLock.release()

            #獲取img標籤
            html = requests.get(page_url).content.decode('gbk')
            soup = BeautifulSoup(html, 'lxml')
            imgs = soup.select("div.list li a img")

            #加鎖3
            gLock.acquire()
            for img in imgs:
                img_link = img['src']
                img_links_list.append(img_link)
            #釋放鎖
            gLock.release()
        #print(len(img_links_list))

#消費者,負責從獲取的圖片鏈接中下載圖片
class Consumer(threading.Thread,):
    def run(self):
        print("%s is running"%threading.current_thread())
        while True:
            #print(len(img_links_list))
            #上鎖
            gLock.acquire()
            if len(img_links_list)==0:
                #不管什麼情況,都要釋放鎖
                gLock.release()
                continue
            else:
                img_url=img_links_list.pop()
                #print(img_links_list)
                gLock.release()
                filename=img_url.split('/')[-1]
                print('正在下載:', filename)
                path = './images/'+filename
                urllib.request.urlretrieve(img_url, filename=path)
                if len(img_links_list)==0:
                    end=time.time()
                    print("消耗的時間爲:", (end - start))
                    exit()


if __name__ == '__main__':
    GetUrls(page_links_list)
    os.mkdir('./images')
    start=time.time()
    # 5個生產者線程,去從頁面中爬取圖片鏈接
    for x in range(5):
        Producer().start()

    # 10個消費者線程,去從中提取下載鏈接,然後下載
    for x in range(10):
        Consumer().start()

結果如下:

請輸入你想爬取的頁數:5
<Consumer(Thread-6, started 5764)> is running
<Consumer(Thread-7, started 9632)> is running
<Consumer(Thread-8, started 9788)> is running
<Consumer(Thread-9, started 5468)> is running
<Consumer(Thread-10, started 9832)> is running
<Consumer(Thread-11, started 3320)> is running
<Consumer(Thread-12, started 6456)> is running
<Consumer(Thread-13, started 10080)> is running
<Consumer(Thread-14, started 3876)> is running
<Consumer(Thread-15, started 9224)> is running
正在下載: small9e75d6ac9506efe1d87e96062791fb261564149099.jpg
正在下載: small02961cac5e02a901b77779eaed43c6f91564156941.jpg
正在下載: small117c84b2f427c981bf33184c1c5c4bb91564193581.jpg
正在下載: smallfedb420af6f753512c169021587982621564455847.jpg
正在下載: small14d3739bf11dd92055abb56e3f792d3f1564456102.jpg
正在下載: smallaf755644af7d114d4cbf36fbe0e84d0c1564456347.jpg
正在下載: small9f7af6d0e2372a4d9e536d8ea9fc40341564456537.jpg
。。。。。。
消耗的時間爲: 1.635000228881836       #分別是10個進程結束的時間
消耗的時間爲: 1.6419999599456787
消耗的時間爲: 1.6560001373291016
消耗的時間爲: 1.684000015258789
消耗的時間爲: 1.7009999752044678
消耗的時間爲: 1.7030000686645508
消耗的時間爲: 1.7060000896453857
消耗的時間爲: 1.7139999866485596
消耗的時間爲: 1.7350001335144043
消耗的時間爲: 1.748000144958496

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章