Django视图间对象传递及LinkedIn登录验证码交互实现问题
解决Django视图中LinkedIn登录验证码Checkpoint的交互问题
你的核心问题在于:每次请求verify_linkedin视图时都会创建一个全新的LinkedinScraper实例,当第一次请求触发checkpoint返回表单后,原来的WebDriver实例已经被销毁;用户提交验证码时,新创建的LinkedinScraper根本不在checkpoint页面,自然无法完成验证。
WebDriver实例本身不能序列化,没法直接存在Django的session里,但我们可以通过以下两种方案解决这个状态保持的问题:
方案一:使用远程ChromeDriver复用会话
这是最可靠的方案,因为它允许跨请求复用同一个浏览器实例。
步骤1:启动远程ChromeDriver服务
先在服务器上启动一个远程ChromeDriver服务(确保已安装Chrome和对应版本的ChromeDriver):
chromedriver --port=9515 --url-base=/wd/hub
步骤2:修改LinkedinScraper类支持远程连接
更新你的Webscraper后端代码,让它可以从session中读取已有的会话ID,复用浏览器实例:
from selenium.webdriver.remote.webdriver import WebDriver from selenium import webdriver class LinkedinScraper(WebScraper): def __init__(self, session_id=None, command_executor="http://localhost:9515/wd/hub", headless=True, **kwargs): super().__init__() if session_id: # 复用已存在的浏览器会话 self.driver = WebDriver(command_executor=command_executor, desired_capabilities={}) self.driver.close() # 关闭默认新建的空白窗口 self.driver.session_id = session_id else: # 创建新的浏览器实例 options = webdriver.ChromeOptions() if headless: options.add_argument("--headless=new") options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage") # 添加UA模拟真实浏览器,避免被LinkedIn检测 options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36") self.driver = webdriver.Remote(command_executor=command_executor, options=options) self.data = {} self.pause = 10 self.retries = 0 self.max_retries = 3 # 保留原有的login_to_linkedin和authenticate_linkedin_login方法
步骤3:修改Django视图复用会话
更新verify_linkedin视图,将WebDriver的会话ID存在用户的session中,实现跨请求复用:
def verify_linkedin(request): # 从session获取已有的浏览器会话ID session_id = request.session.get('linkedin_driver_session_id') ln = LinkedinScraper(session_id=session_id) if request.method == "POST": # 用户提交验证码,执行验证逻辑 form = VerifyLinkedinForm(request.POST) if form.is_valid(): pin_code = form.cleaned_data['verification_key'] try: ln.authenticate_linkedin_login(pin_code) # 登录成功,清理session并关闭浏览器 request.session.pop('linkedin_driver_session_id', None) ln.driver.quit() return redirect("home") except Exception as e: ln.driver.quit() return HttpResponse(f"<h1>验证失败:{str(e)}</h1>") else: # 首次请求,尝试自动登录 login_success = ln.login_to_linkedin( username=os.getenv("LINKEDIN_USERNAME"), password=os.getenv("LINKEDIN_PASSWORD") ) if login_success: # 无验证直接登录成功,清理资源 ln.driver.quit() return HttpResponse("<h1>You are verified</h1>") else: # 触发checkpoint,保存会话ID到session request.session['linkedin_driver_session_id'] = ln.driver.session_id form = VerifyLinkedinForm() context = {"form": form} return render(request, "linkedin_verify.html", context)
方案二:保存浏览器状态(Cookie + 当前URL)
如果不想依赖远程ChromeDriver服务,可以在第一次触发checkpoint时,把浏览器的Cookie和当前URL存在session里,提交验证码时重新初始化WebDriver并恢复状态:
修改LinkedinScraper类添加状态保存/恢复方法
class LinkedinScraper(WebScraper): # 保留原有的__init__、login_to_linkedin、authenticate_linkedin_login方法 def save_state(self): """保存当前浏览器的关键状态(Cookie + 页面URL)""" return { 'cookies': self.driver.get_cookies(), 'current_url': self.driver.current_url } def restore_state(self, state): """恢复之前的浏览器状态""" self.driver.get(state['current_url']) for cookie in state['cookies']: # 处理SameSite属性的兼容性问题 if 'sameSite' in cookie and cookie['sameSite'] not in ['Strict', 'Lax', 'None']: cookie['sameSite'] = 'Lax' self.driver.add_cookie(cookie) # 重新加载页面确保Cookie生效 self.driver.get(state['current_url'])
修改Django视图使用状态保存逻辑
def verify_linkedin(request): if request.method == "POST": form = VerifyLinkedinForm(request.POST) if form.is_valid(): pin_code = form.cleaned_data['verification_key'] # 恢复之前保存的浏览器状态 ln = LinkedinScraper() state = request.session.get('linkedin_login_state') if not state: return HttpResponse("<h1>会话已过期,请重新尝试登录</h1>") ln.restore_state(state) try: ln.authenticate_linkedin_login(pin_code) request.session.pop('linkedin_login_state', None) ln.driver.quit() return redirect("home") except Exception as e: ln.driver.quit() return HttpResponse(f"<h1>验证失败:{str(e)}</h1>") else: ln = LinkedinScraper() login_success = ln.login_to_linkedin( username=os.getenv("LINKEDIN_USERNAME"), password=os.getenv("LINKEDIN_PASSWORD") ) if login_success: ln.driver.quit() return HttpResponse("<h1>You are verified</h1>") else: # 保存状态到session,然后关闭当前实例 request.session['linkedin_login_state'] = ln.save_state() ln.driver.quit() form = VerifyLinkedinForm() context = {"form": form} return render(request, "linkedin_verify.html", context)
关键注意事项
- 会话过期处理:给session设置合理的过期时间(比如10分钟),避免无效的状态占用资源。
- 资源清理:无论登录成功还是失败,一定要调用
driver.quit()关闭浏览器实例,防止内存泄漏。 - 反爬规避:LinkedIn对自动化工具检测严格,除了添加UA,还可以尝试禁用自动化标识:
options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False)
内容的提问来源于stack exchange,提问作者pAulseperformance
相关产品推荐
相关产品推荐

