This commit is contained in:
Kovid Goyal 2011-09-22 20:21:36 -06:00
parent c2feceffb0
commit 72a5e1d5c0
3 changed files with 102 additions and 9 deletions

View File

@ -13,7 +13,7 @@ from cookielib import Cookie
from PyQt4.Qt import (QObject, QNetworkAccessManager, QNetworkDiskCache, from PyQt4.Qt import (QObject, QNetworkAccessManager, QNetworkDiskCache,
QNetworkProxy, QNetworkProxyFactory, QEventLoop, QUrl, QNetworkProxy, QNetworkProxyFactory, QEventLoop, QUrl,
QDialog, QVBoxLayout, QSize, QNetworkCookieJar) QDialog, QVBoxLayout, QSize, QNetworkCookieJar)
from PyQt4.QtWebKit import QWebPage, QWebSettings, QWebView from PyQt4.QtWebKit import QWebPage, QWebSettings, QWebView, QWebElement
from calibre import USER_AGENT, prints, get_proxies, get_proxy_info from calibre import USER_AGENT, prints, get_proxies, get_proxy_info
from calibre.constants import ispy3, config_dir from calibre.constants import ispy3, config_dir
@ -296,11 +296,6 @@ class Browser(QObject, FormsMixin):
log.filter_level = log.DEBUG log.filter_level = log.DEBUG
self.log = log self.log = log
self.jquery_lib = P('content_server/jquery.js', data=True,
allow_user_override=False).decode('utf-8')
self.simulate_lib = P('jquery.simulate.js', data=True,
allow_user_override=False).decode('utf-8')
self.page = WebPage(log, confirm_callback=confirm_callback, self.page = WebPage(log, confirm_callback=confirm_callback,
prompt_callback=prompt_callback, user_agent=user_agent, prompt_callback=prompt_callback, user_agent=user_agent,
enable_developer_tools=enable_developer_tools, enable_developer_tools=enable_developer_tools,
@ -333,6 +328,13 @@ class Browser(QObject, FormsMixin):
raise Timeout('Waiting for replies took longer than %d seconds' % raise Timeout('Waiting for replies took longer than %d seconds' %
timeout) timeout)
def run_for_a_time(self, timeout):
final_time = time.time() + timeout
loop = QEventLoop(self)
while (time.time() < final_time):
if not loop.processEvents():
time.sleep(0.1)
def visit(self, url, timeout=30.0): def visit(self, url, timeout=30.0):
''' '''
Open the page specified in URL and wait for it to complete loading. Open the page specified in URL and wait for it to complete loading.
@ -347,9 +349,9 @@ class Browser(QObject, FormsMixin):
self.page.mainFrame().load(QUrl(url)) self.page.mainFrame().load(QUrl(url))
return self._wait_for_load(timeout, url) return self._wait_for_load(timeout, url)
def click(self, qwe, wait_for_load=True, ajax_replies=0, timeout=30.0): def click(self, qwe_or_selector, wait_for_load=True, ajax_replies=0, timeout=30.0):
''' '''
Click the QWebElement pointed to by qwe. Click the :class:`QWebElement` pointed to by qwe_or_selector.
:param wait_for_load: If you know that the click is going to cause a :param wait_for_load: If you know that the click is going to cause a
new page to be loaded, set this to True to have new page to be loaded, set this to True to have
@ -358,6 +360,12 @@ class Browser(QObject, FormsMixin):
that triggers some AJAX interaction that triggers some AJAX interaction
''' '''
initial_count = self.nam.reply_count initial_count = self.nam.reply_count
qwe = qwe_or_selector
if not isinstance(qwe, QWebElement):
qwe = self.page.mainFrame().findFirstElement(qwe)
if qwe.isNull():
raise ValueError('Failed to find element with selector: %r'
% qwe_or_selector)
js = ''' js = '''
var e = document.createEvent('MouseEvents'); var e = document.createEvent('MouseEvents');
e.initEvent( 'click', true, true ); e.initEvent( 'click', true, true );
@ -370,6 +378,24 @@ class Browser(QObject, FormsMixin):
elif wait_for_load and not self._wait_for_load(timeout): elif wait_for_load and not self._wait_for_load(timeout):
raise LoadError('Clicking resulted in a failed load') raise LoadError('Clicking resulted in a failed load')
def click_text_link(self, text_or_regex, selector='a[href]',
wait_for_load=True, ajax_replies=0, timeout=30.0):
target = None
for qwe in self.page.mainFrame().findAllElements(selector):
src = unicode(qwe.toPlainText())
if hasattr(text_or_regex, 'match') and text_or_regex.search(src):
target = qwe
break
if src.lower() == text_or_regex.lower():
target = qwe
break
if target is None:
raise ValueError('No element matching %r with text %s found'%(
selector, text_or_regex))
return self.click(target, wait_for_load=wait_for_load,
ajax_replies=ajax_replies, timeout=timeout)
def show_browser(self): def show_browser(self):
''' '''
Show the currently loaded web page in a window. Useful for debugging. Show the currently loaded web page in a window. Useful for debugging.
@ -377,6 +403,7 @@ class Browser(QObject, FormsMixin):
view = BrowserView(self.page) view = BrowserView(self.page)
view.exec_() view.exec_()
@property
def cookies(self): def cookies(self):
''' '''
Return all the cookies set currently as :class:`Cookie` objects. Return all the cookies set currently as :class:`Cookie` objects.
@ -384,4 +411,14 @@ class Browser(QObject, FormsMixin):
''' '''
return list(self.nam.py_cookies()) return list(self.nam.py_cookies())
@property
def html(self):
return unicode(self.page.mainFrame().toHtml())
def close(self):
try:
self.visit('about:blank', timeout=0.01)
except Timeout:
pass
self.nam = self.page = None

View File

@ -0,0 +1,56 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre import USER_AGENT
from calibre.web.jsbrowser.browser import Browser
def do_login(login_url, calibre_browser, form_selector, controls={},
num_of_replies=0, timeout=60.0, verbosity=0, pause_time=5,
post_visit_callback=None, post_submit_callback=None,
submit_control_selector=None):
ua = USER_AGENT
for key, val in calibre_browser.addheaders:
if key.lower() == 'user-agent':
ua = val
break
br = Browser(user_agent=ua, verbosity=verbosity)
if not br.visit(login_url, timeout=timeout):
raise ValueError('Failed to load the login URL: %r'%login_url)
if callable(post_visit_callback):
post_visit_callback(br)
f = br.select_form(form_selector)
for key, val in controls.iteritems():
f[key] = val
# br.show_browser()
if num_of_replies > 0:
br.ajax_submit(num_of_replies=num_of_replies, timeout=timeout,
submit_control_selector=submit_control_selector)
else:
br.submit(timeout=timeout,
submit_control_selector=submit_control_selector)
# Give any javascript some time to run
br.run_for_a_time(pause_time)
if callable(post_submit_callback):
post_submit_callback(br)
br.show_browser()
cj = calibre_browser.cookiejar
for cookie in br.cookies:
cj.set_cookie(cookie)
html = br.html
br.close()
return html

View File

@ -185,7 +185,7 @@ class Test(unittest.TestCase):
self.assertEqual(self.browser.visit('http://127.0.0.1:%d/cookies'%self.port), self.assertEqual(self.browser.visit('http://127.0.0.1:%d/cookies'%self.port),
True) True)
sent_cookies = self.server.sent_cookies sent_cookies = self.server.sent_cookies
cookies = self.browser.cookies() cookies = self.browser.cookies
cmap = {c.name:c for c in cookies} cmap = {c.name:c for c in cookies}
for name, vals in sent_cookies.iteritems(): for name, vals in sent_cookies.iteritems():
c = cmap[name] c = cmap[name]