什么是多线程

这里就不说什么高深莫测的专业术语了,一句话,在泡澡的同时喝咖啡。也就是说在代码中同时让多个区域的代码或者说函数同时运行以此达到提高效率的目的。

举个例子

比如我们有这样一场比赛,小明和小红在做跑步测试,现在他们身上都要一个计时器用于计时。这是他们一个一个测试。就像下面的代码一样。

import threading
import time
time0=time.time()
def print_ming():
time.sleep(5)
print('我是小明我跑完了')
def print_hong():
time.sleep(3)
print('我是小红我跑完了')
print_ming()
print_hong()
time1=time.time()
print(time1-time0)

我们可以看到运行结果


显然我们花了8秒左右的时间做完了测试。然而他们都有一个计时器既然如此把他们同时放在一起测试不就可以大大提高效率吗。

同时测试

我们打代码做如下更改

导入threading

1创建线程ming=threading.Thread(target=print_ming)

2生明线程ming.setDaemon(True)

3运行线程ming.star()

更改后的代码如下:

import threading
import time
time0=time.time()
def print_ming():
time.sleep(5)
print('我是小明')
def print_hong():
time.sleep(3)
print('我是小红')
ming=threading.Thread(target=print_ming)
hong=threading.Thread(target=print_hong)
ming.start()
hong.start()
print('执行完了')
time1=time.time()
print(time1-time0)

结果如下:


震惊怎么可能这么快,显然我们可以发现,在这个程序中或者测试中,小明和小红根本就没有跑。怎么会这样呢,显然是我们的主程序运行的速度要比两个函数(也可以是是子函数)快,所以主程序运行完了就会结束运行。

谁跑得快?

这里我们显然需要主程序等一等我们地两个函数,我们可以假设在做跑步测试中有一个跑得飞快的教练,3个人当中只要有一个到达了终点,测试就结束。所以我们先看一看让跑的快地人等一下慢的。

所以我门在原来的基础上加入这样地代码;

1创建线程ming=threading.Thread(target=print_ming)

2生明线程ming.setDaemon(True)

3运行线程ming.star()

4等待线程ming.join()

import threading
import time
time0=time.time()
def print_ming():
time.sleep(5)
print('我是小明')
def print_hong():
time.sleep(3)
print('我是小红')
ming=threading.Thread(target=print_ming)
hong=threading.Thread(target=print_hong)
ming.start()
hong.start()
ming.join()
print('执行完了')
time1=time.time()
print(time1-time0)

结果如下


显然我们知道小明怕的慢所以我们让大家等等小明,但如果我们把ming.join()

改成hong.join()呢


显然小明没有执行了,所以我们必须以慢的为标准,但显然我们有时候并不知道谁更慢。所以直接把两个一起加进去。

并且为了方便管理我们好可以将线程加进循环中,最终代码如下;

import threading
import time
time0=time.time()
def print_ming():
time.sleep(5)
print('我是小明')
def print_hong():
time.sleep(3)
print('我是小红')
thread=[]
ming=threading.Thread(target=print_ming)
thread.append(ming)
hong=threading.Thread(target=print_hong)
thread.append(hong)
for t in thread:
t.setDaemon(True)
t.start()
ming.join()
hong.join()
print('执行完了')
time1=time.time()
print(time1-time0)


这里注意我没有将join()方法加入循环否则你会看见这样的结果;


显然这是没有任何作用的,为什么会这样呢,你可以这样理解,循环是在主程序中的,循环中有两个子程序,每一次即便两个程序一起执行也要执行两次,也就是说第一次小红跑完了线程结束3秒,但是此时主程序未执行完进入下一个循环这时是小明执行花费5秒共计8秒。

所以我们必须在循环外使用join()

最终结果

import threading
import time
time0=time.time()
def print_ming():
time.sleep(3)
print('我是小明')
def print_hong():
time.sleep(5)
print('我是小红')
thread=[]
ming=threading.Thread(target=print_ming)
thread.append(ming)
hong=threading.Thread(target=print_hong)
thread.append(hong)
for t in thread:
t.setDaemon(True)
t.start()
ming.join()
hong.join()
print('执行完了')
time1=time.time()
print(time1-time0)
#ming=threading.Thread(target=print_ming,args=(填入函数值,没有值需要被填则不设置args参数))


tips

这个多线程已经说的比较清楚了,这篇博客是前两篇有关于多线程的一个实例衍生。

这个代码还是很简单的。

爬虫不太懂得我先前没怎么写完爬虫的博客,我没辙。

但是多线程这块我讲的很明白了,看不懂我也没辙,要不然就私信有时间就回。

几分钟搞定python多线程

几分钟python多线程深入解读

那么接下来代码如下;

import requests
from lxml import etree
import threading
import os
from urllib import request
from queue import Queue
url_frist_save=Queue(1000)
#url_save=Queue(200)
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'}
url_root='http://pic.netbian.com'
def get_url():
date_frist=requests.get(url_root,headers=headers)
date_frist=date_frist.content.decode('gbk')
date_frist_=etree.HTML(date_frist)
uel_1=date_frist_.xpath('//li//a[@href and @title and @target]/@href')[4:]
for i in uel_1:
url=url_root+i
url_frist_save.put(url)
for i in range(2,101):
urlx='http://pic.netbian.com/index_{name}.html'.format(name=str(i))
date_frist = requests.get(urlx, headers=headers)
date_frist = date_frist.content.decode('gbk')
date_frist_ = etree.HTML(date_frist)
uel_1 = date_frist_.xpath('//li//a[@href and @target]/@href')[1:]
for i in uel_1:
url = url_root + i
url_frist_save.put(url)
def down_load():
if not os.path.exists(r'C:\Users\a3139\Desktop\projects\爬虫dome\pictures'):
os.makedirs(r'C:\Users\a3139\Desktop\projects\爬虫dome\pictures')
while True:
date_frist=requests.get(url_frist_save.get(),headers=headers)
date_frist=date_frist.content.decode('gbk')
date_frist_=etree.HTML(date_frist)
url_down=url_root+str(date_frist_.xpath('//div[@class="photo-pic"]/a/img/@src')[0])
name=str(date_frist_.xpath('//div[@class="photo-pic"]/a/img/@title')[0])
path=r'C:\Users\a3139\Desktop\projects\爬虫dome\pictures'+'\\'+name+'.jpg'
print(path)
print(url_down)
try:
request.urlretrieve(url_down,path)
request.urlcleanup()
except:
print('此图片下载失败')
if url_frist_save.empty():
print(url_frist_save.empty())
break
for i in range(5):
t1=threading.Thread(target=get_url)
t2=threading.Thread(target=down_load)
t1.start()
t2.start()


这里要注意的是我在这里为这个爬虫开了五个线程,但是这个并不意味着使用Queue可将100个网页爬取分成每个线程爬取20个,同样他们还是爬取100个每个线程。区别是以前是甲先做完再乙去做,现在是甲乙同时做为一组即一个线程。除非你给他们分配任务。我之所以这写主要是为了保证我可以爬取的图片不会遗漏,这个线程没有爬到这张图片,但是不代表另一个爬取不到,