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

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

爬虫

前言

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

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


一、多线程爬虫:

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

相关文章
|
18天前
|
消息中间件 前端开发 Java
美团面试:如何实现线程任务编排?
线程任务编排指的是对多个线程任务按照一定的逻辑顺序或条件进行组织和安排,以实现协同工作、顺序执行或并行执行的一种机制。 ## 1.线程任务编排 VS 线程通讯 有同学可能会想:那线程的任务编排是不是问的就是线程间通讯啊? 线程间通讯我知道了,它的实现方式总共有以下几种方式: 1. Object 类下的 wait()、notify() 和 notifyAll() 方法; 2. Condition 类下的 await()、signal() 和 signalAll() 方法; 3. LockSupport 类下的 park() 和 unpark() 方法。 但是,**线程通讯和线程的任务编排是
22 1
|
19天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
19天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
19天前
|
数据采集 Python
python并发编程:使用多线程,Python爬虫被加速10倍
python并发编程:使用多线程,Python爬虫被加速10倍
19 1
python并发编程:使用多线程,Python爬虫被加速10倍
|
19天前
|
安全 调度 Python
探索Python中的并发编程:协程与多线程的比较
本文将深入探讨Python中的并发编程技术,重点比较协程与多线程的特点和应用场景。通过对协程和多线程的原理解析,以及在实际项目中的应用案例分析,读者将能够更好地理解两种并发编程模型的异同,并在实践中选择合适的方案来提升Python程序的性能和效率。
|
5天前
|
数据采集 存储 C++
单线程 vs 多进程:Python网络爬虫效率对比
本文探讨了Python网络爬虫中的单线程与多进程应用。单线程爬虫实现简单,但处理速度慢,无法充分利用多核CPU。而多进程爬虫通过并行处理提高效率,更适合现代多核架构。代码示例展示了如何使用代理IP实现单线程和多进程爬虫,显示了多进程在效率上的优势。实际使用时还需考虑代理稳定性和反爬策略。
单线程 vs 多进程:Python网络爬虫效率对比
|
10天前
|
消息中间件 安全 调度
基于Python的性能优化(线程、协程、进程)
一、多线程 在CPU不密集、IO密集的任务下,多线程可以一定程度的提升运行效率。
|
17天前
|
Java 测试技术 Python
Python的多线程允许在同一进程中并发执行任务
【5月更文挑战第17天】Python的多线程允许在同一进程中并发执行任务。示例1展示了创建5个线程打印"Hello World",每个线程调用同一函数并使用`join()`等待所有线程完成。示例2使用`ThreadPoolExecutor`下载网页,创建线程池处理多个URL,打印出每个网页的大小。Python多线程还可用于线程间通信和同步,如使用Queue和Lock。
42 1
|
19天前
|
存储 安全 Java
Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列
Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列
|
19天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
56 0