Skip to content

CDP Mode: Patch 43 #3624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ sb.cdp.scroll_to_bottom()
sb.cdp.scroll_up(amount=25)
sb.cdp.scroll_down(amount=25)
sb.cdp.save_screenshot(name, folder=None, selector=None)
sb.cdp.print_to_pdf(name, folder=None)
```

--------
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.36.0"
__version__ = "4.36.1"
12 changes: 11 additions & 1 deletion seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fasteners
import json
import logging
import os
import platform
Expand Down Expand Up @@ -769,6 +770,7 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.scroll_up = CDPM.scroll_up
cdp.scroll_down = CDPM.scroll_down
cdp.save_screenshot = CDPM.save_screenshot
cdp.print_to_pdf = CDPM.print_to_pdf
cdp.page = page # async world
cdp.driver = driver.cdp_base # async world
cdp.tab = cdp.page # shortcut (original)
Expand Down Expand Up @@ -2125,6 +2127,15 @@ def _set_chrome_options(
prefs["enable_do_not_track"] = True
if external_pdf:
prefs["plugins.always_open_pdf_externally"] = True
pdf_settings = {
"recentDestinations": [
{"id": "Save as PDF", "origin": "local", "account": ""}
],
"selectedDestinationId": "Save as PDF",
"version": 2,
}
app_state = "printing.print_preview_sticky_settings.appState"
prefs[app_state] = json.dumps(pdf_settings)
if proxy_string or proxy_pac_url:
# Implementation of https://stackoverflow.com/q/65705775/7058266
prefs["webrtc.ip_handling_policy"] = "disable_non_proxied_udp"
Expand Down Expand Up @@ -3299,7 +3310,6 @@ def get_remote_driver(
from seleniumbase.core import capabilities_parser
desired_caps = capabilities_parser.get_desired_capabilities(cap_file)
if cap_string:
import json
try:
extra_caps = json.loads(str(cap_string))
except Exception as e:
Expand Down
6 changes: 6 additions & 0 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2181,6 +2181,12 @@ def save_screenshot(self, name, folder=None, selector=None):
else:
self.select(selector).save_screenshot(filename)

def print_to_pdf(self, name, folder=None):
filename = name
if folder:
filename = os.path.join(folder, name)
self.loop.run_until_complete(self.page.print_to_pdf(filename))


class Chrome(CDPMethods):
def __init__(self, url=None, **kwargs):
Expand Down
8 changes: 6 additions & 2 deletions seleniumbase/undetected/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ def reconnect(self, timeout=0.1):
if self.current_url.startswith(
"chrome-extension://"
):
self.close()
# https://issues.chromium.org/issues/396611138
# (Uncomment below when resolved)
# self.close()
if self.service.is_connectable():
self.stop_client()
self.service.stop()
Expand Down Expand Up @@ -496,7 +498,9 @@ def connect(self):
if self.current_url.startswith(
"chrome-extension://"
):
self.close()
# https://issues.chromium.org/issues/396611138
# (Uncomment below when resolved)
# self.close()
if self.service.is_connectable():
self.stop_client()
self.service.stop()
Expand Down
40 changes: 36 additions & 4 deletions seleniumbase/undetected/cdp_driver/tab.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations
import asyncio
import base64
import datetime
import logging
import pathlib
import urllib.parse
import warnings
from typing import Dict, List, Union, Optional, Tuple
from . import browser as cdp_browser
Expand Down Expand Up @@ -1133,9 +1136,6 @@ async def save_screenshot(
:return: The path/filename of the saved screenshot.
:rtype: str
"""
import urllib.parse
import datetime

await self.sleep() # Update the target's URL
path = None
if format.lower() in ["jpg", "jpeg"]:
Expand Down Expand Up @@ -1166,8 +1166,40 @@ async def save_screenshot(
"Most possible cause is the page "
"has not finished loading yet."
)
import base64
data_bytes = base64.b64decode(data)
if not path:
raise RuntimeError("Invalid filename or path: '%s'" % filename)
path.write_bytes(data_bytes)
return str(path)

async def print_to_pdf(
self,
filename: Optional[PathLike] = "auto",
) -> str:
"""
Saves a webpage as a PDF.
:param filename: uses this as the save path
:type filename: PathLike
:return: The path/filename of the saved screenshot.
:rtype: str
"""
await self.sleep() # Update the target's URL
path = None
ext = ".pdf"
if not filename or filename == "auto":
parsed = urllib.parse.urlparse(self.target.url)
parts = parsed.path.split("/")
last_part = parts[-1]
last_part = last_part.rsplit("?", 1)[0]
dt_str = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
candidate = f"{parsed.hostname}__{last_part}_{dt_str}"
path = pathlib.Path(candidate + ext) # noqa
else:
path = pathlib.Path(filename)
path.parent.mkdir(parents=True, exist_ok=True)
data, _ = await self.send(cdp.page.print_to_pdf())
if not data:
raise ProtocolException("Could not save PDF.")
data_bytes = base64.b64decode(data)
if not path:
raise RuntimeError("Invalid filename or path: '%s'" % filename)
Expand Down