首页 > 其他分享 >协程

协程

时间:2023-02-17 23:23:07浏览次数:29  
标签:chapter 协程 name get url text asyncio

  • 引入:无论多线程还是多进程其实没有解决一个性能相关的问题,IO阻塞,无论是多进程还是多线程,在遇到IO阻塞时都会被操作系统强行剥夺走CPU的执行权限(使得cup执行其他操作,其他操作可能是我们程序的其他部分,也可能是其他的应用程序),我们自己程序的执行效率因此就降低了下来。
  • 在python3.5之后新增了asyncio模块,可以帮我们检测IO(只能是网络IO【HTTP连接就是网络IO操作】),实现应用程序级别的切换(异步IO)。
  • 1、协程基本语法
import asyncio

async def func():
    print('我是任务')

if __name__ == '__main__':
    #func()   协程函数加上括号,产生一个协程对象
    #执行协程函数的固定逻辑
    # 1、创建好协程对象
    # 2、用asyncio包来运行
    f = func()

    #运行方案有两种
    #1 、 直接run   直接run,windows中容易出现"Event is Closed"
    # asyncio.run(f)
    #2、 需要获取一个事件循环的东西
    #创建事件循环
    event_loop = asyncio.get_event_loop()
    #运行协程对象,直到结束
    event_loop.run_until_complete(f)
  • 2、协程的用法和await
import asyncio


async def faker1():
    print("任务1开始")
    await asyncio.sleep(1)
    print("任务1完成")
    return "任务1结束"


async def faker2():
    print("任务2开始")
    await asyncio.sleep(2)
    print("任务2完成")
    return "任务2结束"


async def faker3():
    print("任务3开始")
    await asyncio.sleep(3)
    print("任务3完成")
    return "任务3结束"


async def main():
    tasks = [
        asyncio.create_task(faker3()),
        asyncio.create_task(faker1()),
        asyncio.create_task(faker2()),
    ]
    # 方案一, 用wait, 返回的结果在result中
    result, pending = await asyncio.wait(tasks)
    for r in result:
        print(r.result())
        
    # 方案二, 用gather, 返回的结果在result中, 结果会按照任务添加的顺序来返回数据
    # 	return_exceptions如果任务在执行过程中报错了. 返回错误信息. 
    result = await asyncio.gather(*tasks, return_exceptions=True)
    for r in result:
        print(r)


if __name__ == '__main__':
    asyncio.run(main())
  • 3、固定的爬虫模板
import requests,time
import asyncio

async def get_page_inf0(url):
    print("发请求到", url)
    await asyncio.sleep(3)  # ????
    print("拿到了页面源代码")
    return " 我就是源代码 "

async def main():
    urls = [
        "http://www.baidu.com",
        "http://www.taobao.com",
        "http://www.google.com",
    ]
    tasks = []
    for url in urls:
        f = get_page_inf0(url)
        t = asyncio.create_task(f)  
        tasks.append(t)
    await asyncio.wait(tasks)

if __name__ == '__main__':
    start = time.time()
    event_loop = asyncio.get_event_loop()
    event_loop.run_until_complete(main())
    end = time.time()
    print(f"总共耗时{end - start}")
  • 4、aiohttp模块基本使用
    • 4.1:基本框架
with aiohttp.ClientSession() as sess:
       #基于请求对象发起请求
       #此处的get是发起get请求,常用参数:url,headers,params,proxy
       #post方法发起post请求,常用参数:url,headers,data,proxy
       #发现处理代理的参数和requests不一样(注意),此处处理代理使用proxy='http://ip:port'
        with sess.get(url=url) as response:
           page_text = response.text()
           #text():获取字符串形式的响应数据
           #read():获取二进制形式的响应数据
           return page_text
  • 4.2:补充细节
    • 在每一个with前加上async关键字
    • 在阻塞操作前加上await关键字
async def get_request(url):
    #requests是不支持异步的模块
    # response = await requests.get(url=url)
    # page_text = response.text
    #创建请求对象(sess)
    async with aiohttp.ClientSession() as sess:
        #基于请求对象发起请求
        #此处的get是发起get请求,常用参数:url,headers,params,proxy
        #post方法发起post请求,常用参数:url,headers,data,proxy
        #发现处理代理的参数和requests不一样(注意),此处处理代理使用proxy='http://ip:port'
        async with await sess.get(url=url) as response:
            page_text = await response.text()
            #text():获取字符串形式的响应数据
            #read():获取二进制形式的响应数据
            return page_text
  • 5、爬取小说 示例
