使用Selenium 在服务器上对网页截图
1. 需求场景
在服务器上实现对特定网页截图
2. 使用工具
- selenium
- xvfb
3. 开发流程
1. Python脚本对网页截图功能的实现
from selenium import webdriver
url = raw_input("Please input URL:")
save_fn = raw_input("Please input filename:")
browser = webdriver.Firefox()
browser.set_window_size(1200, 900)
browser.get(url)
browser.save_screenshot(save_fn)
browser.close()
2. 服务器端没有Xwindow如何截图
xvfb工具相当于一个wrapper, 给应用程序提供虚拟的 X server
Xvfb 可以直接处理 Window 的图形化功能,並且不會把图像输出到屏幕上,也就是说,就算你的电脑沒有启动 Xwindow , 你仍然可以执行图形程序。
sudo apt-get install xvfb
export DISPLAY=:10
xvfb-run python capture.py
3. 浏览器的选择
PhantomJs、Firefox、Chrome
apt-get install phantomjs
driver = webdriver.PhantomJS()
为什么不用PhantomJs: 动态加载页面效果不好
chrome 使用前需要先下载一个chromedriver, 并在使用时指定该文件
browser = webdriver.Chrome(executable_path='/usr/lib/chromium-browser/chromedriver', chrome_options=chrome_options)
- firefox 版本问题
在使用selenium和Firefox组合的时候很多情况下会遇到兼容问题,比如下面这种:
File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/firefox/firefox_binary.py", line 99, in _wait_until_connectable
"The browser appears to have exited "
selenium.common.exceptions.WebDriverException: Message: The browser appears to have exited before we could connect. If you specified a log_file in the FirefoxBinary constructor, check it for details.
解决办法:
升级selenium
安装firefox旧版本
https://www.liberiangeek.net/2012/04/how-to-install-previous-versions-of-firefox-in-ubuntu-12-04-precise-pangolin/
4. 截图时遇到的一些问题:
- 长网页截图
对于一些比较长的网页,为了节省网页加载时间,很多下面的内容只有在你下滑到那个地方的时候才会加载,我们可以用browser执行一段js代码来实现滚动条的滚动
browser.execute_script("""
(function () {
var y = 0;
var step = 100;
window.scroll(0, 0);
function f() {
if (y < document.body.scrollHeight) {
y += step;
window.scroll(0, y);
setTimeout(f, 50);
} else {
window.scroll(0, 0);
document.title += "scroll-done";
}
}
setTimeout(f, 1000);
})();
""")
- 弹窗
可以在预期有弹窗的网页捕获弹窗并关闭
from selenium.common.exceptions import NoAlertPresentException # 2.16版本以上
browser.get(url) # Load page
alert = browser.switch_to.alert
try:
alert.dismiss()
except NoAlertPresentException:
pass
- 视频播放 or 长时间无响应
- set timeout
browser.set_page_load_timeout(3*60)
- 函数计时装饰器
def timeout(seconds, error_message="Timeout Error: the cmd 30s have not finished."):
def decorated(func):
result = ""
def _handle_timeout(signum, frame):
global result
result = error_message
raise TypeError(error_message)
def wrapper(*args, **kwargs):
global result
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return result
return functools.wraps(func)(wrapper)
return decorated
@timeout(3*60, '超时退出')
def capture(url, save_fn="capture.png"):
...
-
不通国家打开内容不一样——代理
-
Firefox
可以设置http代理、ssl代理和socks代理
profile = webdriver.FirefoxProfile()
profile.set_preference('network.proxy.type', 1)
# profile.set_preference("network.proxy.http", "127.0.0.1")
# profile.set_preference("network.proxy.http_port", 1080)
# profile.set_preference("network.proxy.ssl", "127.0.0.1")
# profile.set_preference("network.proxy.ssl_port", 1080)
profile.set_preference('network.proxy.socks', "127.0.0.1")
profile.set_preference('network.proxy.socks_port', 1080)
profile.update_preferences()
browser = webdriver.Firefox(firefox_profile=profile)
- Chrome
proxy = '127.0.0.1:1080'
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--proxy-server=socks5://%s' % proxy)
browser = webdriver.Chrome(executable_path='/usr/lib/chromium-browser/chromedriver', chrome_options=chrome_options)
- 网页跳转
- find element , by_id, by_div, by_class, by_xpath
- click, 点击元素跳转网页
- switch handler, browser切换到新打开的网页。
from selenium.webdriver.common.by import By
browser.find_elements(By.XPATH, xpath)[0].click()
time.sleep(10)
for handle in browser.window_handles:
browser.switch_to.window(handle)
browser.save_screenshot(save_fn)
browser.close()
- 产生大量缓存文件
selenium执行时会在/tmp文件夹产生大量的浏览器临时文件,关闭程序后并没有自动清楚。
解决办法:
每次运行前自动清理/tmp文件夹, 递归删除
import os
def delete_file_folder(src):
'''delete files and folders'''
if os.path.isfile(src):
try:
os.remove(src)
except:
pass
elif os.path.isdir(src):
for item in os.listdir(src):
itemsrc=os.path.join(src,item)
delete_file_folder(itemsrc)
try:
os.rmdir(src)
except:
pass
delete_file_folder('/tmp')