/Selenium-Driverless

undetected Selenium without usage of chromedriver

Primary LanguagePythonOtherNOASSERTION

Selenium-Driverless (Non-commercial use only!)

Downloads

  • use selenium without chromedriver
  • currently passes cloudfare, bet365, turnstile and others
  • multiple tabs simultanously
  • multiple Incognito-contexts with individual proxy & cookies
  • async (asyncio) and sync (experimantal) support

Feel free to test my code!

See dev-branch for the latest implementations.

dev-installation (click to expand)

pip install https://github.com/kaliiiiiiiiii/Selenium-Driverless/archive/refs/heads/dev.zip

Dependencies

Installing

Usage

with asyncio

from selenium_driverless import webdriver
from selenium_driverless.types.by import By
import asyncio


async def main():
    options = webdriver.ChromeOptions()
    async with webdriver.Chrome(options=options) as driver:
        await driver.get('http://nowsecure.nl#relax')
        await driver.sleep(0.5)
        await driver.wait_for_cdp("Page.domContentEventFired", timeout=15)

        elems = await driver.find_elements(By.XPATH, '/html/body/div[2]/div/main/p[2]/a')
        await elems[0].click(move_to=True)

        alert = await driver.switch_to.alert
        print(alert.text)
        await alert.accept()

        print(await driver.title)


asyncio.run(main())

synchronous

asyncified, might be buggy

from selenium_driverless.sync import webdriver

options = webdriver.ChromeOptions()
with webdriver.Chrome(options=options) as driver:
    driver.get('http://nowsecure.nl#relax')
    driver.sleep(0.5)
    driver.wait_for_cdp("Page.domContentEventFired", timeout=15)

    title = driver.title
    url = driver.current_url
    source = driver.page_source
    print(title)

custom debugger address

from selenium_driverless import webdriver

options = webdriver.ChromeOptions()
options.debugger_address = "127.0.0.1:2005"

# specify if you don't want to run remote
# options.add_argument("--remote-debugging-port=2005")

async with webdriver.Chrome(options=options) as driver:
  await driver.get('http://nowsecure.nl#relax', wait_load=True)

use events

Note: synchronous might not work properly

Examle Code (Click to expand)
from selenium_driverless import webdriver
import asyncio

global driver


async def on_request(params):
    await driver.execute_cdp_cmd("Fetch.continueRequest", {"requestId": params['requestId']},
                                 disconnect_connect=False)
    print(params["request"]["url"])


async def main():
    global driver
    options = webdriver.ChromeOptions()
    async with webdriver.Chrome(options=options) as driver:
        await driver.get('http://nowsecure.nl#relax')

        # enable Fetch, we don't want to disconnect after "Fetch.enable"
        await driver.execute_cdp_cmd("Fetch.enable", disconnect_connect=False)
        await driver.add_cdp_listener("Fetch.requestPaused", on_request)

        await driver.wait_for_cdp(event="Page.loadEventFired", timeout=5)

        await driver.remove_cdp_listener("Fetch.requestPaused", on_request)
        await driver.execute_cdp_cmd("Fetch.disable")

        print(await driver.title)


asyncio.run(main())

Multiple tabs simultously

Note: asyncio is recommendet, threading has been reported not too work

Examle Code (Click to expand)
from selenium_driverless.sync import webdriver
from selenium_driverless.utils.utils import read
from selenium_driverless import webdriver
import asyncio


async def target_1_handler(target):
    await target.get('https://abrahamjuliot.github.io/creepjs/')
    print(await target.title)


async def target_2_handler(target):
    await target.get("about:blank")
    await target.execute_script(script=read("/files/js/show_mousemove.js"))
    await target.pointer.move_to(500, 500, total_time=2)


async def main():
    options = webdriver.ChromeOptions()
    async with webdriver.Chrome(options=options) as driver:
        target_1 = await driver.current_target
        target_2 = await driver.switch_to.new_window("tab", activate=False)
        await asyncio.gather(
            target_1_handler(target_1),
            target_2_handler(target_2)
        )
        await target_1.focus()
        input("press ENTER to exit")


asyncio.run(main())

Unique execution contexts

  • execute javascript without getting detected
Examle Code (Click to expand)
from selenium_driverless.sync import webdriver
from selenium_driverless import webdriver
import asyncio


async def main():
    options = webdriver.ChromeOptions()
    async with webdriver.Chrome(options=options) as driver:
        await driver.get('chrome://version')
        script = """
        const proxy = new Proxy(document.documentElement, {
          get(target, prop, receiver) {
            if(prop === "outerHTML"){
                console.log('detected access on "'+prop+'"', receiver)
                return "mocked value:)"
            }
            else{return Reflect.get(...arguments)}
          },
        });
        Object.defineProperty(document, "documentElement", {
          value: proxy
        })
        """
        await driver.execute_script(script)
        src = await driver.execute_script("return document.documentElement.outerHTML", unique_context=True)
        mocked = await driver.execute_script("return document.documentElement.outerHTML", unique_context=False)
        print(src, mocked)


asyncio.run(main())

Pointer Interaction

see @master/dev/show_mousemove.py for visualization

pointer = await driver.current_pointer
move_kwargs = {"total_time": 0.7, "accel": 2, "smooth_soft": 20}

await pointer.move_to(100, 500)
await pointer.click(500, 50, move_kwargs=move_kwargs, move_to=True)

Iframes

  • switch and interact with iframes
iframes = await driver.find_elements(By.TAG_NAME, "iframe")
await asyncio.sleep(0.5)
target = await driver.get_target_for_iframe(iframes[0])

Multiple Contexts

  • different cookies for each context
  • A context can have multiple windows and tabs within
  • different proxy for each context
  • opens as a window as incognito
Examle Code (Click to expand)
from selenium_driverless.sync import webdriver
from selenium_driverless import webdriver
import asyncio


async def main():
    options = webdriver.ChromeOptions()
    async with webdriver.Chrome(options=options) as driver:
        context_1 = driver.current_context
        context_2 = await driver.new_context(proxy_bypass_list=["localhost"], proxy_server="http://localhost:5000")
        await context_1.current_target.get("https://examle.com")
        await context_2.get("https://examle.com")
        input("press ENTER to exit:)")


asyncio.run(main())

Help

Please feel free to open an issue or fork!
Note: please check the todo's below at first!

Todo's

Click to expand - implementations - [x] `WebElement`s - [ ] improve `mid_location` calculation - [x] `Input` - [x] `Mouse` - [x] `mousemove` - [x] `click` - [] `scroll` - [ ] `drag&drop` - [x] `write` - [ ] `Touch` - [ ] `touchmove` - [ ] `TouchTap` - [ ] `scoll` - [ ] `pinch//zoom` - [ ] `KeyBoard` - [ ] `SendKeys` - [ ] `send files` - [x] `execute_script` and `execute_async_script` - [ ] make serialization use `deep` - [x] add `Page.createIsolatedWorld` support with `DOM` access - [x] make `element.rect` use this - [ ] make `elem.box_model use this` - [ ] [support `options.add_extension()`](kaliiiiiiiiii#37) - [x] sync - [ ] move sync to threaded for allowing event_handlers - [ ] support multithreading with sync version

Authors

Copyright and Author:
Aurin Aegerter (aka Steve)

License

Shield: CC BY-NC-SA 4.0

Unless specified differently in a single file, this work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

CC BY-NC-SA 4.0

Disclaimer

I am not responsible what you use the code for!!! Also no warranty!

Acknowledgments

Inspiration, code snippets, etc.