| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-09-27 | 7.6 kB | |
| v0.17.0 source code.tar.gz | 2026-09-27 | 466.9 kB | |
| v0.17.0 source code.zip | 2026-09-27 | 540.7 kB | |
| Totals: 3 Items | 1.0 MB | 0 | |
This is quite a large release, including a number of fixes from first-time contributors (thank you!) as well as ports for all nodriver features and bugfixes that have been added upstream since Zendriver was forked. Additionally, I've managed to remove the last remaining # type: ignore comments, which uncovered a few additional bugs that have also been fixed in this release.
This release marks two years of Zendriver! Huge thanks to everyone that has contributed so far.
Fixed
- Fix
ValueErroron startup when thelangoption is set @bercedev - Fix
verify_cf()never clicking the Turnstile checkbox on Turnstile markup where thecf-turnstile-response/cf_challenge_responseinput is rendered as a document-level sibling of the challenge's shadow-DOM host rather than nested inside it. The lookup was scoped tohost_element, always found nothing, andverify_cf()returned before ever callingmouse_click. Now queries the input from the document (tab.query_selector) instead. @A-Nolan - Fix HTTP method casing to resolve CDP connection issues on some Windows installations @fitudao3788
- Fix
404 Not FoundWebSocket connection error foriframetargets by routing all target connections to/devtools/page/<target_id>inBrowser._handle_target_update. - Fix
Browser.stop()always waiting 3 seconds and then force-killing the browser process, even when the browser had already exited gracefully. The exit check readPopen.returncode, which is only updated bypoll()/wait(). @stephanlensky - Fix
Browser.start()always sleeping forbrowser_connection_timeoutbefore the first connection attempt. It now tries to connect immediately and only waits between retries. @stephanlensky - Fix
Browserinstances never being garbage collected afterBrowser.stop(). The asyncio atexit callback and the global registered instances set kept a reference to every browser, and per-target connections were left open. @stephanlensky - Fix the connection listener crashing with
InvalidStateErrorwhen a response arrived for a command that was already cancelled (for example byasyncio.wait_for), which left later commands hanging forever @stephanlensky - Fix every received CDP event being kept in memory for the lifetime of its connection @stephanlensky
- Fix
Tab.find()raisingProtocolException: No search session with given id foundinstead of retrying when the page's document is replaced during the search @stephanlensky - Fix
await tabraisingValueError: No listener created yeton a tab which no commands were sent to yet, such as one just opened withbrowser.get(url, new_tab=True)@stephanlensky - Fix
browser.get(url, new_tab=True)intermittently raisingStopIterationwhen the new tab was not registered yet, andbrowser.get()returning before the navigation when another target changed in the meantime @stephanlensky - Fix Widevine DRM being unavailable on platforms where Chrome installs it through the component updater, such as Windows. Removed the
--disable-component-update,--disable-background-networking,--disable-backgrounding-occluded-windowsand--disable-renderer-backgroundingdefault browser arguments @stephanlensky - Fix
Browser.start()sometimes returning before the browser's initial tab was registered, which madebrowser.get(url)raiseRuntimeError: coroutine raised StopIteration.browser.get(url)now raises a descriptive error if there is no tab to navigate @stephanlensky - Fix extensions added with
Config.add_extension()not loading on Chrome 137+. The default--disable-featuresswitch was overridden by a second one, since Chrome only uses the last. All--disable-featuresswitches, including user-provided ones, are now merged into one, and--enable-unsafe-extension-debuggingis passed when extensions are added @stephanlensky - Fix commands hanging forever when the browser connection closed while they were waiting for a response, for example because the browser crashed or
Connection.aclose()was called. Pending commands now raise instead, andConnection.send()raisesProtocolExceptioninstead of returningNonewhen the connection is closed @stephanlensky - Fix mouse helpers dispatching inconsistent button and pressure state that pages can observe. Mouse clicks and drags now report a pressure of
0.5while a button is held andbuttons=0on release, drag movements keepbuttons=1,mouse_move()no longer sends amouseReleasedevent, andTab.mouse_move()withstepsnow moves from the last mouse position instead of from(0, 0). Thebuttonsargument ofmouse_click()is deprecated and ignored @stephanlensky - Fix
add_handler()with a CDP domain module (for exampletab.add_handler(cdp.network, handler)) registering the domain's enum types instead of its events, so the handler was never called @stephanlensky - Fix synchronous event handlers sometimes calling only the last registered handler when several handlers were registered for the same event @stephanlensky
- Fix event handlers being called a second time with only the event when they raised a
TypeError. Handlers now receive the connection as a second argument only if their signature accepts one @stephanlensky - Fix
Element.get()returningNonefor attributes with an empty value, such asdisabled="". It now returns""and only returnsNonewhen the attribute does not exist @stephanlensky - Fix
remove_handlers(event_type)raisingKeyErrorwhen no handlers were registered for that event @stephanlensky - Fix
Element.get_position(abs=True)raisingAttributeError@stephanlensky - Fix
Element.get()returning dictionary methods for attribute names such asitemsorkeysinstead ofNone@stephanlensky - Fix
Tab.find_elements_by_text()skipping matches which are not part of the fetched document tree @stephanlensky - Fix
CookieJar.get_all(requests_cookie_format=True)raisingImportErrorwhenrequestsis not installed, and returning session cookies as already expired. It now buildshttp.cookiejar.Cookieobjects with the standard library @stephanlensky
Added
- Add support for unstable variants of Microsoft Edge on macOS @mokurin000
- Add
Tab.get_frames()andElement.get_frame()to access out-of-process (e.g. cross-origin) iframes asTabobjects, and aninclude_framesoption tofind(),find_all(),select()andselect_all()which also searches these iframes @stephanlensky - Add
Browser.create_context()to open a tab in a new browser context with its own proxy. Authenticated HTTP, HTTPS and SOCKS5 proxies (e.g.socks5://user:pass@host:port) are supported through a local forwarding proxy @stephanlensky - Add
Tab.mouse_down(),Tab.mouse_up()andTab.mouse_drag()to press, hold and release mouse buttons separately, for example to drag a slider with custom movement @stephanlensky - Add
Browser.get_tab()to find a tab by text in its URL or title @stephanlensky - Add
Element.shadow_childrento access the children of an element's open or closed shadow root @stephanlensky - Add
Tab.is_scrolled_to_bottom()andTab.bypass_insecure_connection_warning()@stephanlensky
Changed
- Tabs and other targets now share the browser's websocket connection using flat CDP sessions (
Target.attachToTargetwithflatten=True) instead of opening a separate websocket per target. This fixesHTTP 500errors when connecting to iframe targets. @stephanlensky Tab.set_download_path()now creates the download directory if it does not exist @stephanlensky- Expert mode no longer passes
--disable-web-security@stephanlensky