项目背景:
云上服务器存储html,前端通过传递给后端html_url, 由后端服务器获取html文件进行渲染,生成pdf,
然后将pdf上传云上服务器。
使用的框架/库:
tornado/ pyppeteer/docker
选择pyppeteer, 有如下依据: python官方库如xhtml2pdf只能处理类似富文本类的静态页面,而html需要js渲染,
故借助浏览器是一种可行的实现方式; tornado是异步框架,pyppeteer是异步库,匹配。
实现过程:
1、选用pyppeteer的版本
tornado==5.1.1
pyppeteer==1.0.2
2、使用docker部署服务, 安装浏览器, dockerfile 如下:
FROM python:3.7-slim COPY ./requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com
# ===== 安装chrome浏览器, 配置 --no-sandbox =================
RUN apt-get update && apt-get install -y wget && \ wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \ apt-get install -y -f ./google-chrome-stable_current_amd64.deb && \ sed -e '/chrome/ s/^#*/#/' -i /opt/google/chrome/google-chrome && \ echo 'exec -a "$0" "$HERE/chrome" "$@" --user-data-dir="$HOME/.config/chrome" --no-sandbox --disable-dev-shm-usage' >> /opt/google/chrome/google-chrome # ========================================================
# ========= 处理pyppeteer引起的僵尸进程问题 =================
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init RUN chmod +x /usr/local/bin/dumb-init
# =======================================================
ADD . /project WORKDIR /project
3、实现模块
注意两个点: 下面两处注释处
async def get_pdf_file2(self, opts): html_url = opts.get("html_url") browser = await pyppeteer.launch( { "executablePath": "/opt/google/chrome/google-chrome", # docker配置的浏览器路径 "timeout": 160000 }) page = await browser.newPage() # waitUntil参数: 直到 http 连接为 0,即页面加载完成
# 这一步不配置, 会导致数据加载不完全,或数据未加载完毕浏览器关闭而导致报错 await page.goto(html_url, { 'waitUntil': 'networkidle0' }) pdf_file = await page.pdf({"format": 'A4'}) await browser.close() return pdf_file
第一次构建镜像会比较慢,耗时53分钟,之后就利用了docker的缓存, 速度就比较快了。
总结:
1、在实现过程中,遇到无法加载浏览器的原因,因为pyppeteer自身有下载headerless浏览器chrumium的实现,但由于docker中缺乏启动该浏览器的依赖,会导致无法启动浏览器,而安装依赖则亦是一个耗时较大的过程,而且中间会存在配置依赖的过程,故选择直接安装稳定版的浏览器,并将其配置成为非沙箱模式。
2、测试过程中发现会出现大量僵尸进程,原因是每一次访问,浏览器都会启动一次,并且产生浏览器的子进程,而browser.close()只能关闭浏览器主进程,子进程交给操作系统的init进程来处理,而docker的init进程是无法像正常操作系统一样处理这些子进程,导致他们无法被处理,形成僵尸进程。如果该问题不处理,最终将导致内存、端口被挤爆。
3、数据加载不完全,且出现运行异常
pyppeteer.errors.NetworkError: Protocol error Target.sendMessageToTarget: Target closed.
原因是浏览器渲染过程中, 去服务器取动态数据,由于是异步过程,数据还未加载完全,浏览器就被程序关闭了。查看源码,先尝试了延迟时间,但依旧无法处理,而且增加耗时,用户体验变差。后来查看文档,配置了'waitUntil': 'networkidle0', 成功解决了这个问题。
4、一个使用比较广泛的库,一定会考虑得比较完善,所以遇到问题一定要仔细查看文档和源码,一般能找到解决方法。当然,也可以上网查看他人解决方法,他山之石,可以攻玉。
5、方案确定后,遇到这种问题,一定要深挖,解决问题总是在山重水复的时候,不经意间就豁然开朗了。而带来的收获可不止是工作上的、知识领域的,经历过的都会明白。
标签:google,浏览器,tornado,pyppeteer,chrome,html,pdf From: https://www.cnblogs.com/wolfstark/p/16857263.html