前言
对于某一个站点,我们通常会发送多个请求,为了保存请求参数(cookies,headers),就必须用到session来保持会话,以便接下来的操作正常执行。尤其是在某些站点需要登录的时候,我们就不得不使用session来保持会话了。
试了网络上流传的方法,似乎都不好使。
这篇文章主要介绍如何实现aiohttp.ClientSession()持久化。
避坑
- 文章一
- 文章二
- 在类方法中,手动创建session,再把session传到类属性中。(如果方法没有被声明为async类型,执行后会出现警告,让你去把ClientSession写到协程函数中;如果方法被声明为async类型,执行后会出现错误——这个函数没有使用await)
- 还有一种,找不到网址了,贴在文章后面
解决方案
在自定义的爬虫类中,创建一个异步函数执行的总入口
import asyncio
import aiohttp
class Spider(object):
def __init__(self):
# 起始url
self.start_url ="https://www.baidu.com/"
async def param(self,session):
'''
发起请求并解析文本
:param session: 传入的session
:return:
'''
resp=await session.get(self.start_url)
async def main(self):
# 持久化session
async with aiohttp.ClientSession() as session:
# 创建并添加协程任务,传session
param_task = asyncio.create_task(self.param(session))
# 挂起协程任务
await param_task
if __name__ == '__main__':
sp = Spider()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(sp.main())
基于此,以后的协程任务,都需要在async with aiohttp.ClientSession() as session:
中创建。
大坑
import os
import asyncio
import aiohttp
import logging
session_list = {}
logger = logging.getLogger(__name__)
class Req:
@property
def set_session(self):
try:
loop = asyncio.get_running_loop()
except:
loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
session = aiohttp.ClientSession(loop=loop)
# 利用pid标记不同进程的session
session_list.update({os.getpid(): session})
return session
def __init__(self):
if session_list.get(os.getpid()):
self.session = session_list.get(os.getpid())
logger.info("复用session")
else:
self.session = self.set_session
logger.info(f"PID: {os.getpid()} 初次生成")
async def test(self):
if session_list:
session = session_list.get(os.getpid())
if session and session.closed:
session_list.pop(os.getpid())
session = self.set_session
logger.info("session不可用,重新生成session")
elif not session:
logger.info(f"[{os.getpid()}] session_list为空,创建一个session")
session = self.set_session
if not session or not session.loop.is_running():
session = self.set_session
logger.error("session异常")
resp = await session.get("http://www.baidu.com")
return resp.status
req = Req()
req.test()
完事!!!