多線程爬蟲實現(上)

多線程爬蟲實現(上)

來自專欄 python編程

爬蟲主要運行時間消耗是請求網頁時的io阻塞,所以開啟多線程,讓不同請求的等待同時進行,可以大大提高爬蟲運行效率。

本文基於多線程(這裡開啟了10個線程),使用github的api,抓取fork cpython項目的所有5千多個項目信息,將數據存儲到json文件中。

抓取github的這個內容,在上一篇文章中展示了不使用多線程的版本,這裡就直接在那個的基礎上進行改進。

爬蟲所需技術及準備事項

  • requests庫請求網頁,獲取json格式數據,解析字典提取我們要的信息,存儲json文件
  • 使用threading為網頁請求部分設計多線程
  • 使用兩個隊列,分別存儲待抓取url和解析後的結果數據
  • 擁有github賬戶,需要在代碼中填入賬號和密碼
  • 了解裝飾器(這裡只是計算運行時間,不了解也沒關係)

爬蟲代碼如下

import requestsimport timefrom threading import Threadfrom queue import Queueimport jsondef run_time(func): def wrapper(*args, **kw): start = time.time() func(*args, **kw) end = time.time() print(running, end-start, s) return wrapperclass Spider(): def __init__(self): self.qurl = Queue() self.data = list() self.email = # 登錄github用的郵箱 self.password = # 登錄github用的密碼 self.page_num = 171 self.thread_num = 10 def produce_url(self): baseurl = https://api.github.com/repos/python/cpython/forks?page={} for i in range(1, self.page_num + 1): url = baseurl.format(i) self.qurl.put(url) # 生成URL存入隊列,等待其他線程提取 def get_info(self): while not self.qurl.empty(): # 保證url遍歷結束後能退出線程 url = self.qurl.get() # 從隊列中獲取URL print(crawling, url) req = requests.get(url, auth = (self.email, self.password)) data = req.json() for datai in data: result = { project_name: datai[full_name], project_url: datai[html_url], project_api_url: datai[url], star_count: datai[stargazers_count] } self.data.append(result) @run_time def run(self): self.produce_url() ths = [] for _ in range(self.thread_num): th = Thread(target=self.get_info) th.start() ths.append(th) for th in ths: th.join() s = json.dumps(self.data, ensure_ascii=False, indent=4) with open(github_thread.json, w, encoding=utf-8) as f: f.write(s) print(Data crawling is finished.)if __name__ == __main__: Spider().run()

讀者只需要在Spider__init__中,指定自己的github郵箱和密碼,即可運行爬蟲。

爬蟲說明如下

1.run_time函數是一個計算程序運行時間的裝飾器,作用於Spider對象的run函數

2.Spider

  • __init__初始化一些常量
  • produce_url用於生產所有URL,存儲到Queue隊列qurl中。5千多個元素分布在171個頁面之中,將這171個URL存入隊列中等待請求解析。其實這裡不需要多線程之間通信,所以使用list代替Queue隊列也是可以的。
  • get_info網頁的請求與解析,之後開啟多線程就是多個這個函數同時運行。函數邏輯:只要qurl中還有元素,就每次從qurl中提取一個url進行請求解析,將結果存入data列表中。當隊列中沒有元素了即退出循環(爬蟲結束)。
  • run調用函數,運行爬蟲。首先調用produce_url產生待爬URL隊列。然後開啟指定數量的線程,每個線程都從qurl不斷提取URL進行解析,將數據存入data列表中。等到URL隊列被解析結束,將data中的數據存儲入json文件中

爬蟲結果

抓取結果展示如下

這個程序開啟10個線程抓取171個頁面用了33秒。在這篇文章中不使用多線程則使用了333秒。為了能更清晰地體會多線程運行效率的改進,讀者可以自行嘗試修改上面代碼中的self.page_numself.thread_num

我這裡做了一個實驗,self.page_num值設為20,即總共抓取20頁

  • 開2個線程運行 18.51 秒
  • 開5個線程運行 7.49 秒
  • 開10個線程運行 3.97 秒
  • 開20個線程運行 2.11 秒

一個問題

最後留一個問題給讀者思考:在前面的這篇文章中,我們也實現了一個多線程爬蟲,為什麼當時的代碼那麼簡單,而現在卻複雜了這麼多呢?

後續

多線程爬蟲的下一篇文章會實現在翻頁、抓取二級頁面時使用多線程。

專欄信息

專欄主頁:python編程

專欄目錄:目錄

爬蟲目錄:爬蟲系列目錄

版本說明:軟體及包版本說明


推薦閱讀:

TAG:Python | 多線程 | 爬蟲計算機網路 |