如何在Django REST中通过Pyppeteer登录并生成PDF
Django REST Framework + Pyppeteer 生成受限端点PDF问题解决
问题描述
在Django REST Framework项目中使用Pyppeteer==0.2.6生成PDF:
- 当视图
permission_class设为AllowAny时,PDF生成正常 - 访问需要登录的受限端点时,控制台提示目标对象不存在(实际对象存在)
- 尝试通过Pyppeteer登录API页面、后台页面,问题未解决
原代码
async def render_page_as_pdf(self, url, sentinel=SENTINEL): page = await self.browser_page() await page.emulateMedia('print') await page.goto('http://localhost:8000/api/auth/login/?next=/api/rest-auth/user/') await page.type('#id_username', 'username@test.com') await page.type('#id_password', 'my_pass') await page.click('#submit-id-submit') try: await page.evaluate(f"""() => window.goto("{url}")""") except PyppeteerError: await page.goto(url) try: await page.waitFor(sentinel, timeout=self.timeout, visible=True) return await page.pdf( format=self.page_format, timeout=self.timeout, printBackground=True, ) except (TimeoutError, PyppeteerError): logger.exception(f'error to generate pdf: {url}') return None
控制台输出
"GET /document/sale/31/ HTTP/1.1" 200 - Forbidden: /api/rest-auth/user/ Forbidden: /api/rest-auth/user/ 172.19.0.1 - - [01/Jul/2024 13:41:48] "GET /api/rest-auth/user/ HTTP/1.1" 403 - Not Found: /api/documentsales/31/ Not Found: /api/documentsales/31/ 172.19.0.1 - - [01/Jul/2024 13:41:48] "GET /api/documentsales/31/ HTTP/1.1" 404 -
问题分析
- 会话未正确建立:登录后未等待页面跳转完成,导致认证cookie未被正确设置就访问目标URL
- 错误的页面跳转方法:
window.goto不是浏览器原生方法,会触发错误 - URL路径不匹配:控制台中出现的
/api/documentsales/31/与目标/document/sale/31/路径不一致,可能是参数拼写错误 - 权限验证问题:即使登录成功,用户可能没有目标对象的访问权限
解决方案
修正后的代码
async def render_page_as_pdf(self, url, sentinel=SENTINEL): page = await self.browser_page() await page.emulateMedia('print') # 访问登录页,等待页面完全加载 login_url = 'http://localhost:8000/api/auth/login/' await page.goto(login_url, waitUntil='networkidle2') # 输入账号密码并登录,等待跳转完成确保会话建立 await page.type('#id_username', 'username@test.com') await page.type('#id_password', 'my_pass') await Promise.all([ page.click('#submit-id-submit'), page.waitForNavigation(waitUntil='networkidle2') ]) # 访问目标URL,等待页面加载完成 try: await page.goto(url, waitUntil='networkidle2') await page.waitFor(sentinel, timeout=self.timeout, visible=True) return await page.pdf( format=self.page_format, timeout=self.timeout, printBackground=True, ) except (TimeoutError, PyppeteerError): logger.exception(f'error to generate pdf: {url}') finally: await page.close() # 释放页面资源 return None
额外排查步骤
- 验证URL正确性:确认传入的
url参数路径与实际端点一致,避免拼写错误 - 手动验证权限:登录对应账号后,手动访问目标URL,确认能正常加载内容
- 检查会话状态:登录后通过
await page.cookies()查看是否携带sessionid等认证cookie - 确认对象权限:如果使用DRF的对象级权限(如
DjangoObjectPermissions),确保用户对目标ID的对象有访问权限
内容的提问来源于stack exchange,提问作者user25455142
相关产品推荐
相关产品推荐

