initial commit

master
kirbylife 2019-11-04 18:00:31 -06:00
parent 373b0f82c2
commit 1af5236b95
4 changed files with 352 additions and 0 deletions

129
.gitignore vendored 100644
View File

@ -0,0 +1,129 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Poetry Stuff
poetry.lock
*.egg-info/

View File

@ -0,0 +1 @@
from iridium import iridium

205
iridium/iridium.py 100644
View File

@ -0,0 +1,205 @@
try:
from subprocess import getoutput
except ModuleNotFoundError:
from commands import getoutput
from datetime import datetime
from os import system as run
from time import sleep
from uuid import uuid4
import clipboard
from requests_html import HTML
from xdo import Xdo
class Iridium:
def __init__(self, browser, *args, **kwargs):
self.xdo = Xdo()
start_url = kwargs.get("start_url", "about:blank")
private = kwargs.get("private")
self.browser_name = browser.split("/")[-1]
cmd = [browser]
if private:
if "FIREFOX" in self.browser_name.upper():
cmd.append("--private-window")
else:
cmd.append("-incognito")
cmd = cmd + list(args) + ["&>/dev/null &"]
cmd = " ".join(cmd)
prev = self.__search(self.browser_name, sync=False)
run(cmd)
for _ in range(60):
sleep(1)
now = self.__search(self.browser_name)
ids = set(now) - set(prev)
if ids:
break
if len(ids) == 1:
self.id = list(ids)[0]
else:
self.ids = ids
self.id = None
sleep(2)
self.__key("Escape")
sleep(1)
if start_url:
self.__type(start_url)
self.__key("Return")
else:
self.__key("Return")
sleep(1)
def __search(self, arg, type="class", sync=True):
cmd = "xdotool search {} --onlyvisible --{type} {arg}"
sync = "--sync" if sync else ""
out = getoutput(cmd.format(sync, type=type, arg=arg))
out = out.split("\n")
return out
def __focus(self):
if not self.is_running():
raise Exception("{} is down".format(self.browser_name))
run("xdotool windowactivate {}".format(self.id))
focus = __focus
def __key(self, key, delay=20):
self.__focus()
run("xdotool key --delay {} {}".format(delay, key))
key = __key
def __type(self, text, delay=30, old=False):
self.__focus()
sleep(0.1)
if old:
text = text.replace('"', r'\"')
run('xdotool type --delay {} "{}"'.format(delay, text))
else:
temp = clipboard.paste()
clipboard.copy(text)
self.__key("Ctrl+v")
clipboard.copy(temp)
_type = __type
def is_running(self, counter=1):
processes = set(self.__search(self.browser_name))
if not self.id:
remain = self.ids - processes
if len(remain) == 0:
return True
else:
self.id = list(self.ids - remain)[0]
del self.ids
result = self.id in processes
if not result and counter > 0:
sleep(1)
result = self.is_running(counter=counter - 1)
return result
def get(self, url):
sleep(2)
self.__key("F6")
self.__type(url)
self.__key("Return")
def refresh(self, force=False, cut_params=True):
self.__focus()
if force:
self.execute_script("window.location.href = window.location.href" +
(".split('?')[0];" if cut_params else ";"))
else:
self.__key("F5")
def quit(self):
run("xdotool windowkill {}".format(self.id))
@property
def page_source(self):
self.__focus()
id_html = str(datetime.now()).replace(" ", "_")
self.__key("Ctrl+s")
sleep(2)
self.__key("Alt+n")
self.__type("/tmp/{}".format(id_html))
self.__key("Return")
sleep(10)
try:
html = open("/tmp/{}.html".format(id_html), "rb")
except:
html = open("/tmp/{}.htm".format(id_html), "rb")
try:
html.seek(0)
out = html.read().decode("utf-8")
except UnicodeDecodeError:
html.seek(0)
out = html.read().decode("latin")
html.close()
run("rm -R /tmp/{}*".format(id_html))
return out
def execute_script(self, cmd):
self.__focus()
cmd = cmd.splitlines()
if "FIREFOX" in self.browser_name.upper():
self.__key("Ctrl+Shift+k")
else:
self.__key("Ctrl+Shift+j")
sleep(4)
for line in cmd:
if not line.strip():
continue
self.__type(line)
sleep(0.2)
self.__key("Return")
sleep(2)
self.__key("Ctrl+Shift+i")
def get_element(self, css: str):
return IridiumNode(self, css)
def click(self, css: str):
script = f'''document.querySelector('{css}').click()'''
self.execute_script(script)
@property
def title(self):
return self.get_element("title").text()
class IridiumNode:
def __init__(self, driver: Iridium, selector: str):
self.driver = driver
self.uid = str(uuid4())
driver.execute_script(f'''
if(document.querySelector('{selector}'))
document.querySelector('{selector}').setAttribute("iridium_id", "{self.uid}");
''')
self.xpath_selector = f'//*[@iridium_id="{self.uid}"]'
self.css_selector = f'[iridium_id="{self.uid}"]'
def exists(self):
text = self.driver.page_source
html = HTML(html=text)
return bool(html.xpath(self.xpath_selector))
def click(self):
assert self.exists()
self.driver.click(self.css_selector)
def text(self):
assert self.exists()
text = self.driver.page_source
html = HTML(html=text)
value = html.xpath(self.xpath_selector + "/text()", first=True)
return value
def get_attribute(self, attr):
assert self.exists()
text = self.driver.page_source
html = HTML(html=text)
value = html.xpath(self.xpath_selector + "/@" + attr, first=True)
return value
getAttribute = get_attribute

17
pyproject.toml 100644
View File

@ -0,0 +1,17 @@
[tool.poetry]
name = "iridium"
version = "0.1.0"
description = "A diferent way to control a headless browser diferent to selenium"
authors = ["kirbylife <gabriel13m@gmail.com>"]
[tool.poetry.dependencies]
python = "^3.6"
requests-html = "^0.10.0"
clipboard = "^0.0.4"
python-libxdo = { git = "https://github.com/rshk/python-libxdo.git" }
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"