Skip to content

Upgrading to Pydoll 3

Pydoll 3 makes the synchronous API a first-class part of the library and cleans up the surface that grew around the async one. A Pydoll 2 script needs three mechanical changes, and the APIs that were already deprecated in 2.x are gone. Nothing about how the browser is driven changed.

You will learn

Import from pydoll

The classes you use in every script are exported from the top-level package. The sync API exports the same names from pydoll.sync, so the two forms differ by one import line.

from pydoll.sync import Chrome, ChromiumOptions, Key
from pydoll import Chrome, ChromiumOptions, Key

The old paths still work (pydoll.browser.chromium, pydoll.browser.options, pydoll.constants), so this change is optional. The names available at the top level are Chrome, Edge, ChromiumOptions, Tab, WebElement, ShadowRoot, Keyboard, Mouse, Scroll, Request, RequestHandle, Response, ResponseHandle, DownloadHandle, Key, ExtractionModel, Field, PageEvent, NetworkEvent and FetchEvent.

Call what used to be a property

Pydoll 2 exposed a few values as properties that had to be awaited. A property that needs await surprises readers and IDEs, and it had no honest synchronous equivalent, so in Pydoll 3 every one of them is a method:

Pydoll 2 Pydoll 3
await tab.title await tab.title()
await tab.current_url await tab.current_url()
await tab.page_source await tab.page_source()
await element.text await element.text()
await element.inner_html await element.inner_html()
await element.bounds await element.bounds()
await element.iframe_context await element.iframe_context()
await shadow_root.inner_html await shadow_root.inner_html()

Plain properties that never needed await are unchanged: tab.keyboard, element.tag_name, element.is_enabled and the rest.

from pydoll.sync import Chrome

with Chrome() as browser:
    tab = browser.start()
    tab.go_to('https://quotes.toscrape.com')
    print(tab.title())
    print(tab.find(class_name='text').text())
import asyncio

from pydoll import Chrome


async def main():
    async with Chrome() as browser:
        tab = await browser.start()
        await tab.go_to('https://quotes.toscrape.com')
        print(await tab.title())
        print(await (await tab.find(class_name='text')).text())

asyncio.run(main())

Removed APIs

Everything that raised a DeprecationWarning in Pydoll 2 is removed. Each row lists the replacement, which already worked in 2.x.

Removed Use instead
find_or_wait_element(by, value, ...) find() with attributes or query() with a selector; both take timeout, find_all and raise_exc. See Element finding.
tab.get_frame(element) The iframe WebElement itself: find() and query() on it reach inside the frame. See Iframes.
tab.execute_script(script, element) element.execute_script(script)
element.key_down(), element.key_up(), element.press_keyboard_key() tab.keyboard.down(), tab.keyboard.up(), tab.keyboard.press(). See Keyboard.
type_text(text, interval=...) type_text(text, humanize=True)
browser.start(headless=True) options.headless = True before creating the browser
expect_and_bypass_cloudflare_captcha() expect_cloudflare_turnstile()
enable_auto_solve_cloudflare_captcha(), disable_auto_solve_cloudflare_captcha() expect_cloudflare_turnstile() around the navigation; there is no background mode
custom_selector and time_before_click on those methods Drop them; the Turnstile widget is located automatically
NotAnIFrame, IFrameNotFound exceptions No longer raised by anything

The download handle returned by tab.expect_download() is now the public DownloadHandle class, importable from pydoll and pydoll.sync.

What's next