requests+多線程(四)之 下載斗圖表情包

既然提到多線程,那么為何用多線程呢???打個比方,好比做某件事,單線程就是一個人去做,多線程呢就是有多個人去完成,那么排除掉如死鎖等特殊原因,當然多個人去完成某件事會比較快一些了。
多線程的最高境界是并行完成,當然一般是并發(fā)的,那么啥叫并發(fā)和并行呢?并行即同時去完成,并發(fā)就是有可能需要排隊有時就一起去完成。
Python中一般多線程的實現(xiàn)方式有兩種方式:1)繼承 threading.Thread 類并與 queue.Quque() 結合使用(分段完成) 2)調用函數(shù)
好了,接下來我們直接上案例:多線程下載斗圖網(wǎng)的表情包(https://www.doutula.com/article/list/?page=1)

一、導入我們所需的基本模塊以及請求源碼的函數(shù)

import requests, time, threading
from lxml import etree
import urllib.request
from multiprocessing.dummy import Pool as ThreadPool    # 這里注意的是:導入dummy 表示是多線程而非多進程


# 獲取源碼
def get_html(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'}
    response = requests.get(url=url, headers=headers)
    return response.text


二、接下來我們觀察下源碼,并使用 XPath 對源碼進行解析獲取跳轉鏈接:

4.png

這里值得提的是:最好 觀察源碼,因此在右鍵“檢查”中有些屬性是和源碼顯示的是不一樣的,觀察了源碼之后我們發(fā)現(xiàn)列表的10個選項中都有同一個 class 屬性值,因此代碼解析代碼函數(shù)如下:

# 獲取跳轉鏈接
def get_a_href(url):
    html = get_html(url)  # 獲取源碼
    selector = etree.HTML(html)  # 自動修正代碼
    data = selector.xpath('//a[@class="list-group-item random_list"]/@href')  # 獲取所有的跳轉鏈接
    for url in data:
        img_html = get_html(url)  # 獲取源碼
        get_img_src(img_html)  # 調用解析獲取圖片鏈接函數(shù)


在以上方法的源碼中的循環(huán)里面有兩個函數(shù),第一個函數(shù)就是剛剛定義的根據(jù) url 獲取源碼內容,第二個函數(shù)就是解析跳轉后源碼中的圖片鏈接,,,這里我建議還是先別直接寫著兩個函數(shù),最好先輸出測試一下看看我們的解析跳轉鏈接正確了木有,如果沒問題再做進一步的編寫,輸出效果如下表示沒問題:


5.png

三、接下來我們先觀察下跳轉后頁面效果及源代碼


6.png

觀察了源碼之后我們開始編寫解析全部圖片鏈接的函數(shù) <<<這里注意的是:待會兒我們進行單線程和多線程的切換是在這個函數(shù)進行的>>>:

# 解析獲取圖片鏈接
def get_img_src(html):
    selector = etree.HTML(html)  # 自動修正代碼
    img_src_list = selector.xpath('//tbody/tr[1]/td/a/img/@src')

    for url in img_src_list:
        print("全部圖片的鏈接為-> ", url)

    # for url in img_src_list:    #單線程執(zhí)行
    #     save_img(url)  # 下載圖片

    # start_thread_save_img(img_src_list)   # 調用多線程的函數(shù)


我們調用相應函數(shù)之后運行如下說明我們解析沒問題:


7.png

四、測試成功后,我們去掉數(shù)據(jù)循環(huán)語句,然后先去實現(xiàn)下單線程 的 save_img(url) 函數(shù)進行圖片的下載:

# 下載圖片
def save_img(img_url):
    print("正在下載鏈接 -> ", img_url)
    if img_url.startswith('//'):    # 防止長圖片沒有協(xié)議
        img_url = "https:" + img_url

    urllib.request.urlretrieve(img_url, 'C:/Users/Administrator/Desktop/doutuimage/%s' % img_url.split('/')[-1])  # 截取名字并下載到本地


五、為了方便觀察單線程與多線程下載的時間差,我們編寫一個標準化的輸出函數(shù):


def main():
    url = "https://www.doutula.com/article/list/?page={}"   # 點擊不同頁面在地址欄中觀察地址的變換即可找出規(guī)律
    start_time = time.time()
    for i in range(1, 6):  # 獲取 6頁圖標
        get_a_href(url.format(i))
    end_time = time.time()
    print("共耗時:", end_time-start_time)


if __name__ == '__main__':   # 判斷文件入口
    main()



六、接下來我們用單線程運行一下觀察下效果:


8.png

七、我們注釋掉 get_img_src(html) 函數(shù)中的 單線程 執(zhí)行語句,然后去掉 多線程 函數(shù)的 注釋符號,再對該函數(shù)編寫代碼:

# 多線程
def start_thread_save_img(img_url_list):
    #方式一:使用線程池
    pool = ThreadPool(6)
    pool.map(save_img, img_url_list)
    pool.close()
    # pool.join()  # join所完成的工作就是線程同步,即主線程任務結束之后,進入阻塞狀態(tài),一直等待其他的子線程執(zhí)行結束之后,主線程在終止

    #方式二:沒有線程限制
    # for url in img_url_list:
    #     th = threading.Thread(target=save_img, args=(url,))
    #     th.start()


八、經(jīng)過對比我們可以知道多線程和單線程的不一樣咯,還有多線程函數(shù)里的兩個方式最好試一下效率哦,下過圖如下:


9.png

好了,這個多線程就寫到這里,其實如果真正寫過去一遍之后感覺也挺容易的,接下來不要猶豫,直接動手吧!下一篇我們開搞多進程,希望能幫助到您哦!!!(如有疑問可留言...)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容