Python教程02: selenium-chrome-driver-网页截图微服务
1. selenium-chrome-driver有什么用处?
这是一个使用Docker容器为服务化的一个网页截图微服务的Python-Docker教程. 主要技术:
- Python Flask: 提供微服务API
- selenium: 是最广泛使用的开源Web UI(用户界面)自动化测试套件之一.这里我们就使用它强大的截图API
- Chrome+Chrome-driver: 提供网页渲染
- Docker: 快捷的软件依赖安装和部署
这篇文章的全部源代码 https://github.com/mojocn/wordCloudDocker
2. 为什么使用selenium + chrome-driver?
最开始的时候我选择的使用selenium + phantomjs. 后来尝试多次还是决定使用chrome + chrome-driver做为selenium的驱动. 原因有一下几点:
- phantomjs 2018-03-04 开发者宣布停止开发支持,PhantomJS 2.1.1将会是已知最后的稳定版本.
- phantomjs 2.1.1 我尝试在debian docker 镜像中安装总时报错,安装错误解决不了,导致我写Dockerfile的时候疼苦
- chrome+chrome-driver 可以获取最好的浏览器兼容,安装相对与比较简单.
- 如果在selenium中是用phantomsj python总是提示warning: phantomjs is deprecated
所以最终还是选择了chrome + chrome-driver 作为selenium的驱动, 但是在 debian docker 镜像中安装 chrome 和chrome-driver 因为GF墙的存在也是非常疼苦. 所以建议docker build的时候还是带上梯子.
3. 安装chrome 和 chrome-driver
下面安装chrome 和chrome-driver都已经可以直接执行,不需要使用决定路径.
3.1 安装chrome driver
执行在ubuntu系统中执行下面bash代码,安装最小的chrome-driver,因为墙的下载速度有可能很慢.
CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION && \
curl -sS -o /tmp/chromedriver_linux64.zip http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip && \
unzip -qq /tmp/chromedriver_linux64.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION && \
rm /tmp/chromedriver_linux64.zip && \
chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver && \
ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver
3.2 安装 Install Chrome
因为墙的下载速度有可能很慢.
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
dpkg -i google-chrome-stable_current_amd64.deb; apt-get -fy install
4. python截图API业务代码部分
这一部分我将说明Flask 截图API function. 完整代码地址/code/app.py
4.1 API handler获取参数和生成ULR对应unique唯一文件名,
from selenium import webdriver
import os
import hashlib
@app.route('/ss', methods=['GET'])
def screenshot():
id = request.args.get('i', "0")
url = request.args.get('u', '')
if not url.startswith('http'):
return "url u 参数不是合法的URL地址"
key = hashlib.md5(url.encode('utf-8')).hexdigest()
screen_shot_dir = "/data/screen_shot"
image_name = id+"_"+key + ".png"
image_path = os.path.join(screen_shot_dir, image_name)
....
- 获取url和id参数,其中id参数是数据库主键,
- md5 url参数,md5参数的值有可能会重复
- md5值+id就可以避免文件名重复
- 得到保存图片的绝对路径
4.2 API handler 如果截图不存在启动selenium + chrome-driver 保存截图文件
from selenium import webdriver
import os
import hashlib
....
d = app.root_path
if not os.path.isfile(image_path):
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--user-agent="Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 640 XL LTE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Edge/12.10166"')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--window-size=1024,768')
chrome_options.add_argument('lang=zh_CN.UTF-8')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(chrome_options=chrome_options)
browser.set_window_size(1024, 768)
browser.get(url)
time.sleep(2)
if not browser.save_screenshot(image_path):
image_path = os.path.join(d,'image_error.jpg')
browser.close()
return send_file(image_path, mimetype='image/png', attachment_filename=image_name, as_attachment=False)
设置chrome的启动参数,更多启动参数详解 https://developers.google.com/web/updates/2017/04/headless-chrome:
- –no-sandbox: 设置不适用sandbox 降低内存消耗
- –user-agent: 设置user-agent
- –window-size=1024,768: 设置窗口大小
- –headless: 无头模式,系统中不出现窗口
- –disable-gpu 关闭GPU加速
如果截图不存在,就开始selenium chrome-driver 截图保存到当前路径. Flask 做出图片响应Response.
5. API接口说明
接口 | 说明 |
---|---|
URI | :8111/ss |
url-参数:u | url截图网址 |
url-参数:i | id防止md5 url值重复 |
HTTP-Response | image/png |
6. Dockerfile 详解
FROM python:3.6
# 设置环境变脸
# 设置chrome的可执行程序PATH
ENV TIME_ZONE=${TIME_ZONE} \
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/chrome
# 更换成国内源
COPY sources.list /etc/apt/china.sources.list
# 安装工具依赖
RUN mkdir /code \
&&mv /etc/apt/sources.list /etc/apt/source.list.bak \
&&mv /etc/apt/china.sources.list /etc/apt/sources.list\
&&apt-get update \
&&apt-get -y install freetds-dev \
&&apt-get -y install unixodbc-dev
# 给selenium chrome安装中文字体
# 方式chrome 截图中文出现方块,显示中文出错
RUN apt-get update \
&&apt-get -y install xfonts-wqy ttf-wqy-microhei
# 安装chromedirver
# Install Chrome WebDriver
RUN CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION && \
curl -sS -o /tmp/chromedriver_linux64.zip http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip && \
unzip -qq /tmp/chromedriver_linux64.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION && \
rm /tmp/chromedriver_linux64.zip && \
chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver && \
ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver
# 下载 Chrome deb
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
# 安装chrome浏览器
RUN dpkg -i google-chrome-stable_current_amd64.deb; apt-get -fy install
# copy python 代码文件
COPY code /code
# pip 批量安装 python package
# 使用国内源
RUN pip install -r /code/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
# 设置工作目录
WORKDIR /code
# 暴露端口
EXPOSE 8111
# 启动flask
# 加载gunicorn 配置文件
CMD ["gunicorn", "app:app", "-c", "./gunicorn.conf.py"]
Dockerfile 完成工作内容如下:
- 使用Python 3.6 镜像
- 更换国内apt-get源
- 安装chrome 需要的中文字体
- 安装chrome浏览器和chrome-driver
- pip 安装python 依赖
- 使用gunicorn 启动Python Flask 程序
6.1 Docker编译
docker build -t seleniumshot https://github.com/mojocn/wordCloudDocker.git
6.2 Docker run
挂载container volumne,
/data/screen_shot
用来保存selenium chrome-driver 的截图,
/data/log
用来保存 Python Flask 的日志,
docker run -d -p 8111:8111 \
-v /data/dir_of_selenium_image:/data/screen_shot \
-v /data/dir_of_flask_log:/data/log \
seleniumshot