之前学习的内容进行网页内容下载时采用的是单线程单任务的方式,如果下载任务量较大,则需要花费很长时间,资源没有充分利用,时间消耗也比较大,因此引入多线程和线程池的概念和使用。
单线程的特点:对需要完成的任务依次逐个完成;多线程的特点:几个任务同时进行,其最重要的特点就是让程序能够同时执行多个任务。
一 多线程
在python中想实现多线程需要使用threading中的Thread库。
导入包->创建任务(函数)->创建线程(函数名,传参)->启动线程
def func():
print(123)
# 单线程模式
if __name__ == '__main__': # python中默认的线程,即主线程
func()
func()
func()
from threading import Thread
def func():
print(123)
if __name__ == '__main__': # 主线程
t = Thread(target=func) # 创建一个子线程 # 注意target设置函数名,不加函数后的括号(),只需要函数名,代表该线程如果启动,就执行该函数的任务
t.start() # 启动一个线程
print(123)
# 程序进行到主线程中,遇到了子线程(子线程执行的是func函数),start后子线程开始运行,而主线程继续执行主线程中的任务,该例中运算量较小,如果运算量大的话则效果明显
# 可以创建多个子线程
# 在子线程创建时可以定义参数值
def func(name):
for i in range(1000):
print(name,i)
if __name__ == '__main__':
t1 = Thread(target=func,args=("A",)) # 注意args传参,参数设置完成后需要多加一个逗号,target后跟的依然只有函数名
t1.start()
t2 = Thread(target=func,args=("B",))
t2.start()
二 线程池
多线程实现了,那么怎么选取线程数呢,有多少个任务就创建多少个线程,效率显然是最高的,但是这样也会对计算机的内存带来过高的载荷,所以在任务较多时,创建多少个线程合适呢,这里就要用到“线程池”。
线程池可以设置同时执行多少个线程,从而有效对数量庞大的线程任务进行管理和执行而不会由于同时执行的线程太多而使内存炸掉。
# 导包
from concurrent.futures import ThreadPoolExecutor
def func(name): # 创建任务
for i in range(100):
print(name, i)
if __name__=='__main__':
# 创建线程池
with ThreadPoolExecutor(20) as t: # 20表示创建的线程数,创建的个数与cpu质量有关
# 任务设置
for j in range(10000):
t.submit(func,f"任务{j}") # 把任务提交给线程池 线程池分配哪个线程执行该任务,最多同时执行20个线程
三 任务实战
结合之前学习的网页爬取的内容,可以使用线程池多线程同时进行快速下载所需的多个网页信息。
from concurrent.futures impot ThreadPoolExecutor
import requests
import re
import json
def download(imgsrc):
name = imgsrc.split("/"[-1]
print(f"准备开始下载图片{name}")
resp.img = requests.get(imgsrc)
with open(f"img/{name}", mode="wb") as f:
f.write(resp_img.content)
print(f"图片{name}下载完毕")
def main():
url = "xxxx"
resp = requests.get(url)
obj = re.compile(r"正则表达式,将需要的分组单独设置组名", re.S)
result = obj.search(resp.text)
deskPicStr = result.group("组名")
deskPic = json.loads(deskPicStr)
with ThreadPoolExecutor(10) as t:
for item in deskPic["list"]:
oriSize = item.get("oriSize")
imgsrc = item.get("imgsrc")
imgsrc = imgsrc.replace("##SIZE##", oriSize)
t.submit(download, imgsrc) # 函数名,参数
print("all over")
if __name__=='__main__':
main()
四 总结
没有学的知识点包括:反爬——很多网站都设置了反爬机制,对于python爬取信息是比较棘手的;
其他信息获取方式——如beautifulSoup等;协程——多线程下载图片有些时候效率还是不够高,对网络的依赖性较强,协程可以使单线程的利用率无限上升;js逆向——需要对网站进行加密和解密;http协议等;scrapy框架——避免重复性的代码写作。。。。。