{
"info": {
"author": "Arnau Sanchez, Mathieu Le Marec-Pasquet",
"author_email": "pyarnau@gmail.com, kiorky@cryptelium.net",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License (GPL)",
"Natural Language :: English",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Programming Language :: Python",
"Topic :: Internet :: WWW/HTTP"
],
"description": "Intro\n=====================\n\n.. contents::\n\n\n.. image:: https://secure.travis-ci.org/makinacorpus/spynner.png\n :target: http://travis-ci.org/makinacorpus/spynner\n\n\nSpynner is a stateful programmatic web browser module for Python.\nIt is based upon `PyQT `_ and `WebKit `_.\nIt supports Javascript, AJAX, and every other technology that !WebKit is able to handle (Flash, SVG, ...).\nSpynner takes advantage of `JQuery `_. a powerful Javascript library that makes the interaction with pages and event simulation really easy.\n\nUsing Spynner you would able to simulate a web browser with no GUI (though a browsing window can be opened for debugging purposes), so it may be used to implement crawlers or acceptance testing tools.\n\n\nSee usage on: https://github.com/kiorky/spynner/tree/master/src/spynner/tests/spynner.rst\nOr below if the section is preset\n\nCredits\n========\nCompanies\n---------\n|makinacom|_\n\n * `Planet Makina Corpus `_\n * `Contact us `_\n\n.. |makinacom| image:: http://depot.makina-corpus.org/public/logo.gif\n.. _makinacom: http://www.makina-corpus.com\n\n\n\nAuthors\n------------\n\n- Mathieu Le Marec - Pasquet \n- Arnau Sanchez \n\nContributors\n-----------------\n\n- Leo Lou \n\nDependencies\n===================\n\n * `Python >=26 `_\n * `PyQt > 443 `_\n * Libxml2 / Libxslt libraries and includes files for lxml\n * autopy which in turns need xtst lib & headers on linux (aka Xtest)\n\nFeedback\n==============\nOpen an `Issue `_ to report a bug or request a new feature. Other comments and suggestions can be directly emailed to the authors_.\n\nInstall\n============\n* Throught regular easy_install / buildout::\n\n easy_install spynner\n\n (In Windows, you may have to install autopy through its installer at https://pypi.python.org/pypi/autopy/)\n\n* The bleeding edge version is hosted on github::\n\n git clone https://github.com/kiorky/spynner.git\n cd spynner\n python setup.py install\n\nRunning Spynner without X11\n====================================\n- Spynner needs a X11 server to run. If you are running it in a server without X11.\n You must install the virtual `Xvfb server `_.\n Debian users can use the small wrapper (xvfb-run). If you are not using Debian, you can download it here:\n http://www.mail-archive.com/debian-x@lists.debian.org/msg69632/x-run ::\n\n xvfb-run python myscript_using_spynner.py\n\n- You can also use tightvnc, which is the solution of the actual maintainer [kiorky].\n\n\nInitializing the browser\n==================================\nThe main concept to have a browser out there::\n\n >>> import spynner, os, sys\n >>> def print_contents(browser, dest='~/.browser.html'):\n ... \"\"\"Print the browser contents somewhere for you to see its context\n ... in doctest pdb, type print_contents(browser) and that's it, open firefox\n ... with file://~/browser.html.\"\"\"\n ... import os\n ... open(os.path.expanduser(dest), 'w').write(browser.contents)\n >>> import time\n >>> from StringIO import StringIO\n >>> debug_stream = StringIO()\n >>> bp = os.path.dirname(spynner.tests.__file__)\n\nThe browser::\n\n >>> browser = spynner.Browser(debug_level=spynner.DEBUG, debug_stream=debug_stream)\n\nWhen all is done::\n\n >>> browser.close()\n >>> def run_debug(callback, *args, **kwargs): # ** *\n ... pos = debug_stream.pos\n ... ret = callback(*args, **kwargs)\n ... show_debug(pos)\n ... return ret\n\n\n >>> def show_debug(pos=None):\n ... if not pos: print debug_stream.getvalue()\n ... else:\n ... pnow = debug_stream.pos\n ... debug_stream.seek(pos)\n ... print debug_stream.read()\n ... debug_stream.seek(pnow)\n\n\nDebugging\n==========\nSpynner uses webkit which is somewhat low level, never hesitate to activate verbose logs\nSometimes you'll want to see what is going on::\n\n >>> browser = spynner.Browser(debug_level=spynner.DEBUG, debug_stream=debug_stream)\n\nOr after initialization::\n\n >>> browser.debug_level = spynner.DEBUG\n\nSee more examples in the repository: https://github.com/kiorky/spynner/tree/master/examples\n\nShowing spynner window\n========================\nMaybe, you also want to have an output of what the browser is doing, just use that::\n\n >>> browser.show()\n\nYou can hide the webview with::\n\n thebrowser.hide()\n\n\nRunning Javascript\n====================\n\nSimply use::\n\n >>> ret = browser.runjs('console.log(\"foobar\")')\n\nBrowsing with spynner\n============================\nA basic but complicated example\nWord reference has resources loading which can fails, for thus we wait on the content to be there.\n\nIf the website was good, we could simple use ::\n\n >>> ret = debug_stream.read()\n >>> browser.load(bp+\"/html_controls.html\")\n True\n\nThis method throws an exception on timeout, and can customize the default 30 seconds timeout.\n\nBut there, our target can randomly fails.\nInstead, we will load and wait for something in the DOM to be there to continue.\nWe wait to have 'aaa' in the html, thus with unlimited tries at 1 seconds intervals each\n::\n\n >>> def wait_load(br):\n ... return 'aaa' in browser.html\n\nHit the wrong url, Eck, you are on an unlimited loop !::\n\n >>> browser.load(bp+\"html_controls.html\", 1, wait_callback=wait_load)\n\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n content loaded, waiting for content to mach the callback\n \n\nHit the wrong url, Eck, you are on an unlimited loop unless you wear condoms and set the tries!\nIt will throw an exception, but stop::\n\n >>> ret = debug_stream.read()\n\n Traceback (most recent call last):\n ...\n SpynnerTimeout: SPYNNER waitload: Timeout reached: 2 retries for 1s delay.\n\nFinnish to play, go to the real target::\n\n >>> ret = browser.load(bp+\"/html_controls.html\", 1, wait_callback=wait_load)\n >>> [a for a in debug_stream.getvalue().splitlines() if 'SPYNNER waitload' in a][-1]\n 'SPYNNER waitload: The callback found what it was waiting for in its contents!'\n\nInteract with the controls\n============================\n- See the implementation docstrings or examples !\n- You have three levels of control:\n\n - webkit methods which are recommended to us (wk_fill_*, wk_click_*) which are jquery based. The fill_* and click_*\n - The classical methods (fill, click_*) are now wrappers to the wk_* methods.\n - low level using QT raw events which are not that well working ATM.\n At least, you can move the mouse and sendKeys but it's a case per case coding.\n\nSetup::\n\n >>> browser.close()\n >>> del browser\n\nUsing radio inputs\n----------------------\n::\n\n >>> browser = spynner.Browser(debug_level=spynner.DEBUG, debug_stream=debug_stream)\n >>> ret = browser.load(bp+'/html_controls.html', 1, wait_callback=wait_load)\n\n\nUsing jquery\n++++++++++++++++++\n::\n\n >>> browser.load_jquery(True)\n\n >>> browser.radio('#radiomea')\n\n >>> ret = run_debug(browser.runjs, '$(\"input[name=radiome]\").each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.val()+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $(\"input[name=radiome]\").each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.val()+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): radiomea a true\n Javascript console (:1): radiomeb b false\n Javascript console (:1): radiomec c false\n \n >>> browser.radio('#radiomeb')\n >>> ret = run_debug(browser.runjs, '$(\"input[name=radiome]\").each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.val()+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $(\"input[name=radiome]\").each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.val()+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): radiomea a false\n Javascript console (:1): radiomeb b true\n Javascript console (:1): radiomec c false\n \n\n\nUsing webkit native methods\n+++++++++++++++++++++++++++++\nUnder the hood, we use this.evaluateJavaScript('this.value = xxx') ::\n\n >>> browser.wk_radio('#radiomea')\n >>> browser.load_jquery(True)\n >>> ret = run_debug(browser.runjs, '$(\"input[name=radiome]\").each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.val()+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $(\"input[name=radiome]\").each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.val()+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): radiomea a true\n Javascript console (:1): radiomeb b false\n Javascript console (:1): radiomec c false\n \n\n\nUsing check inputs\n----------------------\nUsing webkit native methods\n+++++++++++++++++++++++++++++\n::\n\n >>> browser.close()\n >>> browser = spynner.Browser(debug_level=spynner.DEBUG, debug_stream=debug_stream)\n >>> ret = browser.load(bp+'/html_controls.html', 1, wait_callback=wait_load)\n >>> ret = browser.load_jquery(True)\n\nUnder the hood, we use this.evaluateJavaScript('this.value = xxx') ::\n\n >>> browser.wk_check('#checkmea')\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea true\n Javascript console (:1): checkmeb false\n Javascript console (:1): checkmec false\n \n >>> browser.wk_check(['#checkmeb', '#checkmec'])\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea true\n Javascript console (:1): checkmeb true\n Javascript console (:1): checkmec true\n \n >>> browser.wk_uncheck(['#checkmeb', '#checkmec'])\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea true\n Javascript console (:1): checkmeb false\n Javascript console (:1): checkmec false\n \n >>> browser.wk_uncheck(['#checkmea'])\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea false\n Javascript console (:1): checkmeb false\n Javascript console (:1): checkmec false\n \n\nUsing jquery\n+++++++++++++++++++\n::\n\n >>> browser.load(bp+'/html_controls.html', 1, wait_callback=wait_load)\n >>> browser.load_jquery(True)\n\nUnder the hood, we use $(sel).attr('checked', 'checked')::\n\n >>> browser.check('#checkmea')\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea true\n Javascript console (:1): checkmeb false\n Javascript console (:1): checkmec false\n \n >>> browser.check(['#checkmeb', '#checkmec'])\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea true\n Javascript console (:1): checkmeb true\n Javascript console (:1): checkmec true\n \n >>> browser.uncheck(['#checkmeb', '#checkmec'])\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea true\n Javascript console (:1): checkmeb false\n Javascript console (:1): checkmec false\n \n >>> browser.uncheck(['#checkmea'])\n >>> ret = run_debug(browser.runjs, '$($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});')\n Run Javascript code: $($(\"input[name=checkme]\")).each(function(i, e){je=$(e);console.log(je.attr(\"id\")+\" \"+je.attr(\"checked\"));});\n Javascript console (:1): checkmea false\n Javascript console (:1): checkmeb false\n Javascript console (:1): checkmec false\n \n\nUsing select inputs\n----------------------\nUsing webkit native methods\n+++++++++++++++++++++++++++++\n::\n\n >>> ret = browser.load(bp+'/html_controls.html', 1, wait_callback=wait_load)\n >>> ret = browser.load_jquery(True)\n\nUnder the hood, we use this.evaluateJavaScript('this.value = xxx') ::\n\n >>> browser.wk_select('#sel', 'aa')\n >>> browser.runjs('$(\"#sel\").val();').toString()\n PyQt4.QtCore.QString(u'aa')\n >>> browser.wk_select('#sel', 'bb')\n >>> browser.runjs('$(\"#sel\").val();').toString()\n PyQt4.QtCore.QString(u'bb')\n >>> browser.wk_select('#sel', 'dd')\n >>> browser.runjs('$(\"#sel\").val();').toString()\n PyQt4.QtCore.QString(u'dd')\n\nIf it is not a multiple it takes the last::\n\n >>> browser.wk_select('#sel', ['aa', 'bb', 'dd'])\n >>> browser.runjs('$(\"#sel\").val();').toString()\n PyQt4.QtCore.QString(u'dd')\n\nIf it is a multiple it takes all::\n\n >>> browser.wk_select('#msel', ['maa', 'mbb', 'mdd'])\n >>> ret = run_debug(browser.runjs, '$($(\"#msel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});')\n Run Javascript code: $($(\"#msel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});\n Javascript console (:1): maaa true\n Javascript console (:1): mbbb true\n Javascript console (:1): mccc false\n Javascript console (:1): mddd true\n \n\nUsing jquery\n+++++++++++++++++++\n::\n\n >>> browser.load(bp+'/html_controls.html', 1, wait_callback=wait_load)\n >>> browser.load_jquery(True)\n\nUnder the hood, we use $(sel).attr(\"selected\", \"selected\")::\n\n >>> browser.select('#sel option[name=\"bbb\"]')\n >>> pos = debug_stream.pos\n >>> ret = run_debug(browser.runjs, '$($(\"#sel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});')\n Run Javascript code: $($(\"#sel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});\n Javascript console (:1): aaa false\n Javascript console (:1): bbb true\n Javascript console (:1): ccc false\n Javascript console (:1): ddd false\n \n\nWith a select with multiple args, it can also not deselect already selected values (remove as default)::\n\n >>> browser.select('#asel option[name=\"bbb\"]', remove=False)\n >>> ret = run_debug(browser.runjs, '$($(\"#asel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});')\n Run Javascript code: $($(\"#asel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});\n Javascript console (:1): aaa false\n Javascript console (:1): bbb true\n Javascript console (:1): ccc true\n Javascript console (:1): ddd false\n \n >>> browser.select('#asel option[name=\"bbb\"]', remove=True)\n >>> ret = run_debug(browser.runjs, '$($(\"#asel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});')\n Run Javascript code: $($(\"#asel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});\n Javascript console (:1): aaa false\n Javascript console (:1): bbb true\n Javascript console (:1): ccc false\n Javascript console (:1): ddd false\n \n\nIf it is a multiple it takes all::\n\n >>> browser.select(['#msel option[name=\"mbbb\"]', '#msel option[name=\"mddd\"]'])\n >>> ret = run_debug(browser.runjs, '$($(\"#msel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});')\n Run Javascript code: $($(\"#msel option\")).each(function(i, e){je=$(e);console.log(je.attr(\"name\")+\" \"+je.attr(\"selected\"));});\n Javascript console (:1): maaa false\n Javascript console (:1): mbbb true\n Javascript console (:1): mccc false\n Javascript console (:1): mddd true\n \n\n\nUsing text inputs\n----------------------\nUsing webkit native methods\n+++++++++++++++++++++++++++++\nUnder the hood, we use this.evaluateJavaScript('this.value = xxx')::\n\n >>> browser.wk_fill('input[name=w]', 'bar')\n\nUsing jquery\n+++++++++++++++++++\nUnder the hood, we use jQuery(selector).val(xxx)::\n\n >>> browser.fill('input[name=\"w\"]', 'foo')\n >>> ret = run_debug(browser.fill, 'input[name=\"w\"]', 'foo')\n Run Javascript code: $('input[name=\"w\"]').val('foo')\n \n\nJquery Notes\n=============\nSpynner uses jQuery to make Javascript interface easier.\nBy default, two modules are injected to every loaded page:\n\n * `JQuery core `_ Amongst other things, it adds the powerful `JQuery selectors `_, which are used internally by some Spynner methods.\n Of course you can also use jQuery when you inject your own code into a page.\n\n\n * [OBSOLETE, USE AT YOU OWN RISK, NO MAINTAINED, NO BUGFIX DONE] `Simulate `_ jQuery plugin: Makes it possible to simulate mouse and keyboard events (for now spynner uses it only in the _click_ action). Look up the library code to see which kind of events you can fire.\n\n\nAS nowodays jquery is already included on major websites, so we must not inject if the javascript is already loaded by the targeted website.\n\nBrowser jquery constructor related switches\n-------------------------------------------\nThus if you are targeting a website without jquery just use::\n\n Browser(embed_jquery=True)\n\nBy default the variable using jquery is \"$\", if your website is using something different use::\n\n Browser(jslib=\"jQueryObjectVarName\")\n\nWhere in javascript jQuery is referenced by::\n\n JAVASCRIPT:: \"\"\" jQueryObjectVarName(\"div\") \"\"\"\n\nIf you need jquery compatibility layer (jQuery.noConflict()), the variable referencing jquery will be \"spynnerjq\", use ::\n\n Browser(want_compat=True)\n\n\nLoading manually jquery\n--------------------------\n::\n\n >>> time.sleep(3)\n >>> browser.close()\n >>> browser = spynner.Browser(debug_level=spynner.DEBUG, debug_stream=debug_stream)\n >>> browser.show()\n >>> ret = run_debug(browser.runjs,\"console.log(typeof(jQuery));\")\n Run Javascript code: console.log(typeof(jQuery));\n Javascript console (:1): undefined\n \n\nEck, we didnt included jQuery !\n loading it::\n\n >>> ret = browser.load_jquery(force=True)\n >>> ret = run_debug(browser.runjs, \"console.log(typeof(jQuery));\")\n Run Javascript code: console.log(typeof(jQuery));\n Javascript console (:1): function\n \n\nCook your soup: parsing the HTML\n===================================\nYou can parse the HTML of a webpage with your favorite parsing library eg: `BeautifulSoup `_, `lxml `_ , or lxml, or ...\nSince we are already using Jquery for Javascript.\nIt feels just natural to work with `pyquery `_, its Python counterpart::\n\n >>> import pyquery\n >>> ret = browser.load(bp+'/html_controls.html')\n >>> d = pyquery.PyQuery(browser.html)\n >>> aaa = d.make_links_absolute(\"http://foo\")[0]\n >>> [dict(a.items())['href'] for a in d.root.xpath('//a')]\n ['http://foo/foo', 'http://foo/a/foo', 'http://foo/../b/foo', 'http://foo/c/foo', 'http://foo/d/foo']\n\n\nHTTP Headers\n============\nYou can give a list of http headers to send either which each request at\nconstruct time or via the load methods\n\nHeaders are in the form:\n\n - (['User-Agent', 'foobar')]\n\nSSL support\n=============\n\nyou have two keywords argument to specify:\n\n - a list (see QtSsl) of supported ciphers to use\n - the protocol to use (sslv2, tlsv1, sslv)3)\n\nMouse\n========\nyou can move the move on a css selector ::\n\n br.move_mouse('.myclass', [offsetx=0, offsety=0])\n\nProxy support\n=============\nSpynner support all proxiess supported by qt (http(s), socks5 & ftp)\n\nSee **examples/proxy.py** in the examples directory\n\nbasically use::\n\n br.set_proxy('foo:3128')\n br.set_proxy('http://foo:3128')\n br.set_proxy('http://user:suserpassword@foo:3128')\n br.set_proxy('https://user:suserpassword@foo:3128')\n br.set_proxy('socks5://user:suserpassword@foo:3128')\n br.set_proxy('httpcaching://user:suserpassword@foo:3128')\n br.set_proxy('ftpcaching://user:suserpassword@foo:3128')\n\nYou can also use proxy in the download method.\nNote that it will use by default the proxy setted via a previous br.set_proxy call::\n\n br.download('http://superfile', proxy_url='foo:3128')\n\n\n\nCHANGELOG\n============\n2.24 (2019-04-20)\n-----------------\n- support\n\n2.23 (2019-03-26)\n-----------------\n\n- support\n\n\n2.18 (2014-07-19)\n-----------------\n\n- changelog fix\n\n\n2.17 (2014-07-19)\n-----------------\n\n- py3 support\n\n\n2.16 (2014-04-25)\n-----------------\n\n- fix a bug in download, reply can be not finished to read on exit\n\n\n2.15 (2013-07-16)\n-----------------\n\n- fix #46\n\n\n2.14 (2013-06-05)\n-----------------\n\n- cookie jar fix (#41)\n\n\n2.13 (2013-05-17)\n-----------------\n\n- Better proxy support\n- Travis setup\n\n\n2.12 (2013-05-03)\n-----------------\n\n- Cookie jar fix\n\n\n2.11 (2013-04-23)\n-----------------\n\n- fix release again\n\n2.10 (2013-04-22)\n-----------------\n\n- fix release\n\n\n2.9 (2013-04-22)\n----------------\n\n- run natives clicks with autopy\n\n\n2.8 (2013-04-19)\n----------------\n\n- add a helper to move the mouse more easily\n\n\n2.7 (2013-04-17)\n----------------\n\n- Better ssl support\n- better http headers support\n- pyside support\n- better cookie support\n\n\n2.6 (2013-03-07)\n----------------\n\n- fix #17: download timeout\n\n\n2.5 (2013-03-06)\n----------------\n\n- fix #25: new signal api for sslErrors\n\n\n2.4 (2012-09-28)\n----------------\n\n- Example google fixed\n\n\n2.3 (2012-09-28)\n----------------\n\n- documentation\n\n\n2.2 (2012-09-20)\n----------------\n\n- Fix bug where jquery compatiblity mode can be not activated\n thx to yusumishi (yusumishi@gmail.com) for report.\n\n\n2.1 (2012-08-30)\n----------------\n\n- proper release\n\n\n2.0 (2012-08-05)\n----------------\n\n- Make new defaults for sane initialization & api cleanup, now:\n \n - We remapped simulations's functions to wk_* ones\n - we added extensive documentation in src/spynner/tests/spynner.rst\n - we do not embed jquery as default\n - we do not embed jquery's simulate plugins automaticly which is totally deprecated\n\n\n1.11 (2012-08-04)\n-----------------\n\n- proper release\n\n\n1.10 (2011-06-07)\n-----------------\n\n- add wk_check/_unckeck methods\n\n\n1.9 (2011-05-29)\n----------------\n\n- Rework javascript load [kiorky]\n- Some try in native events [kiorky]\n- Fix directory issue [kiorky]\n- add Samples [kiorky]\n- Fix download cookiesjar free problem [kiorky ]\n- Allow download to be tracked for further reuse [kiorky ]\n- Generate filenames by looking for their filename in response objects. [kiorky ]\n- Add api methods to:\n\n - send raw keyboard keys\n - send qt raw mouse clicks\n - use qtwebkit native JS click element & fill values\n - some helpers to wait for content\n\n [kiorky]\n\n- Add download files tracker [kiorky]\n\n0.0.3 (2009-08-01)\n------------------\n- Click does not wait for page load\n- Use QtNetwork infrastructure to download files\n- Expose webkit objects in Browser class\n- Change jQuery to _jQuery\n- HTTP authentication\n- Callbacks for Javascript confirm and prompts\n- Properties: url, html, soup\n- Better docstrings (using epydoc)\n- Implement image snapshots\n- Implement URL filters\n- Implement cookies setting\n [tokland ]\n\n\n0.0.2 (2009-07-27)\n---------------------\n- Use browser.html instead of browser.get_html\n- Fix setup.py to make it compatible with Win32\n- Add a URL filter mechanism (with a callback)\n- Use class-methods instead of burdening Browser.__init__\n- Instance variable to ignore SSL certificate errors\n- Start using epydoc format for API documentation\n- Add create_webview/destroy_webview for GUI debugging\n [tokland ]\n\n0.0.1 (2009-07-25)\n--------------------\n- Initial release. [tokland ]\n\n\n.. vim:set sts=4 ts=4 ai et tw=0:",
"description_content_type": "",
"docs_url": "https://pythonhosted.org/spynner/",
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/kiorky/spynner",
"keywords": "",
"license": "GPL v3.0",
"maintainer": "",
"maintainer_email": "",
"name": "spynner",
"package_url": "https://pypi.org/project/spynner/",
"platform": "",
"project_url": "https://pypi.org/project/spynner/",
"project_urls": {
"Homepage": "https://github.com/kiorky/spynner"
},
"release_url": "https://pypi.org/project/spynner/2.24/",
"requires_dist": null,
"requires_python": "",
"summary": "Programmatic web browsing module with AJAX support for Python",
"version": "2.24"
},
"last_serial": 5168005,
"releases": {
"1.11": [
{
"comment_text": "",
"digests": {
"md5": "eeee4965284009103062be2d1c9eb5b0",
"sha256": "d908a57d2dc47d3b4c459ffd5d98270ddf7411055456e80fc901ee6b3770fd29"
},
"downloads": -1,
"filename": "spynner-1.11.zip",
"has_sig": false,
"md5_digest": "eeee4965284009103062be2d1c9eb5b0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 141470,
"upload_time": "2012-08-04T14:53:16",
"url": "https://files.pythonhosted.org/packages/45/b3/88ccd8d90cff649e8a47f600692002da92093434c758081f415c013d9dc6/spynner-1.11.zip"
}
],
"2.0": [
{
"comment_text": "",
"digests": {
"md5": "d848dec01fcfc37b4ef2c85988d99671",
"sha256": "6204254eae0c0e065e77ec0333338a0dfa0b8cd02ddae4526944f3ae45ca8cff"
},
"downloads": -1,
"filename": "spynner-2.0.zip",
"has_sig": false,
"md5_digest": "d848dec01fcfc37b4ef2c85988d99671",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 159698,
"upload_time": "2012-08-05T03:54:56",
"url": "https://files.pythonhosted.org/packages/fe/84/a4008850493c47c356b238dcec820c5fc255832d98f95e6ff933673edb20/spynner-2.0.zip"
}
],
"2.1": [
{
"comment_text": "",
"digests": {
"md5": "178a01f3e1d9b7b381d29d8d20d02eb0",
"sha256": "57033b4a4f7c27b1dd943f7c21b4abdc0287251000e4730c918946a7a2e903e0"
},
"downloads": -1,
"filename": "spynner-2.1.zip",
"has_sig": false,
"md5_digest": "178a01f3e1d9b7b381d29d8d20d02eb0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 159904,
"upload_time": "2012-08-30T14:55:47",
"url": "https://files.pythonhosted.org/packages/fa/b6/f8d5197312612e9842e5ea97851ed74ff9d43657c336c1fa4a323f53eb8e/spynner-2.1.zip"
}
],
"2.10": [
{
"comment_text": "",
"digests": {
"md5": "74e21300c5a26a66c110d39bb54d8c31",
"sha256": "8fc723caf940f98d24c6b48ab1ad4c71ffd5ccdd9b9ac6a379f6fcf4cb110a6b"
},
"downloads": -1,
"filename": "spynner-2.10.zip",
"has_sig": false,
"md5_digest": "74e21300c5a26a66c110d39bb54d8c31",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 139090,
"upload_time": "2013-04-21T22:48:19",
"url": "https://files.pythonhosted.org/packages/35/9a/a42173fa77860189299dfd2d47660f45655f6e980363fd08089e6fdec330/spynner-2.10.zip"
}
],
"2.11": [
{
"comment_text": "",
"digests": {
"md5": "db43e3e7ab2c089ffe859d0b3c42a82e",
"sha256": "f8399c46af550a953c646838949df1b1d1670955aa0ca7f233f0f25d762d7a91"
},
"downloads": -1,
"filename": "spynner-2.11.zip",
"has_sig": false,
"md5_digest": "db43e3e7ab2c089ffe859d0b3c42a82e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 145919,
"upload_time": "2013-04-23T16:21:02",
"url": "https://files.pythonhosted.org/packages/fa/35/7a724d95cb8faf69a6d2e86ec4a317294d24dda80f1e8962441f5db89072/spynner-2.11.zip"
}
],
"2.12": [
{
"comment_text": "",
"digests": {
"md5": "13901258e520339af5588782e5b49175",
"sha256": "04cd1e0fcd56b6eba2ea629700ca8bc1d9ff357269b0e9ec9b1f35e64985ee06"
},
"downloads": -1,
"filename": "spynner-2.12.zip",
"has_sig": false,
"md5_digest": "13901258e520339af5588782e5b49175",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 146083,
"upload_time": "2013-05-03T12:02:30",
"url": "https://files.pythonhosted.org/packages/69/13/5159d9e81eb86b94af307f2779bed72d1f2c1d16c14d3ee0873dde7cd22d/spynner-2.12.zip"
}
],
"2.13": [
{
"comment_text": "",
"digests": {
"md5": "e7dd98f4b5986d0f181ace9144e2d1e2",
"sha256": "c688c89917487b5842a106f24f08afbe951d08b1c6306cf26ffc84a7229d9f17"
},
"downloads": -1,
"filename": "spynner-2.13.zip",
"has_sig": false,
"md5_digest": "e7dd98f4b5986d0f181ace9144e2d1e2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 147682,
"upload_time": "2013-05-17T18:27:15",
"url": "https://files.pythonhosted.org/packages/c0/43/c2936ac4357931b2e50cc668de6ef6cca8ef17a18a779fc204ef57d45bad/spynner-2.13.zip"
}
],
"2.14": [
{
"comment_text": "",
"digests": {
"md5": "77e297cce424a5c57fc26f44f7cc69c4",
"sha256": "40136b9f055cc79929f26f1b2d4f70988b85b63882a52ebfb1c19ce21e42d604"
},
"downloads": -1,
"filename": "spynner-2.14.zip",
"has_sig": false,
"md5_digest": "77e297cce424a5c57fc26f44f7cc69c4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 147797,
"upload_time": "2013-06-04T22:00:19",
"url": "https://files.pythonhosted.org/packages/48/39/e2def1cb3f1308938e0ba0c5a68e2d4cce8105c905c4361a115ace4fbb31/spynner-2.14.zip"
}
],
"2.15": [
{
"comment_text": "",
"digests": {
"md5": "58db1d78f00179f8db4a718a17856f23",
"sha256": "5ee5128faeb4f67e0fde3da19a29bf43be16ab94bca5865c8bc6a7b1be50a947"
},
"downloads": -1,
"filename": "spynner-2.15.zip",
"has_sig": false,
"md5_digest": "58db1d78f00179f8db4a718a17856f23",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 147869,
"upload_time": "2013-07-16T16:14:14",
"url": "https://files.pythonhosted.org/packages/8e/63/f959c8717562a04c6f339be4454d4716be182ba0b66f594c5bbdfe574f99/spynner-2.15.zip"
}
],
"2.16": [
{
"comment_text": "",
"digests": {
"md5": "1c8f8f5a5b442d8d820fe3c735fd2638",
"sha256": "852893abe537d499164d544081791dc84d728374af17a0b6a52be3868d7afdc2"
},
"downloads": -1,
"filename": "spynner-2.16.zip",
"has_sig": false,
"md5_digest": "1c8f8f5a5b442d8d820fe3c735fd2638",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 148089,
"upload_time": "2014-04-25T10:01:45",
"url": "https://files.pythonhosted.org/packages/43/1c/2d02c636beeefc99a33eaf4440ea4d0adc1a97a5ecab83e4694056437e46/spynner-2.16.zip"
}
],
"2.17": [
{
"comment_text": "",
"digests": {
"md5": "424cb02a17c65c02eaa279cc3ab5588e",
"sha256": "adf20d5a9f5ee2e647f6e69c2470dde724e2f2c201aa0d20c2e19689fffef76a"
},
"downloads": -1,
"filename": "spynner-2.17.zip",
"has_sig": false,
"md5_digest": "424cb02a17c65c02eaa279cc3ab5588e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 148263,
"upload_time": "2014-07-19T15:35:45",
"url": "https://files.pythonhosted.org/packages/e8/38/289a6afb8432d370baaa7080e790ba259fd9b444476bd18f757a05aecaf5/spynner-2.17.zip"
}
],
"2.18": [
{
"comment_text": "",
"digests": {
"md5": "bfcaee5914949712eb9cb24eb72c5455",
"sha256": "933ed8b0a09f5f8372636a588af9e716b53e7d835bd091765c5d5ab283596bf0"
},
"downloads": -1,
"filename": "spynner-2.18.zip",
"has_sig": false,
"md5_digest": "bfcaee5914949712eb9cb24eb72c5455",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 148281,
"upload_time": "2014-07-19T15:37:16",
"url": "https://files.pythonhosted.org/packages/7b/d9/5f082ed8f87cfc4605c879a7b2f9cb6ec3d7938453126aa5cee09d566b3f/spynner-2.18.zip"
}
],
"2.19": [
{
"comment_text": "",
"digests": {
"md5": "699687b3d38eb1001753bf9e6f4878a0",
"sha256": "fe2647f2feb70374daa83fcc898a0e4bcf0c07d09ea541c02180c5089b0e4c07"
},
"downloads": -1,
"filename": "spynner-2.19.zip",
"has_sig": false,
"md5_digest": "699687b3d38eb1001753bf9e6f4878a0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 146865,
"upload_time": "2014-07-19T15:38:58",
"url": "https://files.pythonhosted.org/packages/a6/93/5d2e68383b503ed5aeab9d05093d00b14ab412d0834429cb8b52a503baae/spynner-2.19.zip"
}
],
"2.2": [
{
"comment_text": "",
"digests": {
"md5": "6202de9675df25830e035993ac1ee455",
"sha256": "6eab3a59d0670bac27433aa3f74216dc6b0fcf95107d6af40ad611d9eb76922f"
},
"downloads": -1,
"filename": "spynner-2.2.zip",
"has_sig": false,
"md5_digest": "6202de9675df25830e035993ac1ee455",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 160113,
"upload_time": "2012-09-20T17:48:33",
"url": "https://files.pythonhosted.org/packages/20/d6/8ce97c2462cc42357e1dabd5e77d1afd5f229d785ce477412ce73063256d/spynner-2.2.zip"
}
],
"2.23": [
{
"comment_text": "",
"digests": {
"md5": "0d258905acb0cdbae03c4155d0a6ac8a",
"sha256": "835b1c0598f959d01779f2899c350d5a9e20b4e9842642c8d7aeaa0664e51c06"
},
"downloads": -1,
"filename": "spynner-2.23.tar.gz",
"has_sig": false,
"md5_digest": "0d258905acb0cdbae03c4155d0a6ac8a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 135468,
"upload_time": "2019-03-26T13:56:42",
"url": "https://files.pythonhosted.org/packages/52/a1/2cf0c18e9e1fadd69d60bf9d51778d134cbe0781d2f9c446a5cbfd288d3b/spynner-2.23.tar.gz"
}
],
"2.24": [
{
"comment_text": "",
"digests": {
"md5": "81e5933ea31aa31c342f359686c4da9a",
"sha256": "64bb226fe3a392dffdc12a91f243c4509d685d48b21d81c703606dadd9bce30f"
},
"downloads": -1,
"filename": "spynner-2.24.tar.gz",
"has_sig": false,
"md5_digest": "81e5933ea31aa31c342f359686c4da9a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 136687,
"upload_time": "2019-04-20T15:42:10",
"url": "https://files.pythonhosted.org/packages/6e/d9/de34a1d6351263fa40d228bc71a4a043012e0a1336204ae21c3272c3c7b8/spynner-2.24.tar.gz"
}
],
"2.3": [
{
"comment_text": "",
"digests": {
"md5": "baaa274b013523dbf4a5c7909da6c705",
"sha256": "6f2a258b2e9af20c4b767f435b8f5f2eab659642297423e7323c0909131a7c10"
},
"downloads": -1,
"filename": "spynner-2.3.zip",
"has_sig": false,
"md5_digest": "baaa274b013523dbf4a5c7909da6c705",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 159870,
"upload_time": "2012-09-28T10:10:01",
"url": "https://files.pythonhosted.org/packages/21/b7/1180425edff3db71d91e28b08da9cc6130a645c42fbb0196f91bab95d93c/spynner-2.3.zip"
}
],
"2.4": [
{
"comment_text": "",
"digests": {
"md5": "bc2bbac8aaa5584dd3a7de87b11c7987",
"sha256": "6dd3c933e99b6f0b08b553da503358257d4d45564bc6e648251a8424286894a5"
},
"downloads": -1,
"filename": "spynner-2.4.zip",
"has_sig": false,
"md5_digest": "bc2bbac8aaa5584dd3a7de87b11c7987",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 159931,
"upload_time": "2012-09-28T10:11:42",
"url": "https://files.pythonhosted.org/packages/e3/88/60633f3c0f1117ef5fb9f59c358960f078b4cfb363b4692059d7d48bdf65/spynner-2.4.zip"
}
],
"2.5": [
{
"comment_text": "",
"digests": {
"md5": "a3c1fbb9f41b9da0f73eb8468196f982",
"sha256": "17b342c94af83f9ec4e3ff16c83871f05f6557e96d717958db674957170a3641"
},
"downloads": -1,
"filename": "spynner-2.5.zip",
"has_sig": false,
"md5_digest": "a3c1fbb9f41b9da0f73eb8468196f982",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 160009,
"upload_time": "2013-03-06T16:43:14",
"url": "https://files.pythonhosted.org/packages/39/22/d3df3d5f4f1f16c2e092c7ef5c13789a57762e706482f4cd16a839c1b351/spynner-2.5.zip"
}
],
"2.6": [
{
"comment_text": "",
"digests": {
"md5": "84bfd1a4a95951689a6524350250583a",
"sha256": "3f11d82c705d9e35b427830126711277fb8c94152ed2be0bc30fd4f04d11d77e"
},
"downloads": -1,
"filename": "spynner-2.6.zip",
"has_sig": false,
"md5_digest": "84bfd1a4a95951689a6524350250583a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 160162,
"upload_time": "2013-03-07T09:08:07",
"url": "https://files.pythonhosted.org/packages/50/ba/df0b27bd10776bce84994cc566d973d243e84fce740050196adf3ac019b2/spynner-2.6.zip"
}
],
"2.7": [
{
"comment_text": "",
"digests": {
"md5": "7cc80530c3e40d7a5c71feed2f6ee40f",
"sha256": "cf79c2409a649a7d3cf6dc71954a1c54208a99a619ec822913dbbb5f428a5c73"
},
"downloads": -1,
"filename": "spynner-2.7.zip",
"has_sig": false,
"md5_digest": "7cc80530c3e40d7a5c71feed2f6ee40f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 138373,
"upload_time": "2013-04-17T16:39:15",
"url": "https://files.pythonhosted.org/packages/2a/81/33c60ea775194e6ff8a3310b6c5ecade8226ad7bac7de10162c7b8bd5e5a/spynner-2.7.zip"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "81e5933ea31aa31c342f359686c4da9a",
"sha256": "64bb226fe3a392dffdc12a91f243c4509d685d48b21d81c703606dadd9bce30f"
},
"downloads": -1,
"filename": "spynner-2.24.tar.gz",
"has_sig": false,
"md5_digest": "81e5933ea31aa31c342f359686c4da9a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 136687,
"upload_time": "2019-04-20T15:42:10",
"url": "https://files.pythonhosted.org/packages/6e/d9/de34a1d6351263fa40d228bc71a4a043012e0a1336204ae21c3272c3c7b8/spynner-2.24.tar.gz"
}
]
}