├── .gitignore ├── Documentation.md ├── LICENSE ├── README.md ├── chrome_remote_shell └── __init__.py ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | include 2 | bin 3 | lib 4 | 5 | *.py[cod] 6 | 7 | .Python 8 | -------------------------------------------------------------------------------- /Documentation.md: -------------------------------------------------------------------------------- 1 | #NAME 2 | chrome_remote_shell - Client for the Google Chrome browser's remote debugging api. 3 | 4 | 5 | #DESCRIPTION 6 | `a = Shell(host='localhost', port=92222)` 7 | 8 | a.tablist has a list of details on open tabs. 9 | 10 | `a.connect(tab=index, updateTabs=True)` 11 | 12 | will connect `a.soc` to the webservice endpoint for `tablist[index]`'th 13 | tab. index is an integer, and updateTabs is True or False. Both `tab` 14 | and `update_tabs` are optional, defaulting to 0 and True respectively. 15 | 16 | At this point `a.soc.send()` and `a.soc.recv()` will synchronously write 17 | commands and read responses. The api is semi-asynchronous with 18 | responses for commands, but also spontaneous events will be 19 | send by the browser. For this kind of advance usage, select/poll 20 | on soc is advised. 21 | 22 | As a convenience, the shell connection object offers a method that, by 23 | injecting JavaScript into the first tab, commands Chrome to open a URL 24 | in a new tab:: 25 | 26 | `a.open_url('http://www.aldaily.com/')` 27 | 28 | You can also optionally specify a different tab to operate on. 29 | 30 | 31 | 32 | class Shell(__builtin__.object) 33 | | A remote debugging connection to Google Chrome. 34 | | 35 | | > a = Shell(host='localhost', port=92222) 36 | | 37 | | a.tablist has a list of details on open tabs. 38 | | 39 | | > a.connect(tab=index, update_tabs=True) 40 | | 41 | | will connect a.soc to the webservice endpoint for tablist[index]'th 42 | | tab. index is an integer, and update_tabs is True or False. Both tab 43 | | and updateTabs are optional, defaulting to 0 and True respectively. 44 | | 45 | | At this point a.soc.send and a.soc.recv will synchronously write 46 | | commands and read responses. The api is semi-asynchronous with 47 | | responses for commands, but also spontaeneous events will be 48 | | send by the browser. For this kind of advance usage, select/pol 49 | | on soc is advised. 50 | | 51 | | Methods defined here: 52 | | 53 | | __init__(self, host='localhost', port=9222) 54 | | 55 | | close(self) 56 | | Close websocket connection to remote browser. 57 | | 58 | | connect(self, tab=None, update_tabs=True) 59 | | Open a websocket connection to remote browser, determined by 60 | | self.host and self.port. Each tab has it's own websocket 61 | | endpoint - you specify which with the tab parameter, defaulting 62 | | to 0. The parameter update_tabs, if True, will force a rescan 63 | | of open tabs before connection. 64 | | 65 | | find_tabs(self) 66 | | Connect to host:port and request list of tabs 67 | | return list of dicts of data about open tabs. 68 | | 69 | | open_url(self, url) 70 | | Open a URL in the oldest tab. 71 | | 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Fred Clift and Brandon Rhodes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #chrome_remote_shell 2 | 3 | 4 | ###Client for the Google Chrome browser's remote debugging shell. 5 | 6 | New update replaces old api code with new - no longer compatibile with 3+ year 7 | old chrome, now compatible with Chrome Remote Debugging Protocol 8 | 9 | 10 | See for details on 11 | Chrome's remote debugging protocol. 12 | 13 | The protocol is composed of json messages described on the site above. 14 | 15 | This library makes it easier to communicate with the Google Chrome remote 16 | debugging api from Python. To enable the debugging api port, start Chrome 17 | with this option:: 18 | 19 | google-chrome --remote-shell-port=9222 20 | 21 | 22 | Note that Chromecast devices also open a remote debugging port and speak 23 | this same api, on devices in development mode, and which are currently 24 | running a dev app. 25 | 26 | Then you can connect from Python through code like this: 27 | 28 | ### Example 29 | 30 | >>> import chrome_remote_shell, json 31 | >>> shell = chrome_remote_shell.Shell(host='localhost', port=9222) 32 | >>> shell.connect(0) 33 | >>> url = 'http://www.clift.org/fred' # shameless 34 | >>> navcom = json.dumps({"id":0, "method":"Page.navigate", "params":{"url":url}}) 35 | >>> shell.soc.send(navcom) 36 | >>> response = json.loads(shell.recv()) 37 | 38 | 39 | As a convenience, the shell connection object offers a method that, by 40 | injecting JavaScript into the first tab, commands Chrome to open a URL 41 | in a new tab:: 42 | 43 | # equivalent to the api call above 44 | >>> shell.open_url('http://www.aldaily.com/') 45 | 46 | #TODO Installation 47 | 48 | 49 | #CHANGELOG 50 | *2014 Sept 15.* Updated to work with modern Chrome Remote Debugging protocol 51 | 52 | *2014 Sept 15.* Package maintaince transitioned to Fred Clift 53 | 54 | *2014 Sept 15.* Package migrated to GitHub. 55 | 56 | *2009 Feb 26.* Added a conditional import of `simplejson` so that the 57 | module runs under Python 2.5. 58 | 59 | -------------------------------------------------------------------------------- /chrome_remote_shell/__init__.py: -------------------------------------------------------------------------------- 1 | """ Client for the Google Chrome browser's remote debugging api. 2 | 3 | > a = Shell(host='localhost', port=92222) 4 | 5 | a.tablist has a list of details on open tabs. 6 | 7 | > a.connect(tab=index, updateTabs=True) 8 | 9 | will connect a.soc to the webservice endpoint for tablist[index]'th 10 | tab. index is an integer, and updateTabs is True or False. Both tab 11 | and updateTabs are optional, defaulting to 0 and True respectively. 12 | 13 | At this point a.soc.send and a.soc.recv will synchronously write 14 | commands and read responses. The api is semi-asynchronous with 15 | responses for commands, but also spontaeneous events will be 16 | send by the browser. For this kind of advance usage, select/pol 17 | on soc is advised. 18 | 19 | As a convenience, the shell connection object offers a method that, by 20 | injecting JavaScript into the first tab, commands Chrome to open a URL 21 | in a new tab:: 22 | 23 | a.open_url('http://www.aldaily.com/') 24 | 25 | You can also optionally specify a different tab to operate on. 26 | """ 27 | import json 28 | import requests 29 | import websocket 30 | 31 | 32 | class Shell(object): 33 | """A remote debugging connection to Google Chrome. 34 | 35 | > a = Shell(host='localhost', port=92222) 36 | 37 | a.tablist has a list of details on open tabs. 38 | 39 | > a.connect(tab=index, update_tabs=True) 40 | 41 | will connect a.soc to the webservice endpoint for tablist[index]'th 42 | tab. index is an integer, and update_tabs is True or False. Both tab 43 | and updateTabs are optional, defaulting to 0 and True respectively. 44 | 45 | At this point a.soc.send and a.soc.recv will synchronously write 46 | commands and read responses. The api is semi-asynchronous with 47 | responses for commands, but also spontaeneous events will be 48 | send by the browser. For this kind of advance usage, select/pol 49 | on soc is advised. """ 50 | 51 | def __init__(self, host='localhost', port=9222): 52 | """ init """ 53 | self.host = host 54 | self.port = port 55 | self.soc = None 56 | self.tablist = None 57 | self.find_tabs() 58 | 59 | def connect(self, tab=None, update_tabs=True): 60 | """Open a websocket connection to remote browser, determined by 61 | self.host and self.port. Each tab has it's own websocket 62 | endpoint - you specify which with the tab parameter, defaulting 63 | to 0. The parameter update_tabs, if True, will force a rescan 64 | of open tabs before connection. """ 65 | if update_tabs or not self.tablist: 66 | self.find_tabs() 67 | if not tab: 68 | tab = 0 69 | wsurl = self.tablist[tab]['webSocketDebuggerUrl'] 70 | if self.soc.connected: 71 | self.soc.close() 72 | self.soc = websocket.create_connection(wsurl) 73 | return self.soc 74 | 75 | def close(self): 76 | """ Close websocket connection to remote browser.""" 77 | if self.soc: 78 | self.soc.close() 79 | self.soc = None 80 | 81 | def find_tabs(self): 82 | """Connect to host:port and request list of tabs 83 | return list of dicts of data about open tabs.""" 84 | # find websocket endpoint 85 | response = requests.get("http://%s:%s/json" % (self.host, self.port)) 86 | self.tablist = json.loads(response.text) 87 | return self.tablist 88 | 89 | def open_url(self, url): 90 | """Open a URL in the oldest tab.""" 91 | if not self.soc or not.self.soc.connected: 92 | self.connect(tab=0) 93 | # force the 'oldest' tab to load url 94 | navcom = json.dumps({"id": 0, 95 | "method": "Page.navigate", 96 | "params": {"url": url}}) 97 | # This code would open a new window, but browsers really dont 98 | # like doing so. And, the results are irritating at best. 99 | # navcom=json.dumps({"id":0,"method":"Runtime.evaluate", 100 | # "params":{"expression": "window.open('%s', #'_blank', 101 | # 'toolbar=1,scrollbars=1,location=1,statusbar=0,menubar=1,resizable=1' 102 | # )" % (url) }}) 103 | self.soc.send(navcom) 104 | return self.soc.recv() 105 | 106 | 107 | if __name__ == '__main__': 108 | import doctest 109 | doctest.testmod() 110 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | websocket-client>=0.18.0 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import chrome_remote_shell 2 | import sys 3 | from distutils.core import setup 4 | 5 | requirements = [] 6 | 7 | setup( 8 | name='chrome_remote_shell', 9 | version='1.2', 10 | description='Client for talking to the Google Chrome remote shell port', 11 | long_description=chrome_remote_shell.__doc__.split('\n\n', 1)[1], 12 | author='Fred Clift and Brandon Craig Rhodes', 13 | author_email='fred@clift.org', 14 | url='https://github.com/minektur/chrome_remote_shell', 15 | packages=['chrome_remote_shell'], 16 | platforms='any', 17 | license='MIT', 18 | classifiers=[ 19 | 'Development Status :: 5 - Production/Stable', 20 | 'Intended Audience :: Developers', 21 | 'Operating System :: OS Independent', 22 | 'Programming Language :: Python', 23 | 'Programming Language :: Python :: 2', 24 | 'Programming Language :: Python :: 3', 25 | 'License :: OSI Approved :: MIT License', 26 | 'Topic :: Internet :: WWW/HTTP :: Browsers', 27 | ], 28 | install_requires=['websocket-client'], 29 | ) 30 | --------------------------------------------------------------------------------