Skip to content

QUESTION: Using without a context manager #3482

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

Closed
PFython opened this issue Feb 6, 2025 · 2 comments
Closed

QUESTION: Using without a context manager #3482

PFython opened this issue Feb 6, 2025 · 2 comments
Labels
question Someone is looking for answers UC Mode / CDP Mode Undetected Chromedriver Mode / CDP Mode

Comments

@PFython
Copy link

PFython commented Feb 6, 2025

Hi Michael and team - first of all thanks so much for an amazing package... I have so many good things to say about it (and your examples... and your videos...) but will save that for another place/time!

I just wanted to check if I'm missing something important with regards to context managers and most/all of the examples in your documentation starting with with SB(uc=True, test=True, locale_code="en") as sb: etc?

For debugging and REPL work, and generally doing more with the sb object without one massive with block, I prefer to manually create and close the object, which means the browser stays open for as long as I need to tinker. Indeed many original selenium scripts include a final driver.close(). Here's the wrapper I've come up with but since I don't know what I don't know, I just wanted to ask if there are any problems you can see with this approach please? And if not, maybe include something like this as an optional approach in the docs or the package itself?

Also, very trivial points but could the arguments of SB.__exit__ be made optional so that SB.__exit__() works without specifying None three times? Also SB.activate_cdp_mode() defaulting to None? And pls=None instead of pls="none"?

Many thanks in advance for your time considering this!

import atexit
import platform

import httpx
import requests
from seleniumbase import SB

sb_defaults = {
    'uc': True,
    'headless': False,
    'test': False,
    'locale_code': "en",
    'cdp': False,
    'pls': "none",
    'ad_block': True,
    'xvfb': platform.platform().startswith("Linux")
}

def get_sb(**kwargs):
    """
    Return a SeleniumBase object with Undetected Chromedriver (UC) and/or Chrome Devtools Protocol
    (CDP) enabled, and helper methods added.

    >>> sb = get_sb()
    >>> sb.get("https://www.rightmove.co.uk/properties/157644773")
    >>> sb.click('#onetrust-accept-btn-handler')
    >>> sb.fetch()
    >>> sb.highlight('h1')
    >>> sb.post_message("SeleniumBase wasn't detected", duration=4)
    >>> sb.close()
    """
    kwargs = sb_defaults | kwargs
    cdp = kwargs.pop('cdp')
    sb_base = SB(**kwargs)
    sb = sb_base.__enter__()
    if cdp:
        sb.activate_cdp_mode(None)
    def _close():
        sb_base.__exit__(None, None, None)
    def _fetch(url=None):
        """Fetch html page source and save as .html attribute"""
        if url:
            sb.get(url)
        sb.html = sb.get_page_source()
    def _get_response(url=None, use_httpx=False):
        """
        Generate requests.Response or httpx.Response object eg for further use by parsel/lxml etc
        """
        sb.fetch(url)  # copy page source to .html
        status_code = sb.get_link_status_code(sb.get_current_url())
        if use_httpx:
            response = httpx.Response(status_code)
        else:
            response = requests.Response()
            response.status_code = status_code
            response._content_consumed = True
        response.url = sb.get_current_url()
        if isinstance(sb.html, bytes):
            response._content = base64.b64decode(sb.html)
        if isinstance(sb.html, str):
            response._content = sb.html.encode("utf-8")
        return response
    for method in [_close, _fetch,  _get_response]:
        setattr(sb, method.__name__.removeprefix("_"), method)
    # Add aliases:
    sb.first = sb.find_element
    sb.all = sb.find_elements
    sb.quit = sb.close
    atexit.register(sb.quit)
    return sb
@PFython PFython changed the title QUESTION: Do we really need a context manager? QUESTION: Using without a context manager Feb 6, 2025
@mdmintz mdmintz added question Someone is looking for answers UC Mode / CDP Mode Undetected Chromedriver Mode / CDP Mode labels Feb 6, 2025
@mdmintz
Copy link
Member

mdmintz commented Feb 6, 2025

See the list of valid SeleniumBase Syntax Formats: SeleniumBase/help_docs/syntax_formats.md.

There are formats for using SeleniumBase without a context manager. Eg: The Driver() format:

from seleniumbase import Driver

driver = Driver()
try:
    driver.open("seleniumbase.io/demo_page")
    driver.highlight("h2")
    driver.type("#myTextInput", "Automation")
    driver.click("#checkBox1")
    driver.highlight("img", loops=6)
finally:
    driver.quit()

You can also do the same as above using atexit:

import atexit
from seleniumbase import Driver

driver = Driver()
atexit.register(driver.quit)
driver.open("seleniumbase.io/demo_page")
driver.highlight("h2")
driver.type("#myTextInput", "Automation")
driver.click("#checkBox1")
driver.highlight("img", loops=6)

The Driver Manager also has access to CDP Mode:

import atexit
from seleniumbase import Driver

driver = Driver(uc=True)
atexit.register(driver.quit)
url = "www.planetminecraft.com/account/sign_in/"
driver.uc_activate_cdp_mode(url)
driver.sleep(2)
driver.cdp.gui_click_element("#turnstile-widget div")
driver.sleep(2)

Here's an example from the CDP Driver format (which is naturally stealthy):

import asyncio
from seleniumbase.core import sb_cdp
from seleniumbase.undetected import cdp_driver

url = "https://seleniumbase.io/demo_page"
loop = asyncio.new_event_loop()
driver = cdp_driver.cdp_util.start_sync()
page = loop.run_until_complete(driver.get(url))
sb = sb_cdp.CDPMethods(loop, page, driver)
sb.press_keys("input", "Text")
sb.highlight("button")
sb.click("button")
sb.sleep(2)

Instead of trying to modify an existing format, use one that matches your needs better.

You also asked about changing the __exit__ method: That's an internal Python method:

If you want to change the structure of __exit__, then that would be a proposition for the Python Core Developers. 🙂

You also asked about pageLoadStrategy (pls):

The available values are not defined by SeleniumBase. (They come from Selenium)

The pls can be three things: "normal", "eager", and "none".
If pls is set to None (which is NOT the same as "none"), then pls takes on the default value that SeleniumBase gives it, which is "normal".

@mdmintz mdmintz closed this as completed Feb 6, 2025
@PFython
Copy link
Author

PFython commented Feb 6, 2025

Many thanks for the detailed reply Michael... I'll work my way through it! Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Someone is looking for answers UC Mode / CDP Mode Undetected Chromedriver Mode / CDP Mode
Projects
None yet
Development

No branches or pull requests

2 participants