在进行网页爬取时,网页的响应速度慢于计算机的处理速度,在串行模式下访问多个网页,在通过网络获取一个网页的过程中,在这个过程之前,计算机只能闲置等待。而异步的方式就是让计算机可以在这个闲置时间先去做后面的工作,等这个网页响应完成再对它进行处理。
这里说的这种异步的方式是一种分时获得时间片的机制,有些像多线程,不同于多进程,私以为比较适合爬虫这种IO密集型操作。
-
异步编程包——asyncio
这里直接通过代码来说明它的语法
async def job(t): # async 形式的功能 print('Start job ', t) await asyncio.sleep(t) # 等待 "t" 秒, 期间切换其他任务 print('Job ', t, ' takes ', t, ' s') async def main(loop): # async 形式的功能 tasks = [ loop.create_task(job(t)) for t in range(1, 3) ] # 创建任务, 但是不执行 await asyncio.wait(tasks) # 执行并等待所有任务完成 t1 = time.time() loop = asyncio.get_event_loop() # 建立 loop loop.run_until_complete(main(loop)) # 执行 loop loop.close() # 关闭 loop print("Async total time : ", time.time() - t1) # Start job 1 # Start job 2 # Job 1 takes 1 s # Job 2 takes 2 s # Async total time : 2.0259742736816406
再做个非异步串行的对比操作
def job(t): print('Start job ', t) time.sleep(t) # wait for "t" seconds print('Job ', t, ' takes ', t, ' s') def main(): [job(t) for t in range(1, 3)] t1 = time.time() main() print("NO async total time : ", time.time() - t1) # Start job 1 # Job 1 takes 1 s # Start job 2 # Job 2 takes 2 s # NO async total time : 3.001530647277832
(注意rang(1,3)的结果是只有1,2两个值)
可以看到异步方式的执行总时间是多个任务的最大时间,而非异步方式的执行总时间是多个任务的执行时间之和。当然这是在这种简单实例下的结果,在复杂问题或者非IO密集型的处理过程中,效果不会这么理想。
-
将异步操作应用到爬虫——aiohttp
对于aiohttp,可以简单的理解为异步形式的requests,(只是逻辑上的理解,语法并不同)
还是通过代码来说明语法
URL = 'https://blog.csdn.net/BBJG_001' async def job(session, url): response = await session.get(url) # 等待并切换 return str(response.url) async def main(loop): async with aiohttp.ClientSession() as session: # 官网推荐建立 Session 的形式 tasks = [loop.create_task(job(session, URL)) for _ in range(2)] finished, unfinished = await asyncio.wait(tasks) all_results = [r.result() for r in finished] # 获取所有结果 # print(all_results) t1 = time.time() loop = asyncio.get_event_loop() loop.run_until_complete(main(loop)) loop.close() print("Async total time:", time.time() - t1) # Async total time: 0.44501423835754395
同样也进行非异步方式的对比
URL = 'https://blog.csdn.net/BBJG_001' def normal(): for i in range(2): r = requests.get(URL) url = r.url # print(url) t1 = time.time() normal() print("Normal total time:", time.time()-t1) # Normal total time: 0.7451720237731934
可以看到在真正应用到网络环境中之后效果提速效果就不再像上面那样明显了,但是效率的提升还是很可观的。
-
参考文献