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

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

爬虫

前言

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

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


一、多线程爬虫:

对于大量数据要多次发送请求可以利用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文件中,所以在下载的时候也可以多任务异步协程同时下载。

相关文章
|
20天前
|
数据采集 存储 网络协议
Java HttpClient 多线程爬虫优化方案
Java HttpClient 多线程爬虫优化方案
|
2月前
|
数据采集 Java 调度
使用Kotlin实现动态代理池的多线程爬虫
使用Kotlin实现动态代理池的多线程爬虫
|
2月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
118 6
|
3月前
|
消息中间件 调度
如何区分进程、线程和协程?看这篇就够了!
本课程主要探讨操作系统中的进程、线程和协程的区别。进程是资源分配的基本单位,具有独立性和隔离性;线程是CPU调度的基本单位,轻量且共享资源,适合并发执行;协程更轻量,由程序自身调度,适合I/O密集型任务。通过学习这些概念,可以更好地理解和应用它们,以实现最优的性能和资源利用。
116 11
|
3月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
161 17
|
4月前
|
数据采集 机器学习/深度学习 前端开发
PHP爬虫性能优化:从多线程到连接池的实现
本文介绍了一种通过多线程技术和连接池优化PHP爬虫性能的方法,以新浪投诉平台为例,详细展示了如何提高数据采集效率和稳定性,解决了传统单线程爬虫效率低下的问题。
189 2
PHP爬虫性能优化:从多线程到连接池的实现
|
6月前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
105 1
|
6月前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
106 2
|
6月前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
116 0
|
6月前
|
网络协议 安全 Java
难懂,误点!将多线程技术应用于Python的异步事件循环
难懂,误点!将多线程技术应用于Python的异步事件循环
176 0

热门文章

最新文章