V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
just1
V2EX  ›  Python

是我对于协程的用法有误还是 http 请求本身就这么消耗资源

  •  
  •   just1 · 2020-06-29 19:13:59 +08:00 · 3781 次点击
    这是一个创建于 1618 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原有的代码是爬虫,化简完代码是这样

    import asyncio
    
    import aiohttp
    
    
    async def worker():
        while 1:
            try:
                async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=5)) as session:
                    async with session.post('https://www.baidu.com') as r:
                        _ = await r.text()
            except RuntimeError:
                break
    
    
    async def main():
        await asyncio.wait([worker() for _ in range(100)])
    
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
        loop.close()
    

    只需要不到 100 个协程,cpu 单核就可以 100%,是我对于协程有错误的理解吗,求指点

    21 条回复    2020-07-02 11:12:33 +08:00
    xiaolinjia
        1
    xiaolinjia  
       2020-06-29 19:32:27 +08:00
    你这不是 100 个。是 100 x while 循环的次数个。
    arrow8899
        2
    arrow8899  
       2020-06-29 19:43:08 +08:00
    不是你这么用的。。。
    j0hnj
        3
    j0hnj  
       2020-06-29 19:52:38 +08:00   ❤️ 1
    百度要被你干死了……
    just1
        4
    just1  
    OP
       2020-06-29 20:00:04 +08:00 via Android
    @xiaolinjia
    @arrow8899
    @j0hnj 求指点...实际工作的脚本应该是从 queue 获得目标 url,这里简化写成了死循环而已
    rimutuyuan
        5
    rimutuyuan  
       2020-06-29 20:02:47 +08:00
    ```import asyncio

    import aiohttp


    async def worker(url):
    try:
    async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=5)) as session:
    async with session.post(url) as r:
    _ = await r.text()
    except RuntimeError:
    break


    async def main():
    urls = ["https://baidu.com", "https://baidu.com"]
    await asyncio.wait([worker(url) for url in range(urls)])


    if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()```
    Vibra
        6
    Vibra  
       2020-06-29 20:02:59 +08:00 via iPhone
    @j0hnj 没有 ua 百度不会理你的
    just1
        7
    just1  
    OP
       2020-06-29 20:19:26 +08:00 via Android
    @rimutuyuan 实际工作的脚本应该是从 queue 获得目标 url,这里简化写成了死循环而已
    crella
        8
    crella  
       2020-06-29 21:39:42 +08:00   ❤️ 1
    如果你要爬取得网站网速快的话,那么平均每两次下载的时间间隔很小,而代码中又是 100 个 worker(),那么可以认为每两次下载时间没有空闲时间,cpu 的单核占用就是达到了 100%
    just1
        9
    just1  
    OP
       2020-06-29 22:18:52 +08:00
    @crella #8 大概可能是这个情况,但是我的疑惑在于 http 会这么消耗 cpu 吗?运行时网络不过是大概 send340kb/s,recv900kb/s (不过小包比较多)
    如果这样好像除了堆机器、开多进程,是不是没有办法优化了?
    silencefly
        10
    silencefly  
       2020-06-29 22:22:43 +08:00 via iPhone
    如一楼的意思 是 100 个 job 一起跑 不是一个 job 跑 100 次
    just1
        11
    just1  
    OP
       2020-06-29 22:44:10 +08:00
    @silencefly #10 就是 100 个 job 一起所以引入协程,不然一个个太慢了
    tolerance
        12
    tolerance  
       2020-06-29 22:59:09 +08:00
    求教,写爬虫怎么才能不违法
    linvaux
        13
    linvaux  
       2020-06-29 23:12:58 +08:00
    @tolerance 做搜索引擎,而且体量要很大的那种
    imn1
        14
    imn1  
       2020-06-29 23:22:49 +08:00
    如果用的是板载网卡,是需要 CPU 处理数据的
    用那种死贵的独立网卡,可以卡内处理数据,省点 CPU
    msg7086
        15
    msg7086  
       2020-06-30 03:30:05 +08:00   ❤️ 1
    这也不是 HTTP 啊,这明明是 HTTPS 啊,初始化 TLS 秘钥交换多次握手不要钱的啊……
    leer
        16
    leer  
       2020-06-30 07:45:36 +08:00 via iPhone
    @tolerance 自己研究不违法,遵守爬虫协议
    lpts007
        17
    lpts007  
       2020-06-30 09:47:37 +08:00
    @just1 你知道 while 1 的意思吗 死循环内异步 单核满载不是意料之中吗,跟 http 协议有什么关系?
    再说你实际业务生产待爬 url 的速度 也不可能就是 while 1 吧。
    注意点别人网站的承受能力,别一不小心搞成攻击了。
    BingoXuan
        18
    BingoXuan  
       2020-06-30 10:04:03 +08:00   ❤️ 2
    https 需要消耗一定 cpu 资源去加解密的,而且你写的是 100x 的死循环请求。假如你一秒单线程能完成 10 次,那么实际就是 1000 次请求了。

    你需要去掉 while 循环,通过 asyncio 创建 size 为 100 的 queue,不断从 queue 中获取任务,通过 futures 来设置 result 或者 error
    yzk66880
        19
    yzk66880  
       2020-06-30 18:09:49 +08:00
    你这 while 1 。。。
    just1
        20
    just1  
    OP
       2020-06-30 20:01:54 +08:00
    @yzk66880 #19 怎么都不看题啊
    yucongo
        21
    yucongo  
       2020-07-02 11:12:33 +08:00
    网上有个 limited_as_completed ( asyncioplus )包,或许适应你要做的。limited_as_completed 的原始作者试过建议将 limited_as_completed 作为 asyncio 的内置标准函数,但好像不成功。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1086 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:12 · PVG 03:12 · LAX 11:12 · JFK 14:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.