python-3.x - WebKit2 和 DomDocument/JavaScriptCore (Python3)
问题描述
我正在将 Python3 应用程序转换为使用 WebKit2 而不是 WebKit(在 Debian Buster 中不再可用)。
在应用程序中,用户可以(取消)选择我从 Python3 应用程序中读取的复选框。在原始代码中,我可以简单地获取 Webview 的 DomDocument 并遍历子对象以返回具有给定名称的对象的值(下面的示例代码)。
在 WebKit2 中,get_dom_document函数不再可用,WebKit2 文档也不清楚如何进行。
有谁知道如何从 WebKit2 Webview 或其他方式获取 DomDocument 按名称遍历所有对象并获取它们的值?
来自 WebKit Webview 的示例代码 DomDocument:
def get_element_values(self, element_name):
values = []
doc = self.get_dom_document()
elements = doc.get_elements_by_name(element_name)
for i in range(elements.get_length()):
child = elements.item(i)
value = child.get_value().strip()
if not child.get_checked():
value = ''
if value:
values.append(value)
return values
注意:“self”是 WebKit.Webview。
[编辑]
我一直在试验这个。首先,我尝试通过首先获取 WebPage 对象来获取 DomDocument。我找到了 WebView.get_page_id() 和 WebKit2WebExtension.get_page(page_id) 但不幸的是,这给了我这个错误:AttributeError:'gi.repository.WebKit2WebExtension'对象没有属性'get_page'。
之后我尝试了 JavaScriptCore 并使用 WebView.run_javascript() 和 WebView.run_javascript_finish() 插入 JavaScript。我得到了一个 JavaScriptResult 对象,但是当我尝试使用 .get_value() 获取值时,我收到了这个错误:TypeError:找不到'JavaScriptCore.Value'的外部结构转换器。
这里有一个用户有同样的问题,但建议的解决方案对我来说也太复杂了。
这是我用于测试的代码(检查 get_element_values() 和 javascript_finished() 函数):
#! /usr/bin/env python3
# WebKit2 reference: https://webkitgtk.org/reference/webkit2gtk/stable
# Code examples: https://github.com/sidus-dev/poseidon/blob/master/poseidon.py
import gi
gi.require_version('WebKit2', '4.0')
gi.require_version('WebKit2WebExtension', '4.0')
from gi.repository import WebKit2, WebKit2WebExtension, Gtk
from os.path import exists
import webbrowser
import re
class SimpleBrowser(WebKit2.WebView):
def __init__(self):
WebKit2.WebView.__init__(self)
# Get version
self.webkit_ver = WebKit2.get_major_version(), WebKit2.get_minor_version(), WebKit2.get_micro_version()
print(("WebKit2 Version: {0}".format('.'.join(map(str, self.webkit_ver)))))
# Signals
self.connect('decide-policy', self.on_decide_policy)
#self.connect("load_changed", self.on_load_changed)
self.connect("load-failed", self.on_load_failed)
self.connect('button-press-event', lambda w, e: e.button == 3)
# Settings
s = self.get_settings()
s.set_property('allow_file_access_from_file_urls', True)
s.set_property('enable-spatial-navigation', False)
s.set_property('enable_javascript', True)
def show_html(self, html_or_url):
if exists(html_or_url):
matchObj = re.search('^file:\/\/', html_or_url)
if not matchObj:
html_or_url = "file://{0}".format(html_or_url)
matchObj = re.search('^[a-z]+:\/\/', html_or_url)
if matchObj:
self.load_uri(html_or_url)
else:
self.load_html(html_or_url)
self.show()
def get_element_values(self, object, element_name):
# JavaScript > dead end :(
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-run-javascript
js = 'var e = document.getElementsByName("' + element_name + '"); var r = []; var c = 0; ' \
'for (var i = 0; i < e.length; i++) { if (e[i].checked) { r[c] = e[i].value; c++;} }'
self.run_javascript(js, None, self.javascript_finished, None);
return
# DOM > dead end :(
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebPage.html
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-get-page-id
page_id = self.get_page_id()
print((page_id))
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebExtension.html#webkit-web-extension-get-page
# AttributeError: 'gi.repository.WebKit2WebExtension' object has no attribute 'get_page'
web_page = WebKit2WebExtension.get_page(page_id)
print((web_page))
def javascript_finished(self, webview, result, user_data):
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-run-javascript-finish
js_result = self.run_javascript_finish(result)
print((">>> js_result = %s" % str(js_result)))
# TypeError: Couldn't find foreign struct converter for 'JavaScriptCore.Value'
value = js_result.get_value()
print((">>> value = %s" % str(value)))
def on_decide_policy(self, webview, decision, decision_type):
# User clicked on a <a href link: open uri in new tab or new default webview
if (decision_type == WebKit2.PolicyDecisionType.NAVIGATION_ACTION):
action = decision.get_navigation_action()
action_type = action.get_navigation_type()
if action_type == WebKit2.NavigationType.LINK_CLICKED:
decision.ignore()
uri = action.get_request().get_uri()
# Open link in default browser
webbrowser.open_new_tab(uri)
else:
if decision is not None:
decision.use()
def on_load_changed(webview, event):
# TODO: get html of loaded page
if event == WebKit2.LoadEvent.FINISHED:
resource = webview.get_main_resource()
resource.get_data()
html = resource.get_data_finish(None)
print(html)
def on_load_failed(webview, event, url, error):
print("Error loading", url, "-", error)
html = '<html><body style="background-color:#E6E6E6;"><h1>WebKit2 Test</h1>' \
'<p><a href="https://solydxk.com">link</a></p><form onsubmit="return false;">' \
'<input type="checkbox" name="chktst" value="checkbox1" checked /> CheckBox 1<br />' \
'<input type="checkbox" name="chktst" value="checkbox2" /> CheckBox 2<br />' \
'<button onclick="alert(show_values());">JS Show Values</button>' \
'</form>' \
'<script>' \
'function show_values() {' \
'var e = document.getElementsByName("chktst"); var r = []; var c = 0;' \
'for (var i = 0; i < e.length; i++) {' \
' if (e[i].checked) { r[c] = e[i].value; c++;}' \
'}return r;}' \
'</script>' \
'</body></html>'
win = Gtk.Window()
win.connect("delete-event", Gtk.main_quit)
win.set_default_size(600,400)
webview = SimpleBrowser()
webview.show_html(html)
box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
button = Gtk.Button(label="Python Show Values")
button.connect("clicked", webview.get_element_values, 'chktst')
win.add(box)
box.pack_start(webview, True, True, 0)
box.pack_start(button, False, False, 1)
win.show_all()
Gtk.main()
[编辑2]
在 Debian Stretch 中,您需要从 backports 安装软件包:
sudo apt-get install -t stretch-backports gir1.2-javascriptcoregtk-4.0 gir1.2-webkit2-4.0 libjavascriptcoregtk-4.0-18 libwebkit2gtk-4.0-37 libwebkit2gtk-4.0-37-gtk2
现在,WebKit2.JavascriptResult.get_js_value() 将返回一个 JavaScriptCore.Value 对象。如果我找到答案,我将进一步测试并回发答案。
解决方案
我发现如果我要返回getElementsByClassName
对象本身,则会引发以下错误:
GLib.Error: WebKitJavascriptError: (699)
这只告诉我它失败了。
但是,我可以让 JavaScript 函数返回带有值的字符串或数组,并将其转换为 Python 中的字符串。
不理想,但它对于我需要它做的事情是可行的。
这是示例代码(查看评论以获取更多信息):
#! /usr/bin/env python3
# WebKit2 reference: https://webkitgtk.org/reference/webkit2gtk/stable
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0')
from gi.repository import Gtk
from gi.repository import WebKit2
from gi.repository import GObject
from os.path import exists
import webbrowser
import re
import sys
class SimpleBrowser(WebKit2.WebView):
# Create custom signals
__gsignals__ = {
"js-finished" : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()),
"html-response-finished" : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ())
}
def __init__(self):
WebKit2.WebView.__init__(self)
# Get version
webkit_ver = WebKit2.get_major_version(), WebKit2.get_minor_version(), WebKit2.get_micro_version()
print(("WebKit2 Version: {0}".format('.'.join(map(str, webkit_ver)))))
if webkit_ver[0] < 2 or \
webkit_ver[1] < 22:
print(("ERROR: upgrade WebKit2 to version 2.22.x or higher"))
sys.exit()
# Store JS output
self.js_value = None
# Store html response
self.html_response = None
# WebKit2 Signals
self.connect('decide-policy', self.on_decide_policy)
self.connect("load_changed", self.on_load_changed)
self.connect("load-failed", self.on_load_failed)
self.connect('button-press-event', lambda w, e: e.button == 3)
# Settings
s = self.get_settings()
s.set_property('allow_file_access_from_file_urls', True)
s.set_property('enable-spatial-navigation', False)
s.set_property('enable_javascript', True)
def show_html(self, html_or_url):
if exists(html_or_url):
matchObj = re.search('^file:\/\/', html_or_url)
if not matchObj:
html_or_url = "file://{0}".format(html_or_url)
matchObj = re.search('^[a-z]+:\/\/', html_or_url)
if matchObj:
self.load_uri(html_or_url)
else:
self.load_html(html_or_url)
self.show()
def js_run(self, function_name, js_return=True):
# JavaScript
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-run-javascript
run_js_finish = self._js_finish if js_return else None
self.run_javascript(function_name, None, run_js_finish, None);
def _js_finish(self, webview, result, user_data=None):
# https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-run-javascript-finish
js_result = self.run_javascript_finish(result)
if js_result is not None:
# https://webkitgtk.org/reference/jsc-glib/stable/JSCValue.html
# TypeError: Couldn't find foreign struct converter for 'JavaScriptCore.Value':
# Make sure you have WebKit2 2.22.x or higher installed.
# For Debian Stretch you need the backports packages:
# apt install -t stretch-backports gir1.2-javascriptcoregtk-4.0 gir1.2-webkit2-4.0 libjavascriptcoregtk-4.0-18 libwebkit2gtk-4.0-37 libwebkit2gtk-4.0-37-gtk2
# Couldn't handle anything but string :(
# If returning the getElementsByClassName object itself: GLib.Error: WebKitJavascriptError: (699)
value = js_result.get_js_value()
self.js_value = value.to_string()
#print((self.js_value))
self.emit('js-finished')
def on_decide_policy(self, webview, decision, decision_type):
# User clicked on a <a href link: open uri in new tab or new default webview
if (decision_type == WebKit2.PolicyDecisionType.NAVIGATION_ACTION):
action = decision.get_navigation_action()
action_type = action.get_navigation_type()
if action_type == WebKit2.NavigationType.LINK_CLICKED:
decision.ignore()
uri = action.get_request().get_uri()
# Open link in default browser
webbrowser.open_new_tab(uri)
else:
if decision is not None:
decision.use()
def on_load_changed(self, webview, event):
# Get html of loaded page
if event == WebKit2.LoadEvent.FINISHED:
resource = webview.get_main_resource()
resource.get_data(None, self._get_response_data_finish, None)
def _get_response_data_finish(self, resource, result, user_data=None):
self.html_response = resource.get_data_finish(result)
self.emit('html-response-finished')
def on_load_failed(webview, event, url, error):
print("Error loading", url, "-", error)
html = '<html>' \
'<script>' \
'function get_checked_values(class_name) {' \
'var e = document.getElementsByClassName(class_name); var r = []; var c = 0;' \
'if (e.length == 0) { e = document.getElementsById(class_name); }' \
'for (var i = 0; i < e.length; i++) {' \
' if (e[i].checked) { r[c] = e[i].value; c++;}' \
'}return r;}' \
'</script>' \
'</head><body style="background-color:#E6E6E6;"><h1>WebKit2 JavaScript Test</h1>' \
'<p><a href="https://solydxk.com">link</a></p><form onsubmit="return false;">' \
'<input type="checkbox" class="chktst" value="checkbox1" checked /> CheckBox 1<br />' \
'<input type="checkbox" class="chktst" value="checkbox2" /> CheckBox 2<br />' \
'</form></body></html>'
# Create test window
win = Gtk.Window()
webview = SimpleBrowser()
win.set_default_size(600,400)
button = Gtk.Button(label="Python Show Checked Values")
box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
box.pack_start(webview, True, True, 0)
box.pack_start(button, False, False, 1)
win.add(box)
# Functions
def get_element_values(self, element_name):
webview.js_run('get_checked_values("{0}")'.format(element_name))
def show_js_value(object):
print((webview.js_value))
def show_html_response(object):
print((webview.html_response))
# Signals
win.connect("delete-event", Gtk.main_quit)
button.connect("clicked", get_element_values, 'chktst')
webview.connect("js-finished", show_js_value)
#webview.connect("html-response-finished", show_html_response)
# Load html and show window
webview.show_html(html)
win.show_all()
Gtk.main()
推荐阅读
- angular - 如何在 mat-sidenav Angular 7 中引用正确的 routerlink?
- html - 我的 :hover 在 CSS 中只会影响我的 div/image
- linux - 如何使用“find”和“grep”来获取文件大小?
- html - CSS 过渡时间不适用于所有元素,li 和锚标签中的单词不会随着时间而缓和
- node.js - 带有快速请求的空白 PDF
- scala - 如何比较 3 个选项层次结构值并以 scala 方式返回布尔值?
- python - 如何在熊猫的多列中获得对行求和的百分比?
- javascript - 使用 JavaScript 计算小时数
- python - python:CSV文件中的列表元素
- scheme - 需要一个关于要求以特定方式在列表中添加数字的家庭作业问题的提示