python项目实战——多线程爬虫

多线程爬虫


希望读到这篇文章的小伙伴可以跟我交流一下哪里有疑惑,大家需要互相成长,毕竟金无足赤,人无完人

概念

一般情况下,一个程序有一个进程和一个线程,代码依次线性执行

多线程可以并发执行,一次性多个人做多件事

并行

多件事同时刻发生,利用多个CPU,就像短跑一样,一组选手同时起步

请添加图片描述

并发

多件事同一时间段发生,利用一个CPU,就像接力赛一样,在一圈内有好几个人跑步,这里把一圈看成一个时间段

请添加图片描述

虽然总的运行时间没有减少,那也要分情况

上面的情况就是:CPU密集型

这里是因为都是CPU自己在工作,所以怎么切割都没用

对于下面的情况就有作用了

IO密集型

请添加图片描述

所谓的IO就是用户的输入和输出,不需要CPU的工作,比如,使用requests模块时,我们输入一个url,在得到服务器的返回值时,程序什么都不用执行

上面的线程1执行完之后,进行IO操作,CPU就被释放,不再占用CPU资源了

在执行A2操作时,B1也可以同时执行,然后把B1挂起,等待A2执行结束,执行B2,在执行C1,挂起C1,等待C2执行。。。可以缩短时间

Python多线程用途

Python自带的解释器是Cpython,并不支持真正意义上的多线程。Cpython提供了多线程包,包含一个叫Global Interpreter Lock(GIL)锁,它能确保你的代码中永远只有一个线程在执行。经过G的处理,会增加执行的开销。这就意味着如果你先要提高代码执行效率,使用treading不是一个明智的选择当然如果你的代码是IO密集型,比如爬虫,多线程可以明显提高效率,相反如果你的代码是CPU密集型,比如大量计算类型,这种情况下多线程反而没有优势,建议使用多进程。

threading模块

threading模块是python中专门提供用来做多线程编程的模块。threading模块中最常用的类是Thread。

  1. 用treading模块直接写一个多线程程序
  2. threading模块下的Thread类,继承自这个类,然后实现run方法,线程就会自动运行run方法中的代码

小知识----函数体内pass的用处

在 Python 中,pass 是一个空语句,用于表示"无操作"。它可以在需要某种语法结构但又不想执行任何操作的地方使用。常见的用法包括以下几种情况:

1. 占位符

有时在定义函数或类时,可能还未实现具体的逻辑。可以使用 pass 作为占位符,确保代码能够正常运行。

def my_function():
    pass  # 暂未实现

class MyClass:
    pass  # 暂未实现的类

2. 控制结构

在条件语句、循环、异常处理等结构中,如果你暂时不想处理某些分支,也可以使用 pass

for i in range(5):
    if i == 2:
        pass  # 你可以选择在这里执行某些操作,或者什么都不做
    else:
        print(i)

try:
    # 可能会抛出异常的代码
    x = 1 / 0
except ZeroDivisionError:
    pass  # 处理异常,但不做任何事情

3. 定义接口

在设计接口时,可能会有一些方法不需要立即实现。可以使用 pass 来定义这些方法。

class BaseClass:
    def method(self):
        pass

总结

pass 语句在编写 Python 代码时是非常有用的,它使得你的代码结构可以更容易地构建和调整,特别是在开发过程中。使用 pass 可以帮助你先搭建代码框架,然后再逐步实现具体功能。

代码解读

单线程–串行

import threading
import time


def singing(name,delay): # name填歌手,delay填延迟时间
    print(f'{
     name}开始唱歌')
    time.sleep(delay)
    print('结束唱歌')

def dancing(name,delay): # name填舞者,delay填延迟时间
    print(f'{
     name}开始跳舞')
    time.sleep(delay)
    print('结束跳舞')


def single_thread():  # 创建一个单线程
    singing('张学友',2)
    dancing('小狗',3)


if __name__ == '__main__':   # 写main关键字,作为函数入口
    # 计时
    strat_time = time.time()
    single_thread() # 调用单线程函数
    end_time = time.time()
    print(f'共消耗时间:{
     end_time-strat_time}')

多线程–并行

import threading
import time


def singing(name,delay): # name填歌手,delay填延迟时间
    print(f'{
     name}开始唱歌')
    time.sleep(delay)
    print('结束唱歌')

def dancing(name,delay): # name填舞者,delay填延迟时间
    print(f'{
     name}开始跳舞')
    time.sleep(delay)
    print('结束跳舞')


def multi_thread():  # 多线程
    # 线程1
    th1 = threading.Thread(target=singing,args=('张学友',2)) # 调用threading模块的Thread方法
    # 在第一个参数target后面写函数名,不要加括号!不要加括号!不要加括号!写了括号就不是指定函数了,就变成调用函数了
    # 第二个参数args后面以元组的形式,填入函数的参数,
    # args即使只有一个参数,也必须用元组的形式填入,必须有一个逗号(参数1,) 也可以是(参数1,参数2)
    # args要接收一个元组,只传一个参数,不加逗号,就相当于把参数传给args,不是元组就会报错
    th1.start()  # 上面这一步只是建立一个对象,这行语句才是刚刚执行

    # 线程2
    th2 = threading.Thread(target=dancing,args=('小狗',3))
    th2.start()


if __name__ == '__main__':   # 写main关键字,作为函数入口
    # 计时
    strat_time = time.time()
    multi_thread() # 调用多线程
    end_time = time.time()
    print(f'共消耗时间:{
     end_time-strat_time}')

运行结果是:

张学友开始唱歌
小狗开始跳舞
共消耗时间:0.002039194107055664
结束唱歌
结束跳舞

很明显,不是我们想要的结果,因为在这里有三个线程同时执行

主线程main,子线程th1,子线程th2

主线程只是负责调用子线程,运行的很快,而子线程还在延迟中,我们现在查看的是主线程的运行时间

查看当前程序的线程

print(threading.enumerate())

使用这个程序可以看到程序运行到这个语句时的所有线程

if __name__ == '__main__':   # 写main关键字,作为函数入口
    # 计时
    strat_time = time.time()
    multi_thread() # 调用多线程
    print(threading.enumerate())
    end_time = time.time()
    print(f'共消耗时间:{
     end_time-strat_time}')

加上这条语句之后,就可以看到程序的此刻的所有线程了

张学友开始唱歌
小狗开始跳舞
[<_MainThread(MainThread, started 22976)>, 
 <Thread(Thread-1 (singing), started 2784)>,
 <Thread(Thread-2 (dancing), started 23204)>]
共消耗时间:0.0009999275207519531
结束唱歌
结束跳舞

不难发现,分别是主函数main,子线程singing,子线程dancing

让主函数等待子线程结束,再运行—.join()

def multi_thread():  # 多线程
    th1 = threading.Thread(target=singing,args=('张学友',2)) 
    th1.start()

    th2 = threading.Thread(target=dancing,args=('小狗',3))
    th2.start()
    
    th1.join()
    th2.join()


if __name__ == '__main__':   # 写main关键字,作为函数入口
    # 计时
    strat_time = time.time()
    multi_thread() # 调用多线程,有join控制时,会一直等待join的子线程
    print(threading.enumerate())
    end_time = time.time()
    print(f'共消耗时间:{
     end_time-strat_time}')
    
    	
张学友开始唱歌
小狗开始跳舞
结束唱歌
结束跳舞
[<_MainThread(MainThread, started 22236)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兆。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值