CEF Python是一个开源项目,旨在为Chromium Embedded Framework提供Python绑定,许多流行的GUI工具包都提供了嵌入CEF浏览器,例如QT。
安装
pip install cefpython3==66.1
支持的Python版本:
实现打开网页
from cefpython3 import cefpython as cef
import platform
import sysdef main():check_versions()sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on errorcef.Initialize()cef.CreateBrowserSync(url="https://www.google.com/",window_title="Hello World!")cef.MessageLoop()cef.Shutdown()def check_versions():ver = cef.GetVersion()print("[hello_world.py] CEF Python {ver}".format(ver=ver["version"]))print("[hello_world.py] Chromium {ver}".format(ver=ver["chrome_version"]))print("[hello_world.py] CEF {ver}".format(ver=ver["cef_version"]))print("[hello_world.py] Python {ver} {arch}".format(ver=platform.python_version(),arch=platform.architecture()[0]))assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this"if __name__ == '__main__':main()
绑定js
"""
Communicate between Python and Javascript asynchronously using
inter-process messaging with the use of Javascript Bindings.
"""from cefpython3 import cefpython as cefg_htmlcode = """
<!doctype html>
<html>
<head><style>body, html {font-family: Arial;font-size: 11pt;}</style><script>function print(msg) {document.getElementById("console").innerHTML += msg+"<br>";}function js_function(value) {print("Value sent from Python: <b>"+value+"</b>");py_function("I am a Javascript string #1", js_callback);}function js_callback(value, py_callback) {print("Value sent from Python: <b>"+value+"</b>");py_callback("I am a Javascript string #2");}</script>
</head>
<body><h1>Javascript Bindings</h1><div id=console></div>
</body>
</html>
"""def main():cef.Initialize()browser = cef.CreateBrowserSync(url=cef.GetDataUrl(g_htmlcode),window_title="Javascript Bindings")browser.SetClientHandler(LoadHandler())bindings = cef.JavascriptBindings()bindings.SetFunction("py_function", py_function)bindings.SetFunction("py_callback", py_callback)browser.SetJavascriptBindings(bindings)cef.MessageLoop()del browsercef.Shutdown()def py_function(value, js_callback):print("Value sent from Javascript: "+value)js_callback.Call("I am a Python string #2", py_callback)def py_callback(value):print("Value sent from Javascript: "+value)class LoadHandler(object):def OnLoadEnd(self, browser, **_):browser.ExecuteFunction("js_function", "I am a Python string #1")if __name__ == '__main__':main()
键盘处理
from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="https://www.google.com/",window_title="Keyboard Handler")browser.SetClientHandler(KeyboardHandler())cef.MessageLoop()del browsercef.Shutdown()class KeyboardHandler(object):def OnKeyEvent(self, browser, event, event_handle, **_):print("OnKeyEvent: "+str(event))if __name__ == '__main__':main()
鼠标点击
# Perform mouse clicks and mouse movements programmatically.from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="data:text/html,<h1>Mouse clicks snippet</h1>""This text will be selected after one second.<br>""This text will be selected after two seconds.",window_title="Mouse clicks")browser.SetClientHandler(LifespanHandler())cef.MessageLoop()del browsercef.Shutdown()def click_after_1_second(browser):print("Click after 1 second")# Mouse move to the top-left corner of the textbrowser.SendMouseMoveEvent(0, 70, False, 0)# Left mouse button click in the top-left corner of the textbrowser.SendMouseClickEvent(0, 70, cef.MOUSEBUTTON_LEFT, False, 1)# Mouse move to the bottom-right corner of the text,# while holding left mouse button.browser.SendMouseMoveEvent(400, 80, False, cef.EVENTFLAG_LEFT_MOUSE_BUTTON)# Release left mouse buttonbrowser.SendMouseClickEvent(400, 80, cef.MOUSEBUTTON_LEFT, True, 1)cef.PostDelayedTask(cef.TID_UI, 1000, click_after_2_seconds, browser)def click_after_2_seconds(browser):print("Click after 2 seconds")browser.SendMouseMoveEvent(0, 90, False, 0)browser.SendMouseClickEvent(0, 90, cef.MOUSEBUTTON_LEFT, False, 1)browser.SendMouseMoveEvent(400, 99, False, cef.EVENTFLAG_LEFT_MOUSE_BUTTON)browser.SendMouseClickEvent(400, 99, cef.MOUSEBUTTON_LEFT, True, 1)cef.PostDelayedTask(cef.TID_UI, 1000, click_after_1_second, browser)class LifespanHandler(object):def OnLoadEnd(self, browser, **_):# Execute function with a delay of 1 second after page# has completed loading.print("Page loading is complete")cef.PostDelayedTask(cef.TID_UI, 1000, click_after_1_second, browser)if __name__ == '__main__':main()
网络Cookie
"""
Implement RequestHandler.CanGetCookies and CanSetCookie
to block or allow cookies over network requests.
"""from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="http://www.html-kit.com/tools/cookietester/",window_title="Network cookies")browser.SetClientHandler(RequestHandler())cef.MessageLoop()del browsercef.Shutdown()class RequestHandler(object):def __init__(self):self.getcount = 0self.setcount = 0def CanGetCookies(self, frame, request, **_):# There are multiple iframes on that website, let's log# cookies only for the main frame.if frame.IsMain():self.getcount += 1print("-- CanGetCookies #"+str(self.getcount))print("url="+request.GetUrl()[0:80])print("")# Return True to allow reading cookies or False to blockreturn Truedef CanSetCookie(self, frame, request, cookie, **_):# There are multiple iframes on that website, let's log# cookies only for the main frame.if frame.IsMain():self.setcount += 1print("-- CanSetCookie @"+str(self.setcount))print("url="+request.GetUrl()[0:80])print("Name="+cookie.GetName())print("Value="+cookie.GetValue())print("")# Return True to allow setting cookie or False to blockreturn Trueif __name__ == '__main__':main()
关闭前
"""
Implement LifespanHandler.OnBeforeClose to execute custom
code before browser window closes.
"""from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="https://www.google.com/",window_title="OnBeforeClose")browser.SetClientHandler(LifespanHandler())cef.MessageLoop()del browsercef.Shutdown()class LifespanHandler(object):def OnBeforeClose(self, browser):print("Browser ID: {}".format(browser.GetIdentifier()))print("Browser will close and app will exit")if __name__ == '__main__':main()
DOM 就绪
"""
Execute custom Python code on a web page as soon as DOM is ready.
Implements a custom "_OnDomReady" event in the LoadHandler object.
"""from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="https://www.google.com/",window_title="_OnDomReady event")load_handler = LoadHandler(browser)browser.SetClientHandler(load_handler)bindings = cef.JavascriptBindings()bindings.SetFunction("LoadHandler_OnDomReady",load_handler["_OnDomReady"])browser.SetJavascriptBindings(bindings)cef.MessageLoop()del load_handlerdel browsercef.Shutdown()class LoadHandler(object):def __init__(self, browser):self.browser = browserdef __getitem__(self, key):return getattr(self, key)def OnLoadStart(self, browser, **_):browser.ExecuteJavascript("""if (document.readyState === "complete") {LoadHandler_OnDomReady();} else {document.addEventListener("DOMContentLoaded", function() {LoadHandler_OnDomReady();});}""")def _OnDomReady(self):print("DOM is ready!")self.browser.ExecuteFunction("alert","Message from Python: DOM is ready!")if __name__ == '__main__':main()
页面加载完毕
"""
Execute custom Python code on a web page when page loading is complete.
Implements a custom "_OnPageComplete" event in the LoadHandler object.
"""from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="https://www.google.com/",window_title="_OnPageComplete event")browser.SetClientHandler(LoadHandler())cef.MessageLoop()del browsercef.Shutdown()class LoadHandler(object):def OnLoadingStateChange(self, browser, is_loading, **_):"""For detecting if page loading has ended it is recommendedto use OnLoadingStateChange which is most reliable. The OnLoadEndcallback also available in LoadHandler can sometimes fail insome cases e.g. when image loading hangs."""if not is_loading:self._OnPageComplete(browser)def _OnPageComplete(self, browser):print("Page loading is complete!")browser.ExecuteFunction("alert", "Message from Python: Page loading"" is complete!")if __name__ == '__main__':main()
设置Cookie
"""
Shows how to set a cookie.
"""from cefpython3 import cefpython as cef
import datetimedef main():cef.Initialize()cef.CreateBrowserSync(url="http://www.html-kit.com/tools/cookietester/",window_title="Set a cookie")manager = cef.CookieManager.GetGlobalManager()cookie = cef.Cookie()cookie.Set({"name": "my_cookie","value": "my_value",# Make sure domain is a valid value otherwise it crashes# app (Issue #459)"domain": "www.html-kit.com","path": "/","secure": False,"httpOnly": False,"creation": datetime.datetime(2018, 8, 22),"lastAccess": datetime.datetime(2018, 8, 22),"hasExpires": True,"expires": datetime.datetime(2028, 12, 31, 23, 59, 59),})manager.SetCookie("http://www.html-kit.com/", cookie)cef.MessageLoop()cef.Shutdown()if __name__ == '__main__':main()
窗口大小
"""
Set initial window size to 900/640px without use of
any third party GUI framework. On Linux/Mac you can set
window size by calling WindowInfo.SetAsChild. On Windows
you can accomplish this by calling Windows native functions
using the ctypes module.
"""from cefpython3 import cefpython as cef
import ctypes
import platformdef main():cef.Initialize()window_info = cef.WindowInfo()parent_handle = 0# This call has effect only on Mac and Linux.# All rect coordinates are applied including X and Y parameters.window_info.SetAsChild(parent_handle, [0, 0, 900, 640])browser = cef.CreateBrowserSync(url="https://www.google.com/",window_info=window_info,window_title="Window size")if platform.system() == "Windows":window_handle = browser.GetOuterWindowHandle()insert_after_handle = 0# X and Y parameters are ignored by setting the SWP_NOMOVE flagSWP_NOMOVE = 0x0002# noinspection PyUnresolvedReferencesctypes.windll.user32.SetWindowPos(window_handle, insert_after_handle,0, 0, 900, 640, SWP_NOMOVE)cef.MessageLoop()del browsercef.Shutdown()if __name__ == '__main__':main()
嵌入 QT
# Example of embedding CEF browser using PyQt4, PyQt5 and
# PySide libraries. This example has two widgets: a navigation
# bar and a browser.
#
# Tested configurations:
# - PyQt 5.8.2 (qt 5.8.0) on Windows/Linux/Mac
# - PyQt 4.10.4 / 4.11.4 (qt 4.8.6 / 4.8.7) on Windows/Linux
# - PySide 1.2.1 (qt 4.8.6) on Windows/Linux/Mac
# - PySide2 5.6.0, 5.11.2 (qt 5.6.2, 5.11.2) on Windows/Linux/Mac
# - CEF Python v55.4+
#
# Issues with PySide 1.2:
# - Mac: Keyboard focus issues when switching between controls (Issue #284)
# - Mac: Mouse cursor never changes when hovering over links (Issue #311)from cefpython3 import cefpython as cef
import ctypes
import os
import platform
import sys# GLOBALS
PYQT4 = False
PYQT5 = False
PYSIDE = False
PYSIDE2 = Falseif "pyqt4" in sys.argv:PYQT4 = True# noinspection PyUnresolvedReferencesfrom PyQt4.QtGui import *# noinspection PyUnresolvedReferencesfrom PyQt4.QtCore import *
elif "pyqt5" in sys.argv:PYQT5 = True# noinspection PyUnresolvedReferencesfrom PyQt5.QtGui import *# noinspection PyUnresolvedReferencesfrom PyQt5.QtCore import *# noinspection PyUnresolvedReferencesfrom PyQt5.QtWidgets import *
elif "pyside" in sys.argv:PYSIDE = True# noinspection PyUnresolvedReferencesimport PySide# noinspection PyUnresolvedReferencesfrom PySide import QtCore# noinspection PyUnresolvedReferencesfrom PySide.QtGui import *# noinspection PyUnresolvedReferencesfrom PySide.QtCore import *
elif "pyside2" in sys.argv:PYSIDE2 = True# noinspection PyUnresolvedReferencesimport PySide2# noinspection PyUnresolvedReferencesfrom PySide2 import QtCore# noinspection PyUnresolvedReferencesfrom PySide2.QtGui import *# noinspection PyUnresolvedReferencesfrom PySide2.QtCore import *# noinspection PyUnresolvedReferencesfrom PySide2.QtWidgets import *
else:print("USAGE:")print(" qt.py pyqt4")print(" qt.py pyqt5")print(" qt.py pyside")print(" qt.py pyside2")sys.exit(1)# Fix for PyCharm hints warnings when using static methods
WindowUtils = cef.WindowUtils()# Platforms
WINDOWS = (platform.system() == "Windows")
LINUX = (platform.system() == "Linux")
MAC = (platform.system() == "Darwin")# Configuration
WIDTH = 800
HEIGHT = 600# OS differences
CefWidgetParent = QWidget
if LINUX and (PYQT4 or PYSIDE):# noinspection PyUnresolvedReferencesCefWidgetParent = QX11EmbedContainerdef main():check_versions()sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on errorsettings = {}if MAC:# Issue #442 requires enabling message pump on Mac# in Qt example. Calling cef.DoMessageLoopWork in a timer# doesn't work anymore.settings["external_message_pump"] = Truecef.Initialize(settings)app = CefApplication(sys.argv)main_window = MainWindow()main_window.show()main_window.activateWindow()main_window.raise_()app.exec_()if not cef.GetAppSetting("external_message_pump"):app.stopTimer()del main_window # Just to be safe, similarly to "del app"del app # Must destroy app object before calling Shutdowncef.Shutdown()def check_versions():print("[qt.py] CEF Python {ver}".format(ver=cef.__version__))print("[qt.py] Python {ver} {arch}".format(ver=platform.python_version(), arch=platform.architecture()[0]))if PYQT4 or PYQT5:print("[qt.py] PyQt {v1} (qt {v2})".format(v1=PYQT_VERSION_STR, v2=qVersion()))elif PYSIDE:print("[qt.py] PySide {v1} (qt {v2})".format(v1=PySide.__version__, v2=QtCore.__version__))elif PYSIDE2:print("[qt.py] PySide2 {v1} (qt {v2})".format(v1=PySide2.__version__, v2=QtCore.__version__))# CEF Python version requirementassert cef.__version__ >= "55.4", "CEF Python v55.4+ required to run this"class MainWindow(QMainWindow):def __init__(self):# noinspection PyArgumentListsuper(MainWindow, self).__init__(None)# Avoids crash when shutting down CEF (issue #360)if PYSIDE:self.setAttribute(Qt.WA_DeleteOnClose, True)self.cef_widget = Noneself.navigation_bar = Noneif PYQT4:self.setWindowTitle("PyQt4 example")elif PYQT5:self.setWindowTitle("PyQt5 example")elif PYSIDE:self.setWindowTitle("PySide example")elif PYSIDE2:self.setWindowTitle("PySide2 example")self.setFocusPolicy(Qt.StrongFocus)self.setupLayout()def setupLayout(self):self.resize(WIDTH, HEIGHT)self.cef_widget = CefWidget(self)self.navigation_bar = NavigationBar(self.cef_widget)layout = QGridLayout()# noinspection PyArgumentListlayout.addWidget(self.navigation_bar, 0, 0)# noinspection PyArgumentListlayout.addWidget(self.cef_widget, 1, 0)layout.setContentsMargins(0, 0, 0, 0)layout.setSpacing(0)layout.setRowStretch(0, 0)layout.setRowStretch(1, 1)# noinspection PyArgumentListframe = QFrame()frame.setLayout(layout)self.setCentralWidget(frame)if (PYSIDE2 or PYQT5) and WINDOWS:# On Windows with PyQt5 main window must be shown first# before CEF browser is embedded, otherwise window is# not resized and application hangs during resize.self.show()# Browser can be embedded only after layout was set upself.cef_widget.embedBrowser()if (PYSIDE2 or PYQT5) and LINUX:# On Linux with PyQt5 the QX11EmbedContainer widget is# no more available. An equivalent in Qt5 is to create# a hidden window, embed CEF browser in it and then# create a container for that hidden window and replace# cef widget in the layout with the container.# noinspection PyUnresolvedReferences, PyArgumentListself.container = QWidget.createWindowContainer(self.cef_widget.hidden_window, parent=self)# noinspection PyArgumentListlayout.addWidget(self.container, 1, 0)def closeEvent(self, event):# Close browser (force=True) and free CEF referenceif self.cef_widget.browser:self.cef_widget.browser.CloseBrowser(True)self.clear_browser_references()def clear_browser_references(self):# Clear browser references that you keep anywhere in your# code. All references must be cleared for CEF to shutdown cleanly.self.cef_widget.browser = Noneclass CefWidget(CefWidgetParent):def __init__(self, parent=None):# noinspection PyArgumentListsuper(CefWidget, self).__init__(parent)self.parent = parentself.browser = Noneself.hidden_window = None # Required for PyQt5 on Linuxself.show()def focusInEvent(self, event):# This event seems to never get called on Linux, as CEF is# stealing all focus due to Issue #284.if cef.GetAppSetting("debug"):print("[qt.py] CefWidget.focusInEvent")if self.browser:if WINDOWS:WindowUtils.OnSetFocus(self.getHandle(), 0, 0, 0)self.browser.SetFocus(True)def focusOutEvent(self, event):# This event seems to never get called on Linux, as CEF is# stealing all focus due to Issue #284.if cef.GetAppSetting("debug"):print("[qt.py] CefWidget.focusOutEvent")if self.browser:self.browser.SetFocus(False)def embedBrowser(self):if (PYSIDE2 or PYQT5) and LINUX:# noinspection PyUnresolvedReferencesself.hidden_window = QWindow()window_info = cef.WindowInfo()rect = [0, 0, self.width(), self.height()]window_info.SetAsChild(self.getHandle(), rect)self.browser = cef.CreateBrowserSync(window_info,url="https://www.google.com/")self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar))self.browser.SetClientHandler(FocusHandler(self))def getHandle(self):if self.hidden_window:# PyQt5 on Linuxreturn int(self.hidden_window.winId())try:# PyQt4 and PyQt5return int(self.winId())except:# PySide:# | QWidget.winId() returns <PyCObject object at 0x02FD8788># | Converting it to int using ctypes.if sys.version_info[0] == 2:# Python 2ctypes.pythonapi.PyCObject_AsVoidPtr.restype = (ctypes.c_void_p)ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = ([ctypes.py_object])return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId())else:# Python 3ctypes.pythonapi.PyCapsule_GetPointer.restype = (ctypes.c_void_p)ctypes.pythonapi.PyCapsule_GetPointer.argtypes = ([ctypes.py_object])return ctypes.pythonapi.PyCapsule_GetPointer(self.winId(), None)def moveEvent(self, _):self.x = 0self.y = 0if self.browser:if WINDOWS:WindowUtils.OnSize(self.getHandle(), 0, 0, 0)elif LINUX:self.browser.SetBounds(self.x, self.y,self.width(), self.height())self.browser.NotifyMoveOrResizeStarted()def resizeEvent(self, event):size = event.size()if self.browser:if WINDOWS:WindowUtils.OnSize(self.getHandle(), 0, 0, 0)elif LINUX:self.browser.SetBounds(self.x, self.y,size.width(), size.height())self.browser.NotifyMoveOrResizeStarted()class CefApplication(QApplication):def __init__(self, args):super(CefApplication, self).__init__(args)if not cef.GetAppSetting("external_message_pump"):self.timer = self.createTimer()self.setupIcon()def createTimer(self):timer = QTimer()# noinspection PyUnresolvedReferencestimer.timeout.connect(self.onTimer)timer.start(10)return timerdef onTimer(self):cef.MessageLoopWork()def stopTimer(self):# Stop the timer after Qt's message loop has endedself.timer.stop()def setupIcon(self):icon_file = os.path.join(os.path.abspath(os.path.dirname(__file__)),"resources", "{0}.png".format(sys.argv[1]))if os.path.exists(icon_file):self.setWindowIcon(QIcon(icon_file))class LoadHandler(object):def __init__(self, navigation_bar):self.initial_app_loading = Trueself.navigation_bar = navigation_bardef OnLoadingStateChange(self, **_):self.navigation_bar.updateState()def OnLoadStart(self, browser, **_):self.navigation_bar.url.setText(browser.GetUrl())if self.initial_app_loading:self.navigation_bar.cef_widget.setFocus()# Temporary fix no. 2 for focus issue on Linux (Issue #284)if LINUX:print("[qt.py] LoadHandler.OnLoadStart:"" keyboard focus fix no. 2 (Issue #284)")browser.SetFocus(True)self.initial_app_loading = Falseclass FocusHandler(object):def __init__(self, cef_widget):self.cef_widget = cef_widgetdef OnTakeFocus(self, **_):if cef.GetAppSetting("debug"):print("[qt.py] FocusHandler.OnTakeFocus")def OnSetFocus(self, **_):if cef.GetAppSetting("debug"):print("[qt.py] FocusHandler.OnSetFocus")def OnGotFocus(self, browser, **_):if cef.GetAppSetting("debug"):print("[qt.py] FocusHandler.OnGotFocus")self.cef_widget.setFocus()# Temporary fix no. 1 for focus issues on Linux (Issue #284)if LINUX:browser.SetFocus(True)class NavigationBar(QFrame):def __init__(self, cef_widget):# noinspection PyArgumentListsuper(NavigationBar, self).__init__()self.cef_widget = cef_widget# Init layoutlayout = QGridLayout()layout.setContentsMargins(0, 0, 0, 0)layout.setSpacing(0)# Back buttonself.back = self.createButton("back")# noinspection PyUnresolvedReferencesself.back.clicked.connect(self.onBack)# noinspection PyArgumentListlayout.addWidget(self.back, 0, 0)# Forward buttonself.forward = self.createButton("forward")# noinspection PyUnresolvedReferencesself.forward.clicked.connect(self.onForward)# noinspection PyArgumentListlayout.addWidget(self.forward, 0, 1)# Reload buttonself.reload = self.createButton("reload")# noinspection PyUnresolvedReferencesself.reload.clicked.connect(self.onReload)# noinspection PyArgumentListlayout.addWidget(self.reload, 0, 2)# Url inputself.url = QLineEdit("")# noinspection PyUnresolvedReferencesself.url.returnPressed.connect(self.onGoUrl)# noinspection PyArgumentListlayout.addWidget(self.url, 0, 3)# Layoutself.setLayout(layout)self.updateState()def onBack(self):if self.cef_widget.browser:self.cef_widget.browser.GoBack()def onForward(self):if self.cef_widget.browser:self.cef_widget.browser.GoForward()def onReload(self):if self.cef_widget.browser:self.cef_widget.browser.Reload()def onGoUrl(self):if self.cef_widget.browser:self.cef_widget.browser.LoadUrl(self.url.text())def updateState(self):browser = self.cef_widget.browserif not browser:self.back.setEnabled(False)self.forward.setEnabled(False)self.reload.setEnabled(False)self.url.setEnabled(False)returnself.back.setEnabled(browser.CanGoBack())self.forward.setEnabled(browser.CanGoForward())self.reload.setEnabled(True)self.url.setEnabled(True)self.url.setText(browser.GetUrl())def createButton(self, name):resources = os.path.join(os.path.abspath(os.path.dirname(__file__)),"resources")pixmap = QPixmap(os.path.join(resources, "{0}.png".format(name)))icon = QIcon(pixmap)button = QPushButton()button.setIcon(icon)button.setIconSize(pixmap.rect().size())return buttonif __name__ == '__main__':main()
截屏
"""
Example of using CEF browser in off-screen rendering mode
(windowless) to create a screenshot of a web page. This
example doesn't depend on any third party GUI framework.
This example is discussed in Tutorial in the Off-screen
rendering section.Before running this script you have to install Pillow image
library (PIL module):pip install PillowWith optionl arguments to this script you can resize viewport
so that screenshot includes whole page with height like 5000px
which would be an equivalent of scrolling down multiple pages.
By default when no arguments are provided will load cefpython
project page on Github with 5000px height.Usage:python screenshot.pypython screenshot.py https://github.com/cztomczak/cefpython 1024 5000python screenshot.py https://www.google.com/ncr 1024 768Tested configurations:
- CEF Python v57.0+
- Pillow 2.3.0 / 4.1.0NOTE: There are limits in Chromium on viewport size. For somewebsites with huge viewport size it won't work. In suchcase it is required to reduce viewport size to an usualsize of a window and perform scrolling programmaticallyusing javascript while making a screenshot for each ofthe scrolled region. Then at the end combine all thescreenshots into one. To force a paint event in OSRmode call cef.Invalidate().
"""from cefpython3 import cefpython as cef
import os
import platform
import subprocess
import systry:from PIL import Image, __version__ as PILLOW_VERSION
except ImportError:print("[screenshot.py] Error: PIL module not available. To install"" type: pip install Pillow")sys.exit(1)# Config
URL = "https://github.com/cztomczak/cefpython"
VIEWPORT_SIZE = (1024, 5000)
SCREENSHOT_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)),"screenshot.png")def main():check_versions()sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on errorif os.path.exists(SCREENSHOT_PATH):print("[screenshot.py] Remove old screenshot")os.remove(SCREENSHOT_PATH)command_line_arguments()# Off-screen-rendering requires setting "windowless_rendering_enabled"# option.settings = {"windowless_rendering_enabled": True,}switches = {# GPU acceleration is not supported in OSR mode, so must disable# it using these Chromium switches (Issue #240 and #463)"disable-gpu": "","disable-gpu-compositing": "",# Tweaking OSR performance by setting the same Chromium flags# as in upstream cefclient (Issue #240)."enable-begin-frame-scheduling": "","disable-surfaces": "", # This is required for PDF ext to work}browser_settings = {# Tweaking OSR performance (Issue #240)"windowless_frame_rate": 30, # Default frame rate in CEF is 30}cef.Initialize(settings=settings, switches=switches)create_browser(browser_settings)cef.MessageLoop()cef.Shutdown()print("[screenshot.py] Opening screenshot with default application")open_with_default_application(SCREENSHOT_PATH)def check_versions():ver = cef.GetVersion()print("[screenshot.py] CEF Python {ver}".format(ver=ver["version"]))print("[screenshot.py] Chromium {ver}".format(ver=ver["chrome_version"]))print("[screenshot.py] CEF {ver}".format(ver=ver["cef_version"]))print("[screenshot.py] Python {ver} {arch}".format(ver=platform.python_version(),arch=platform.architecture()[0]))print("[screenshot.py] Pillow {ver}".format(ver=PILLOW_VERSION))assert cef.__version__ >= "57.0", "CEF Python v57.0+ required to run this"def command_line_arguments():if len(sys.argv) == 4:url = sys.argv[1]width = int(sys.argv[2])height = int(sys.argv[3])if url.startswith("http://") or url.startswith("https://"):global URLURL = urlelse:print("[screenshot.py] Error: Invalid url argument")sys.exit(1)if width > 0 and height > 0:global VIEWPORT_SIZEVIEWPORT_SIZE = (width, height)else:print("[screenshot.py] Error: Invalid width and height")sys.exit(1)elif len(sys.argv) > 1:print("[screenshot.py] Error: Expected arguments: url width height")sys.exit(1)def create_browser(settings):# Create browser in off-screen-rendering mode (windowless mode)# by calling SetAsOffscreen method. In such mode parent window# handle can be NULL (0).parent_window_handle = 0window_info = cef.WindowInfo()window_info.SetAsOffscreen(parent_window_handle)print("[screenshot.py] Viewport size: {size}".format(size=str(VIEWPORT_SIZE)))print("[screenshot.py] Loading url: {url}".format(url=URL))browser = cef.CreateBrowserSync(window_info=window_info,settings=settings,url=URL)browser.SetClientHandler(LoadHandler())browser.SetClientHandler(RenderHandler())browser.SendFocusEvent(True)# You must call WasResized at least once to let know CEF that# viewport size is available and that OnPaint may be called.browser.WasResized()def save_screenshot(browser):# Browser object provides GetUserData/SetUserData methods# for storing custom data associated with browser. The# "OnPaint.buffer_string" data is set in RenderHandler.OnPaint.buffer_string = browser.GetUserData("OnPaint.buffer_string")if not buffer_string:raise Exception("buffer_string is empty, OnPaint never called?")image = Image.frombytes("RGBA", VIEWPORT_SIZE, buffer_string,"raw", "RGBA", 0, 1)image.save(SCREENSHOT_PATH, "PNG")print("[screenshot.py] Saved image: {path}".format(path=SCREENSHOT_PATH))# See comments in exit_app() why PostTask must be usedcef.PostTask(cef.TID_UI, exit_app, browser)def open_with_default_application(path):if sys.platform.startswith("darwin"):subprocess.call(("open", path))elif os.name == "nt":# noinspection PyUnresolvedReferencesos.startfile(path)elif os.name == "posix":subprocess.call(("xdg-open", path))def exit_app(browser):# Important note:# Do not close browser nor exit app from OnLoadingStateChange# OnLoadError or OnPaint events. Closing browser during these# events may result in unexpected behavior. Use cef.PostTask# function to call exit_app from these events.print("[screenshot.py] Close browser and exit app")browser.CloseBrowser()cef.QuitMessageLoop()class LoadHandler(object):def OnLoadingStateChange(self, browser, is_loading, **_):"""Called when the loading state has changed."""if not is_loading:# Loading is completesys.stdout.write(os.linesep)print("[screenshot.py] Web page loading is complete")print("[screenshot.py] Will save screenshot in 2 seconds")# Give up to 2 seconds for the OnPaint call. Most of the time# it is already called, but sometimes it may be called later.cef.PostDelayedTask(cef.TID_UI, 2000, save_screenshot, browser)def OnLoadError(self, browser, frame, error_code, failed_url, **_):"""Called when the resource load for a navigation failsor is canceled."""if not frame.IsMain():# We are interested only in loading main url.# Ignore any errors during loading of other frames.returnprint("[screenshot.py] ERROR: Failed to load url: {url}".format(url=failed_url))print("[screenshot.py] Error code: {code}".format(code=error_code))# See comments in exit_app() why PostTask must be usedcef.PostTask(cef.TID_UI, exit_app, browser)class RenderHandler(object):def __init__(self):self.OnPaint_called = Falsedef GetViewRect(self, rect_out, **_):"""Called to retrieve the view rectangle which is relativeto screen coordinates. Return True if the rectangle wasprovided."""# rect_out --> [x, y, width, height]rect_out.extend([0, 0, VIEWPORT_SIZE[0], VIEWPORT_SIZE[1]])return Truedef OnPaint(self, browser, element_type, paint_buffer, **_):"""Called when an element should be painted."""if self.OnPaint_called:sys.stdout.write(".")sys.stdout.flush()else:sys.stdout.write("[screenshot.py] OnPaint")self.OnPaint_called = Trueif element_type == cef.PET_VIEW:# Buffer string is a huge string, so for performance# reasons it would be better not to copy this string.# I think that Python makes a copy of that string when# passing it to SetUserData.buffer_string = paint_buffer.GetBytes(mode="rgba",origin="top-left")# Browser object provides GetUserData/SetUserData methods# for storing custom data associated with browser.browser.SetUserData("OnPaint.buffer_string", buffer_string)else:raise Exception("Unsupported element_type in OnPaint")if __name__ == '__main__':main()
cookie 操作
"""Shows how to fetch all cookies, all cookies for
a given url and how to delete a specific cookie. For
an example on how to set a cookie see the 'setcookie.py'
snippet."""from cefpython3 import cefpython as cefdef main():cef.Initialize()browser = cef.CreateBrowserSync(url="http://www.html-kit.com/tools/cookietester/",window_title="Cookies")browser.SetClientHandler(LoadHandler())cef.MessageLoop()del browsercef.Shutdown()class LoadHandler(object):def OnLoadingStateChange(self, browser, is_loading, **_):if is_loading:print("Page loading complete - start visiting cookies")manager = cef.CookieManager.GetGlobalManager()# Must keep a strong reference to the CookieVisitor object# while cookies are being visited.self.cookie_visitor = CookieVisitor()# Visit all cookiesresult = manager.VisitAllCookies(self.cookie_visitor)if not result:print("Error: could not access cookies")# To visit cookies only for a given url uncomment the# code below."""url = "http://www.html-kit.com/tools/cookietester/"http_only_cookies = Falseresult = manager.VisitUrlCookies(url, http_only_cookies,self.cookie_visitor)if not result:print("Error: could not access cookies")"""class CookieVisitor(object):def Visit(self, cookie, count, total, delete_cookie_out):"""This callback is called on the IO thread."""print("Cookie {count}/{total}: '{name}', '{value}'".format(count=count+1, total=total, name=cookie.GetName(),value=cookie.GetValue()))# Set a cookie named "delete_me" and it will be deleted.# You have to refresh page to see whether it succeeded.if cookie.GetName() == "delete_me":# 'delete_cookie_out' arg is a list passed by reference.# Set its '0' index to True to delete the cookie.delete_cookie_out[0] = Trueprint("Deleted cookie: {name}".format(name=cookie.GetName()))# Return True to continue visiting more cookiesreturn Trueif __name__ == '__main__':main()
相关链接
https://pypi.org/project/cefpython3/
https://github.com/cztomczak/cefpython