"""
需求:爬取明朝那些事,总共7卷
每一卷单独保存一个文件夹,每一章保存为一个文件

分析:
1、首先获取每一卷的名称、每一章的名称,每一章详情的url
2、数据格式为
    ["juan_name": 万国来朝,chapter_name:第一章, chapter_url:xxx]
    ["juan_name": 万国来朝,chapter_name:第二章, chapter_url:xxx]
    ["juan_name": 万国来朝,chapter_name:第三章, chapter_url:xxx]
    ["juan_name": 最后一章,chapter_name:第一章, chapter_url:xxx]
    ....
3、使用协程获取每一章的详情数据,并且保存为文件
"""
import requests
from lxml import etree
import asyncio
import aiohttp
import aiofiles
import os,time

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}
def get_chapter_info(url):
    resp = requests.get(url=url,headers = headers)
    resp.encoding = 'utf-8'
    tree = etree.HTML(resp.text)
    #获取所有的divs标签
    divs = tree.xpath("//div[@class='mulu']")   #每个div就是一卷
    result = []
    for div in divs:
        trs = div.xpath(".//table/tr")   #一堆tr
        # 第一个tr标签是卷名
        juan_name = trs[0].xpath(".//a/text()")
        #处理卷名,有些特殊字符在保存为文件名时有问题
        juan_name = "".join(juan_name).replace(":","_").strip()
        # print(juan_name)
        for tr in trs[1:]:
            tds = tr.xpath(".//td")
            for td in tds:
                txt = td.xpath(".//a/text()")
                href = td.xpath(".//a/@href")

                txt = "".join(txt).strip().replace(" ","")
                href = "".join(href).strip()
                # print(txt,href)

                dic = {
                    "chapter_name":txt,
                    "chapter_url":href,
                    "juan_name":juan_name
                }
                result.append(dic)
    return result

async def download_chapter_one(url,file_path):
    print("我要开始下载了")
    async with aiohttp.ClientSession() as session:
        async with session.get(url=url,headers=headers) as resp:
            page_soure = await resp.text(encoding="utf-8")
            tree = etree.HTML(page_soure)
            text = tree.xpath("//div[@class='content']//p//text()")
            text = "".join(text).strip().replace("\r","")

            #写入文件
            async with aiofiles.open(file_path,"w",encoding='utf-8') as f:
                await f.write(text)

async def download_chapter(chapter_lst):
    tasks = []
    for chapter in chapter_lst:
        juan = chapter["juan_name"]
        name = chapter["chapter_name"]
        url = chapter["chapter_url"]
        #r如果卷名文件夹不存在,就新建
        if not os.path.exists(juan):
            os.makedirs(juan)
        #文件保存路径
        file_path = f"{juan}/{name}.txt"
        f = download_chapter_one(url,file_path)
        t = asyncio.create_task(f)
        tasks.append(t)

        # break   #测试代码的时候,不注释
    await asyncio.wait(tasks)


def main():
    url = 'https://www.mingchaonaxieshier.com/'
    chapter_lst = get_chapter_info(url)
    # print(chapter_lst)
    event_loop = asyncio.get_event_loop()
    event_loop.run_until_complete(download_chapter(chapter_lst))
    # asyncio.run(download_chapter(chapter_lst))


if __name__ == '__main__':
    start = time.time()
    main()
    end = time.time()
    print(f"下载完毕总共耗时:{end - start}")

标签:chapter,协程,name,get,url,text,asyncio
From: https://www.cnblogs.com/xwltest/p/17131742.html

相关文章

  • 第三篇:协程
    协程的介绍"""进程:资源单位线程:执行单位协程:单线程下实现并发(这个概念完全是由程序员意淫出来的,根本不存在)程序员在代码层面上检测所有的IO操作,一旦遇到IO,将在代......
  • 4.协程模块
    协程:用户态的线程,相当于线程中的线程,更轻量级。后续配置sockethook,可以把复杂的异步调用,封装成同步操作。降低业务逻辑的编写复杂度。目前该协程是基于ucontext_t来实现......
  • Python 高级编程之生成器与协程进阶(五)
    目录一、概述二、生成器1)生成器和迭代器的区别2)生成器创建方式1、通过生成器函数创建2、通过生成器表达式创建3)生成器表达式4)yield关键字5)生成器函数6)return和yield异同......
  • Python 高级编程之IO模型与协程(四)
    目录一、概述二、IO模型1)IO模型准备1、用户空间和内核空间2、进程切换3、进程的阻塞4、文件描述符fd5、缓存I/O2)IO模型详解1、同步阻塞IO(BlockingIO)2、同步非阻塞IO(Non......
  • Go使用协程批量获取数据,加快接口返回速度
    服务端经常需要返回一个列表,里面包含很多用户数据,常规做法当然是遍历然后读缓存。使用Go语言后,可以并发获取,极大提升效率。使用channelpackagemainimport("fmt"......
  • Python使用协程的缺点
    1、多核资源不能使用:协程的本质是单线程,它不能同时使用单个CPU的多核、协程。2、在多CPU上运行程需要与过程配合。当然,每天编写的大多数应用程序都没有必要。除非是cpu密集......
  • Go使用协程批量获取数据,加快接口返回速度
    服务端经常需要返回一个列表,里面包含很多用户数据,常规做法当然是遍历然后读缓存。使用Go语言后,可以并发获取,极大提升效率。使用channelpackagemainimport("fmt"......
  • Python requests.Session 协程 下载文件
    Pythonrequests.Session协程下载文件 #coding:utf-8fromgeventimportmonkeymonkey.patch_all()fromgevent.poolimportPoolimportgeventimportrequ......
  • Python中monkey.patch_all()解决协程阻塞问题
    Python全栈工程师核心面试300问深入解析(2020版)----全文预览Python全栈工程师核心面试300问深入解析(2020版)----欢迎订阅直接参考以下实例,采用协程访问三个网站......
  • kotlin协程介绍(一)
    问题背景我们在kotlin的学习和使用过程中,协程是一个很重要的概念,这个也是java没有的,那么,什么是协程呢?协程的定义:可以将它理解成一种轻量级的线程。我们之前所学习的线程是......