多线程与多任务异步协程高效爬虫

简介: 多线程与多任务异步协程高效爬虫

爬虫

前言

线程与进程的讲解:
进程是一个资源单位,每一个进程至少有一个线程,而线程是一个执行单位。

而一个程序若是只有一个线程,那么该程序将会串行运行,如果要爬取大量的数据那么执行效率将会显得比较低下。


一、多线程爬虫:

对于大量数据要多次发送请求可以利用python中内置库threaing开启多个线程,但是这样子的话就只能手动一个一个的开启线程,所以还有一种方法使用线程池来进行多线程操作
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(50) as T:

这样就开启了50个线程的线程池

比如下面一个场景:
要爬取一个网站多页的数据,而该网站每一页url的请求体中有第几页的参数
所以想要爬取50页就遍历(1,52)即可。如果此时并没开启线程池去请求50次,就很慢,但是如果开启了50个线程那么就可以同时去进行,速度自然快得多得多了

样例代码:

import requests
from concurrent.futures import ThreadPoolExecutor




def DownLoad_OnePage(Url,i):

    headers={
   
        'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
    }

    data={
   
        "current":i
    }
    resp=requests.post(Url,data=data)
    for price in (resp.json()['list']):
        data_row=price['prodName']+':'+price['avgPrice']
        print(f'第{i}页数据{data_row}爬取完毕')
        f.write(data_row)
        f.write('\n')



if __name__=='__main__':
    f = open('data.txt', 'w', encoding='utf-8')
    with ThreadPoolExecutor(50) as T:
        for i in range(1,50):
            T.submit(DownLoad_OnePage,Url="http://www.xinfadi.com.cn/getPriceData.html",i=i)
    print('爬取完毕')
    f.close()

二、多任务异步协程爬虫

概述:

当程序中遇到了如input,sleep等会阻塞程序运行的操作时,使用协程就可以跳过该阻塞运行其他的代码,再当阻塞停止时返回。

而requests.get(**)在网络请求返回数据之前,程序也是处于阻塞状态的一般情况下,当程序处于IO操作的时候。线程都会处于阻塞状态

协程:
当程序遇见了IO操作的时候,可以选择性的切换到其他任务上.

在微观上是一个任务一个任务的进行切换.切换条件一般就是IO操作
在宏观上,我们能看到的其实是多个任务一起在执行

例如:

import asyncio
import time
async def func1():
    print("协程1")
    # time.sleep(4) 当程序出现了同步的操作,异步就断掉了。
    await asyncio.sleep(4)  # 阻塞时进行await
    print("协程1")


async def func2():
    print("协程2")
    # time.sleep(3)
    await asyncio.sleep(3)
    print("协程2")
async def func3():
    print("协程3")
    await asyncio.sleep(2)
    print("协程3")

async def main():
    tasks=[
        func1(),
        func2(),
        func3()
    ]    # 添加多任务异步协程
    await asyncio.wait(tasks)
if __name__=='__main__':
    # 主线程最好不出现协程,所以应在写一个main函数在主线程运行
    t1=time.time()
    asyncio.run(main())   # 协程运行
    t2=time.time()
    print(t2-t1)

这一段代码如果没有利用协程,三个time.sleep()至少也会运行9s了
但是这段代码只会运行4s多一点点。

而实际上多任务异步协程进行爬取时模板与上面样例实际是差不多的

实战样例:

import asyncio
import aiohttp   
import requests
import aiofile   
import json
async def aioDownLoad(cid,b_id,title):
    data = {
   
        "book_id" :b_id,
        "cid":f"{b_id}|{cid}",
        "need_bookinfo":1
    }
    data = json.dumps(data)
    url = "https://dushu.baidu.com/api/pc/getChapterContent?data={%22book_id%22:%22"+str(b_id)+"%22,%22cid%22:%22"+str(b_id)+"|"+str(cid)+"%22,%22need_bookinfo%22:1}"
    async  with aiohttp.ClientSession() as Session:
        async with Session.get(url) as resp:
            dic = await resp.json()
            async with aiofile.AIOFile(f'novel/{title}.txt','w','utf-8') as f:
                await f.write(dic['data']['novel']['content'])
