前言
在上个项目中,初步采集了知道的问题、问题url以及问题id,可是在采集的过程中我发现,爬取速度太慢,几乎每几秒才能得到一条数据,为了提升效率,我在想,能不能通过某个方式提高数据抓取的效率,最后考虑使用python中的多线程来实现。
多线程的介绍
什么是线程,进程?多进程,多线程又是什么?
如果将1台电脑比作1个工厂,里面的员工比作一个进程,那么
当这个厂只有1名员工在只做1件事的时候,那就是单进程单线程。
当这个厂有2名或者2名以上的员工在同时只做1件事的时候,那就是多进程
当这个厂只有1名员工在同时做不同事情的时候,那这个时候就是多线程。
注意:线程是基于进程而存在的,运行效率上单线程速度最慢,多线程和多进程的速度相似,但是多进程所占用的资源最多,一般使用多线程或者协程
多进程的使用
直接导入threading模块
import threading
threading模块中最核心的内容是Thread这个类
通常来说,我们想要创建多少个线程,就是创建多少个Thread对象,Thread对象的数量就是线程的数量,同时我们还能指定每一个线程中所做不同的事情,这就是多线程编程,同时多线程之间使用队列Queue来进行通信。
创建Thread对象有两种方法:
- 直接创建Thread,将运行函数传入,使用start()方法开启线程处理任务。
- 编写一个自定义类继承Thread,复写Thread中的run()方法,将需要处理的任务写入,最后创建Thread的子类,通过start()开启线程
总的来说,两种方法都能创建Thread对象,不过第二种方法符合面向对象思想,一般使用第二种。
Thread两种创建对象的方法与demo
1、直接创建Thread,将函数传入其中,调用start()方法开启线程
import threading
import time
def test1():
print("test1 start_time:\t",time.time())
time.sleep(5)
print("test1 over_time:\t",time.time())
def test2():
print("test2 start_time:\t",time.time())
time.sleep(2)
print("="*40)
print("test2 over_time:\t",time.time())
if __name__ == "__main__":
# 创建对象,这里传入的是函数,不需要执行,因此不用在函数后加括号
test_1 = threading.Thread(target=test1)
test_2 = threading.Thread(target=test2)
# 开启线程
test_1.start()
test_2.start()
# 等待线程
test_1.join()
test_2.join()
运行结果:
从运行结果可以看出,程序几乎是同时执行的两个函数,由于两个函数sleep的时间不同,所以结束的时间也不相同。
2、编写一个自定义类继承Thread,复写Thread中的run()方法,将需要处理的任务写入,最后创建Thread的子类,通过start()开启线程
我们先来看例子,下面是自定义类继承Thread实现多线程的demo:
import threading
import time
class test(threading.Thread):
def __init__(self, name):
super(test, self).__init__()
self.name = name
def run(self):
print("{0}\tstart_time:\t{1}".format(self.name, time.time()))
time.sleep(3)
print("\n{0}\tover_time:\t{1}".format(self.name, time.time()))
def main():
test_list_name = []
test_list = []
# 批量生成线程名字,存入列表
for i in range(1, 4):
name = str(i) + "号test"
test_list_name.append(name)
# 根据名字列表的数量创建线程,并开启
for threadName in test_list_name:
thread = test(threadName)
thread.start()
test_list.append(thread)
# 等待所有线程结束
for i in test_list:
i.join()
if __name__ == "__main__":
main()
下面是运行结果图:
自此,我们可以使用多线程来同时处理不同的任务。
Queue(队列)
有时候在某些项目中要使用多线程实现生产者-消费者模式,所以就要解决线程之间的通信问题,这就要用到queue(队列)来解决线程之间的通信问题。
- Queue.qsize() ---- 返回当前队列包含消息数量
- Queue.empty() ---- 如果队列为空,返回True,反之则为False
- Queue.full() ---- 当队列满时返回True;当队列为空时返回False
- Queue.get() ---- 从队列中提取一条信息,若无信息则弹出异常(异常类型:Queue.Empty)
- Queue.put() ---- 将一条数据存入队列中
注:
1、Queue 是 先进先出 类型
2、多线程共享全局变量
3、但是多线程同时操作一个全局变量的时候会有bug,可以选择轮询或者使用互斥锁解决。
附:
在做完第一个项目不久,考虑是不是可以用多线程的方式去实现,随着我对多线程进一步的理解,就开始着手对我第一个项目进行重构。?
- 加入了IP池(感谢GitHub大神的IPProxy项目)
- 加入多线程进行爬取、解析
- 将获取的数据存入mysql
- 使用互斥锁、添加延时等方式降低程序死锁风险
具体代码可以到我的GitHub查看------>点这里