iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Software Development

Python 爬蟲這樣學,一定是大拇指拉!系列 第 26

[Python 爬蟲這樣學,一定是大拇指拉!] DAY26 - 實戰演練:多執行緒 - 抓取多個個股日成交資訊

多執行緒(multithreading)

所以我們的多執行緒在程式是怎麼運作呢?

  • 一般情況:

    • 假設 req1、req2 都是送 request 到 server 的 function。
    • 送出收到資料皆須花費三秒
    import time
    
    def req1():
        print("req1: 開始送 request") 
        time.sleep(3)
        print("req1: 接收到 response") 
    
    def req2():
        print("req2: 開始送 request") 
        time.sleep(3)
        print("req2: 接收到 response")
    
    if __name__ == '__main__':
        req1()
        req2()
    

    輸出:

    https://ithelp.ithome.com.tw/upload/images/20211011/20139358R4d6JdinWW.png

    • 根據輸出結果我們可以知道:
      req2 執行前,必須得等 req1 整個流程(function 裡的那些動作)跑完。所以 CPU 這種一秒鐘幾千萬上下的核心也陪 req2 乾等了 3 秒鐘。但這樣子的使用是非常浪費 CPU 資源的。那該怎麼解決呢?
  • 我們可以運用多執行緒:

    import time
    # import python 的多執行緒套件
    from threading import Thread
    
    def req1():
        print("req1: 開始送 request") 
        time.sleep(3)
        print("req1: 接收到 response") 
    
    def req2():
        print("req2: 開始送 request") 
        time.sleep(3)
        print("req2: 接收到 response")
    
    if __name__ == '__main__':
        # 先將 funtion 包裝成 thread 的物件,再執行
        Thread(target=req1).start()
        Thread(target=req2).start()
    

    輸出:

    https://ithelp.ithome.com.tw/upload/images/20211011/201393589xCmv9MVBK.png

    • 根據輸出結果我們發現到:
      req1 還沒執行完就馬上接著做 req2 了!終於不用再等 req1 整個流程(function 裡的那些動作)跑完才做下一個動作了。簡單來說,程式(main)的運行就不會阻塞在那邊,會繼續往下做。根據下面的比較圖,花費時間真的變少了!

      • 一般情況花費時間:
        https://ithelp.ithome.com.tw/upload/images/20211011/20139358edrIMaLtxa.png

      • 使用多執行緒花費時間:
        https://ithelp.ithome.com.tw/upload/images/20211011/20139358f0yScMI8db.png

    • 而當要送出多個 request 時,會建議使用多執行緒來做,是因為 request 送出到收到 response 這中間多多少少都是要等的,而且根據現實的網路狀況,也不能保證每次都很快,可能0.3秒、5秒、10秒。所以與其等,我不如先送其他 request 來節省時間。

多執行緒搭配爬蟲

那我們拿之前教的個股日成交資訊結合多執行緒,來抓取多個個股日成交資訊吧!

  • 示範:

    import time
    import requests
    from threading import Thread
    
    # 將流程先用 function 封裝起來,接下來才能帶入 thread 中
    def daily_price_req(date, stock_no):
        res = requests.get("https://www.twse.com.tw/exchangeReport/STOCK_DAY",
                           params={
                               "response": "json",
                               "date": date,
                               "stockNo": stock_no
                           })
    
        # 把 JSON 轉成 Python 可存取之型態
        res_json = res.json()
    
        # 我們要的每日成交資訊在 data 這個欄位
        daily_price_list = res_json['data']
    
        # 印出資料
        print("{} 每日成交資訊: {}".format(stock_no, daily_price_list))
    
    
    if __name__ == '__main__':
        # 日期什麼的,可以依照需求設定
        req_info_list = [
            {
                "date": "20211011",
                "stockNo": "2330"
            },
            {
                "date": "20211011",
                "stockNo": "2603"
            },
            {
                "date": "20211011",
                "stockNo": "2609"
            }
        ]
    
        for req_info in req_info_list:
            req_date = req_info.get("date")
            req_stock_no = req_info.get("stockNo")
            if req_date and req_stock_no:
                # args 為 function 會用到的參數
                req_thread = Thread(target=daily_price_req,
                                    args=(req_date, req_stock_no))
                req_thread.start()
    
                # 每個 req 間隔最好 3 秒以上,不然會被證交所鎖 IP 一段時間
                time.sleep(3)
    

    輸出(圖片橫幅太寬,我截圖只截一半請見諒):
    https://ithelp.ithome.com.tw/upload/images/20211011/20139358hbl2iZmRR7.png

  • 多執行緒的另外一個用法:

    req_thread_list = []
    for req_info in req_info_list:
            req_date = req_info.get("date")
            req_stock_no = req_info.get("stockNo")
            if req_date and req_stock_no:
                req_thread = Thread(target=daily_price_req,
                                    args=(req_date, req_stock_no))
                req_thread.start()
                req_thread_list.append(req_thread)
    
                # 每個 req 間隔最好 3 秒以上,不然會被證交所鎖 IP 一段時間
                time.sleep(3)
    
    for req_thread in req_thread_list:
        # join() 就是讓程式等著,確定該 thread 已經執行完畢之後再繼續往下
        # 所以這邊的用法是說:
        # 我的程式會在這邊等所有 req thread(daily_price_req 裡的那些動作)都執行完畢後再往下做
        req_thread.join()
    
    # do something...
    print("do something...")
    

    輸出(圖片橫幅太寬,我截圖只截一半請見諒):
    https://ithelp.ithome.com.tw/upload/images/20211011/201393580TNK5WDf7c.png

以上就是多執行緒搭配爬蟲的應用!可以依照不同需求再自行修改~


上一篇
[Python 爬蟲這樣學,一定是大拇指拉!] DAY25 - 實戰演練:關於多執行緒
下一篇
[Python 爬蟲這樣學,一定是大拇指拉!] DAY27 - 實戰演練:重複使用 TCP 連線
系列文
Python 爬蟲這樣學,一定是大拇指拉!30

尚未有邦友留言

立即登入留言