async def getCatlog(url):
    resp = requests.get(url)
    dic=resp.json()
    tasks = []
    for item in dic['data']['novel']['items']:
        title = item['title']
        cid = item['cid']
        tasks.append(aioDownLoad(cid,b_id,title))
    await asyncio.wait(tasks)
if __name__ == '__main__':
    b_id='4306063500'
    url='https://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"'+b_id+'"}'
    asyncio.run(getCatlog(url))

aiohttp是python的⼀个⾮常优秀的第三⽅异步http请求库
举一反三aiofile就是第三方异步文件操作库

这两个的操作与requests和文件操作都是极其类似的,看上面的样例就能很容易理解。

总结

提示:这里对文章进行总结:

除了爬取多页这种要发送大量请求的的案例可以用多任务异步协程节省大量时间外,还有就是下载到本地也会处于阻塞状态,例如在爬取下载视频的时候,要知道现在视频都是分割成了海量几秒的视频放在一个m3u8文件中,所以在下载的时候也可以多任务异步协程同时下载。

相关文章
|
2月前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
58 1
|
1月前
|
存储 Java 数据库
如何处理线程池关闭时未完成的任务?
总之,处理线程池关闭时未完成的任务需要综合考虑多种因素,并根据实际情况选择合适的处理方式。通过合理的处理,可以最大程度地减少任务丢失和数据不一致等问题,确保系统的稳定运行和业务的顺利开展。
119 64
|
1月前
|
消息中间件 监控 Java
线程池关闭时未完成的任务如何保证数据的一致性?
保证线程池关闭时未完成任务的数据一致性需要综合运用多种方法和机制。通过备份与恢复、事务管理、任务状态记录与恢复、数据同步与协调、错误处理与补偿、监控与预警等手段的结合,以及结合具体业务场景进行分析和制定策略,能够最大程度地确保数据的一致性,保障系统的稳定运行和业务的顺利开展。同时,不断地优化和改进这些方法和机制,也是提高系统性能和可靠性的重要途径。
119 62
|
24天前
|
数据采集 机器学习/深度学习 前端开发
PHP爬虫性能优化:从多线程到连接池的实现
本文介绍了一种通过多线程技术和连接池优化PHP爬虫性能的方法,以新浪投诉平台为例,详细展示了如何提高数据采集效率和稳定性,解决了传统单线程爬虫效率低下的问题。
PHP爬虫性能优化:从多线程到连接池的实现
|
26天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
53 12
|
2月前
|
存储 消息中间件 人工智能
进程,线程,协程 - 你了解多少?
本故事采用简洁明了的对话方式,尽洪荒之力让你在轻松无负担的氛围中,稍微深入地理解进程、线程和协程的相关原理知识
46 2
进程,线程,协程 - 你了解多少?
|
2月前
|
前端开发 Java API
vertx学习总结5之回调函数及其限制,如网关/边缘服务示例所示未来和承诺——链接异步操作的简单模型响应式扩展——一个更强大的模型,特别适合组合异步事件流Kotlin协程
本文是Vert.x学习系列的第五部分,讨论了回调函数的限制、Future和Promise在异步操作中的应用、响应式扩展以及Kotlin协程,并通过示例代码展示了如何在Vert.x中使用这些异步编程模式。
61 5
vertx学习总结5之回调函数及其限制,如网关/边缘服务示例所示未来和承诺——链接异步操作的简单模型响应式扩展——一个更强大的模型,特别适合组合异步事件流Kotlin协程
|
2月前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
69 2
|
2月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
87 5
|
1月前
|
NoSQL 关系型数据库 MySQL
python协程+异步总结!
本文介绍了Python中的协程、asyncio模块以及异步编程的相关知识。首先解释了协程的概念和实现方法,包括greenlet、yield关键字、asyncio装饰器和async/await关键字。接着详细讲解了协程的意义和应用场景,如提高IO密集型任务的性能。文章还介绍了事件循环、Task对象、Future对象等核心概念,并提供了多个实战案例,包括异步Redis、MySQL操作、FastAPI框架和异步爬虫。最后提到了uvloop作为asyncio的高性能替代方案。通过这些内容,读者可以全面了解和掌握Python中的异步编程技术。
50 0