本文首发于知乎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编程
专栏目录:目录
版本说明:软件及包版本说明