本文首發於知乎html
爬蟲主要運行時間消耗是請求網頁時的io阻塞,因此開啓多線程,讓不一樣請求的等待同時進行,能夠大大提升爬蟲運行效率。python
本文基於多線程(這裏開啓了10個線程),使用github的api,抓取fork cpython項目的全部5千多個項目信息,將數據存儲到json文件中。git
抓取github的這個內容,在上一篇文章中展現了不使用多線程的版本,這裏就直接在那個的基礎上進行改進。github
爬蟲所需技術編程
爬蟲代碼以下json
import requests
import time
from threading import Thread
from queue import Queue
import json
def run_time(func):
def wrapper(*args, **kw):
start = time.time()
func(*args, **kw)
end = time.time()
print('running', end-start, 's')
return wrapper
class 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郵箱和密碼,便可運行爬蟲。api
爬蟲說明以下bash
1.run_time
函數是一個計算程序運行時間的裝飾器,做用於Spider
對象的run
函數多線程
2.Spider
類app
__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_num
和self.thread_num
。
我這裏作了一個實驗,self.page_num
值設爲20,即總共抓取20頁
一個問題
最後留一個問題給讀者思考:在前面的這篇文章中,咱們也實現了一個多線程爬蟲,爲何當時的代碼那麼簡單,而如今卻複雜了這麼多呢?
後續
多線程爬蟲的下一篇文章會實如今翻頁、抓取二級頁面時使用多線程。
專欄主頁:python編程
專欄目錄:目錄
版本說明:軟件及包版本說明