高性能异步爬虫概述

利用线程和线程池进行爬虫:

同步调用:即提交一个任务后就在原地等待任务结束,等到拿到任务的结果后再继续下一行代码,效率低下,调用一个任务,就在原地等待任务结束拿到结果后才继续往后执行。

解决方案一:使用多线程/多进程(不建议)

好处:在服务器端使用多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他的连接。
缺点:在遇到要同时处理成百上千个的连接请求时,则无论多线程还是多进程都会严重占据系统资源,降低系统对外界响应效率,而且线程与进程本身也更容易进入假死状态。

解决方案二:是哟个线程池/进程池(适当)

好处:减少创建和销毁线程的频率,其维持一定合理数量的线程,让空闲的线程重新承担新的执行任务。可以很好的降低系统开销。
缺点:当请求大大超过上限时,“池”构成的系统对外界的响应并不比没有池的时候效果好多少。所以使用“池”必须考虑其面临的响应规模,并根据响应规模调整“池”的大小

总结:对应上例中的所面临的可能同时出现的上千甚至上万次的客户端请求,“线程池”或“连接池”或许可以缓解部分压力,但是不能解决所有问题。总之,多线程模型可以方便高效的解决小规模的服务请求,但面对大规模的服务请求,多线程模型也会遇到瓶颈,可以用非阻塞接口来尝试解决这个问题。

弊端:IO阻塞,无论是多进程还是多线程,在遇到IO阻塞时都会被操作系统强行剥夺走CPU的执行权限,程序的执行效率因此就降低了下来。

使用异步协程进行爬虫:

优点:从应用程序级别检测IO阻塞然后切换到我们自己程序的其他任务执行,这样把我们程序的IO降到最低,我们的程序处于就绪态就会增多,以此来迷惑操作系统,操作系统便以为我们的程序是IO比较少的程序,从而会尽可能多的分配CPU给我们,这样也就达到了提升程序执行效率的目的。

在python3.4之后版本。可以利用asyncio模块检测IO阻塞,并实现异步IO。

异步IO:发起一个 IO阻塞 操作,却不用等它结束,你可以继续做其他事情,当它结束时,会得到通知。

实现异步IO操作方式:单线程+异步协程
import asyncio
async def execute(x):
    print('Number:', x)
coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)
print('After calling loop')

运行结果:

Coroutine: 
After calling execute
Number: 1
After calling loop

最终:使用aiohttp模块实现单线程并发IO操作:

pip install aiohttp
#环境安装:pip install aiohttp
#使用该模块中的ClientSession
import requests
import asyncio
import time
import aiohttp
start = time.time()
urls = [
    'http://127.0.0.1:5000/bobo','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom',
    'http://127.0.0.1:5000/bobo''http://127.0.0.1:5000/jay''http://127.0.0.1:5000/tom',
    'http://127.0.0.1:5000/bobo''http://127.0.0.1:5000/jay''http://127.0.0.1:5000/tom',
    'http://127.0.0.1:5000/bobo''http://127.0.0.1:5000/jay''http://127.0.0.1:5000/tom',
]
async def get_page(url):
    async with aiohttp.ClientSession() as session:
        #get()、post():
        #headers,params/data,proxy='http://ip:port'
        async with await session.get(url) as response:
            #text()返回字符串形式的响应数据
            #read()返回的二进制形式的响应数据
            #json()返回的就是json对象
            #注意:获取响应数据操作之前一定要使用await进行手动挂起
            page_text = await response.text()
            print(page_text)
tasks = []
for url in urls:
    c = get_page(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print('总耗时:',end-start)

总结:使用了异步协程之后,我们几乎可以在相同的时间内实现成百上千倍次的网络请求,把这个运用在爬虫中,速度提升可谓是非常可观了。

posted @ 2020-05-15 11:02  y0um  阅读(168)  评论(0编辑  收藏  举报

新人优惠服务器