【爬虫】使用多线程、多进程、多协程提升爬虫速度

本文详细介绍了如何利用Python实现多线程、多进程和多协程来提升爬虫速度。从并发并行、同步异步的基本概念出发,深入探讨了Python中多线程爬虫的GIL限制,多进程爬虫的并行优势,以及协程的高效并发特性。同时,给出了多线程和多进程的具体实现示例,指出在选择爬虫策略时应考虑IO密集型任务的特点。
摘要由CSDN通过智能技术生成

本系列为自己学习爬虫的相关笔记,如有误,欢迎大家指正

要学习提升爬虫速度用到的知识,必须先熟悉并发和并行、同步和异步的概

一、并发和并行,同步和异步

并发和并行

并发(concurrency)和并行(parallelism)是两个相似的概念。并发是指在一个时间段内发生若干事件的情况,并行是指在同一时刻发生若干事件的情况。

使用单核CPU和多核CPU来说就是:在使用单核CPU时,多个工作任务是以并发的方式运行的,因为只有一个CPU,所以各个任务会分别占用CPU的一段时间依次执行。如果在自己分得的时间段没有完成任务,就会切换到另一个任务,然后在下一次得到CPU使用权的时候再继续执行,直到完成。在这种情况下,因为各个任务的时间段很短、经常切换,所以给我们的感觉是“同时”进行。在使用多核CPU时,在各个核的任务能够同时运行,这是真正的同时运行,也就是并行。

同步和异步

同步就是并发或并行的各个任务不是独自运行的,任务之间有一定的交替顺序,可能在运行完一个任务得到结果后,另一个任务才会开始运行。就像接力赛跑一样,要拿到交接棒之后下一个选手才可以开始跑。

异步则是并发或并行的各个任务可以独立运行,一个任务的运行不受另一个任务影响,任务之间就像比赛的各个选手在不同的赛道比赛一样,跑步的速度不受其他赛道选手的影响。

二、多线程爬虫

多线程爬虫是以并发的方式执行的。也就是说,多个线程并不能真正的同时执行,而是通过进程的快速切换加快网络爬虫速度的。

在Python设计之初,为了数据安全所做的决定设置有GIL(Global Interpreter Lock,全局解释器锁)。在Python中,一个线程的执行过程包括获取GIL、执行代码直到挂起和释放GIL。在一个Python进程中,只有一个GIL,拿不到GIL的线程就不允许进入CPU执行。

正因为如此,在多核CPU上Python的多线程效率也不高。因为每次释放GIL锁,线程之间都会进行锁竞争,而切换线程会消耗资源。

虽然如此,但是因为网络爬虫是IO密集型,线程能够有效地提升效率,因为单线程下有IO操作会进行IO等待,所以会造成不必要的时间浪费,而开启多线程能在线程A等待时自动切换到线程B,可以不浪费CPU的资源,从而提升程序执行的效率。

Python的多线程对于IO密集型代码比较友好

使用单线程爬虫

import requests
import time
link_list = []

with open('most.txt') as file:
    file_list = file.readlines()
    for each in file_list:
        link = each.split()[1]
        link = link.replace('\n','')
        link_list.append(link)
    start = time.time()
    for eachone in link_list:
        try:
            r = requests.get(eachone)
            print(r.status_code,eachone)
        except Exception as e:
            print('Error:',e)
    end = time.time()
    print('串行时间:',end-start)

使用多线程

在python中使用多线程有两种方法

1.函数式

调用_thread模块中的start_new_thread()函数产生新线程

简单示例

import _thread
import time
# 为线程定义一个函数
def print_time(threadName,delay):
    count = 0
    while count < 3:
        time.sleep(delay)
        count += 1
        print(threadName,time.ctime())

_thread.start_new_thread(print_time,('Thread-1',1))
_thread.start_new_thread(print_time,('Thread-2',2))

#time.sleep(5)
print('Main Finished')

这个代码没出现自己想要的结果,暂时没看出来问题在哪。

  • _thread中使用start_new_thread ()函数来产生新线程

    def start_new_thread(function, args, kwargs=None): 
    
  • function表示线程函数

  • args为传递给线程函数的参数,它必须是tuple类型

2.类包装式

调用Threading库创建线程,从threading.Thread继承

threading模块提供了Thread类来处理线程,包括以下方法。

  • run():用以表示线程活动的方法。
  • start():启动线程活动。
  • join([time]):等待至线程中止。阻塞调用线程直至线程的join()方法被调用为止。
  • isAlive():返回线程是否是活动的。
  • getName():返回线程名。
  • setName():设置线程名。

示例:

import threading
import  time

class myThread(threading.Thread):
    def __init__(self,name,delay):
        threading.Thread.__init__(self)
        self.name = name
        self.delay = delay
    def run(self):
        print('Starting:'+self.name)
        print_time(self.name,self.delay)
        print('Exiting:'+self.name)
def print_time(threadName,delay):
    counter = 0;
    while counter<3:
        time.sleep(delay)
        print(threadName,time.ctime())
        counter += 1

threads = []
# 创建新线程
thread1 = myThread('Thread-1',
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值