Undetectable Automation:
5th Edition




Press SPACE to continue!

Now on YouTube!


https://youtube.com/watch?v=R9HNsnbYh8o

This continues the series that started here...

๐Ÿ”น We're going to be using... ๐Ÿ”น


๐Ÿ”น To bypass all the CAPTCHAs... ๐Ÿ”น



๐Ÿ”น In all the places... ๐Ÿ”น



โœ… Local Desktop Machine - (Eg. Mac/Windows)

โœ… Docker Container - (Eg. ubuntu:24.04)

โœ… Linux Server - (Eg. GitHub Actions)

๐Ÿ”น In all the places... ๐Ÿ”น



โœ… Local Desktop Machine - (Eg. Mac/Windows)

โœ… Docker Container - (Eg. ubuntu:24.04)

โœ… Linux Server - (Eg. GitHub Actions)

๐Ÿ”น In all the places... ๐Ÿ”น



โœ… Local Desktop Machine - (Eg. Mac/Windows)

โœ… Docker Container - (Eg. ubuntu:24.04)

โœ… Linux Server - (Eg. GitHub Actions)

๐Ÿ”น In all the places... ๐Ÿ”น



โœ… Local Desktop Machine - (Eg. Mac/Windows)

โœ… Docker Container - (Eg. ubuntu:24.04)

โœ… Linux Server - (Eg. GitHub Actions)

๐Ÿ”น With live demos on major sites... ๐Ÿ”น


โœ… TikTok

โœ… Facebook

โœ… Amazon

โœ… LinkedIn

โœ… And many more!

๐Ÿ”น With live demos on major sites... ๐Ÿ”น


โœ… TikTok

โœ… Facebook

โœ… Amazon

โœ… LinkedIn

โœ… And many more!

๐Ÿ”น With live demos on major sites... ๐Ÿ”น


โœ… TikTok

โœ… Facebook

โœ… Amazon

โœ… LinkedIn

โœ… And many more!

๐Ÿ”น With live demos on major sites... ๐Ÿ”น


โœ… TikTok

โœ… Facebook

โœ… Amazon

โœ… LinkedIn

โœ… And many more!

๐Ÿ”น With live demos on major sites... ๐Ÿ”น


โœ… TikTok

โœ… Facebook

โœ… Amazon

โœ… LinkedIn

โœ… And many more!

๐Ÿ”น With live demos on major sites... ๐Ÿ”น


โœ… TikTok

โœ… Facebook

โœ… Amazon

โœ… LinkedIn

โœ… And many more!


Let's warm up with a few live demos
of bypassing Cloudflare's Turnstile...



With the tools I'll be covering in this video,
you be able to web-scrape sites with confidence.


This continues my Undetectable Automation series:

That all started with...

The Automation Entertainment Industry was born.

The field changes rapidly, so it's important to
note that the first three videos are out-of-date.

The first three used a modified Chromedriver
called "Undetected Chromedriver" for stealth.
At some point, modifying Chromedriver
wasn't enough to make WebDriver actions
stealthy because anti-bot services improved.
That led to the rise of CDP in order to bypass CAPTCHAs and bot-detection services again.

(This was the focus the 4th Undetectable Automation video.)
That led to the rise of CDP in order to bypass CAPTCHAs and bot-detection services again.

(This was the focus the 4th Undetectable Automation video.)
Lots of changes have happened since the last
video in the Undetectable Automation series:

Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.


Here are some advancements since then:



    โœ… Stealthy Playwright Mode.
    โœ… Stealthy Docker automation.
    โœ… Stealthy headless automation.
    โœ… Stealthy mobile emulation.
    โœ… Imperva-based hCaptcha can be bypassed.
    โœ… Friendly Captcha can be bypassed.
    โœ… DataDome Slider can be bypassed.
    โœ… Unbranded Chromium support.
    โœ… Pure CDP Mode.



Compared to standard JS, CDP actions are stealthy!

๐Ÿ”น CDP alone isn't enough for stealth... ๐Ÿ”น


โœ… The web browser's fingerprint must look like
one coming from a human-controlled browser.

๐Ÿ”น CDP alone isn't enough for stealth... ๐Ÿ”น


โœ… The web browser's fingerprint must look like
one coming from a human-controlled browser.

Browsers launched with regular Playwright or WebDriver
have a poisoned fingerprint that results in bot-detection.

To avoid this issue, you can launch the system's default
browser... and THEN attach the automation framework!

Some special configuration is needed
to make this work in a stealthy way.

And you may need special methods
for handling CAPTCHAs successfully

โคต๏ธ (That brings us to SeleniumBase...
a framework that takes care of
a lot of that work for you...)

Contrary to logical thinking,
SeleniumBase no longer uses Selenium
for some things (such as CDP Mode).

If that sounds confusing, note that JavaScript does not use Java.
Contrary to logical thinking,
SeleniumBase no longer uses Selenium
for some things (such as CDP Mode).

If that sounds confusing, note that JavaScript does not use Java.

SeleniumBase CDP Mode comes in 3 formats:



    โœ… SB() "nested sync" format.

    โœ… sb_cdp "sync" format. (Pure CDP)

    โœ… cdp_driver "async" format. (Pure CDP)

    "Stealthy Playwright Mode" can be
    initiated from any of these formats.


SeleniumBase CDP Mode comes in 3 formats:



    โœ… SB() "nested sync" format.

    โœ… sb_cdp "sync" format. (Pure CDP)

    โœ… cdp_driver "async" format. (Pure CDP)

    "Stealthy Playwright Mode" can be
    initiated from any of these formats.


SeleniumBase CDP Mode comes in 3 formats:



    โœ… SB() "nested sync" format.

    โœ… sb_cdp "sync" format. (Pure CDP)

    โœ… cdp_driver "async" format. (Pure CDP)

    "Stealthy Playwright Mode" can be
    initiated from any of these formats.


SeleniumBase CDP Mode comes in 3 formats:



    โœ… SB() "nested sync" format.

    โœ… sb_cdp "sync" format. (Pure CDP)

    โœ… cdp_driver "async" format. (Pure CDP)

    "Stealthy Playwright Mode" can be
    initiated from any of these formats.


SeleniumBase CDP Mode comes in 3 formats:



    โœ… SB() "nested sync" format.

    โœ… sb_cdp "sync" format. (Pure CDP)

    โœ… cdp_driver "async" format. (Pure CDP)

    "Stealthy Playwright Mode" can be
    initiated from any of these formats.


sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

sb_cdp "sync" Stealthly Playwright Mode example:


from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint_url)
    page = browser.contexts[0].pages[0]
    page.goto("https://copilot.microsoft.com")
    page.wait_for_selector("textarea#userInput")
    query = "Playwright Python connect_over_cdp() sync example"
    page.fill("textarea#userInput", query)
    page.wait_for_timeout(2000)
    page.click('button[data-testid="submit-button"]')
    sb.sleep(5.25)
    sb.solve_captcha()
    ...

It's time for that live demo where we use Stealthy
Playwright Mode to bypass Copilot's CAPTCHA...


Are you ready for another live demo?

Let's do some Walmart scraping now...

Are you ready for another live demo?

Let's do some Walmart scraping now...

Let's do another live demo
before returning to the learning.

This time, we're web-scraping TikTok...


Let's do another live demo
before returning to the learning.

This time, we're web-scraping TikTok...

In addition to using CDP for controlling Chrome in a stealthy way, you can also achieve stealth by using tools that can control the mouse and keyboard.

PyAutoGUI is one such tool:
In addition to using CDP for controlling Chrome in a stealthy way, you can also achieve stealth by using tools that can control the mouse and keyboard.

PyAutoGUI is one such tool:
PyAutoGUI requires a headed browser to work.

Since most Linux machines have headless displays that don't support headed browsers, an external tool called Xvfb must be used in order to simulate a headed browser in a headless Linux environment...
PyAutoGUI requires a headed browser to work.

Since most Linux machines have headless displays that don't support headed browsers, an external tool called Xvfb must be used in order to simulate a headed browser in a headless Linux environment...
Xvfb is automatically used when needed in SeleniumBase.
This makes stealthy automation easy from Linux servers.

Here's another GitHub Actions example:

I have a full YouTube tutorial on that:

Same story with stealthy Docker automation:

Note that Docker requires extra config to be stealthy!

These changes can be made from the Dockerfile.

Note that Docker requires extra config to be stealthy!

These changes can be made from the Dockerfile.

For instance, some standard fonts are not
installed from the default Docker config.

This is a problem because websites can see
which fonts are installed on your system.

If your system is missing standard fonts,
then websites know you're running from a server,
and therefore they know you're using automation.

The SeleniumBase Dockerfile has you covered:

It would be a big mistake to think that you can
be stealthy in a regular Docker container without
changing the default fonts and configuration...

In the spirit of online shopping,
the next demo is on Nordstrom...

And now for some Ralph Lauren...

Let's wrap up this quick
shopping session on Kohls...

Now that we have the ingredients for stealth...

    โœ… An automation framework that uses
    a browser with a natural fingerprint.

    โœ… CDP methods for performing natural actions
    (eg. Input.dispatchMouseEvent).

    โœ… PyAutoGUI for performing trickier actions
    (eg. drag-and-drop with a real mouse).

    โœ… Xvfb integration for using headed browsers
    on Linux systems that don't have a GUI.


Now that we have the ingredients for stealth...

    โœ… An automation framework that uses
    a browser with a natural fingerprint.


    โœ… CDP methods for performing natural actions
    (eg. Input.dispatchMouseEvent).

    โœ… PyAutoGUI for performing trickier actions
    (eg. drag-and-drop with a real mouse).

    โœ… Xvfb integration for using headed browsers
    on Linux systems that don't have a GUI.


Now that we have the ingredients for stealth...

    โœ… An automation framework that uses
    a browser with a natural fingerprint.

    โœ… CDP methods for performing natural actions
    (eg. Input.dispatchMouseEvent).


    โœ… PyAutoGUI for performing trickier actions
    (eg. drag-and-drop with a real mouse).

    โœ… Xvfb integration for using headed browsers
    on Linux systems that don't have a GUI.


Now that we have the ingredients for stealth...

    โœ… An automation framework that uses
    a browser with a natural fingerprint.

    โœ… CDP methods for performing natural actions
    (eg. Input.dispatchMouseEvent).

    โœ… PyAutoGUI for performing trickier actions
    (eg. drag-and-drop with a real mouse).


    โœ… Xvfb integration for using headed browsers
    on Linux systems that don't have a GUI.


Now that we have the ingredients for stealth...

    โœ… An automation framework that uses
    a browser with a natural fingerprint.

    โœ… CDP methods for performing natural actions
    (eg. Input.dispatchMouseEvent).

    โœ… PyAutoGUI for performing trickier actions
    (eg. drag-and-drop with a real mouse).

    โœ… Xvfb integration for using headed browsers
    on Linux systems that don't have a GUI.


We can use all that to bypass different CAPTCHAs!

Get ready for some live demos of that!

We can use all that to bypass different CAPTCHAs!

Get ready for some live demos of that!


๐Ÿ”น One method call does it all: ๐Ÿ”น




    โœ… sb.solve_captcha()






๐Ÿ”น One method call does it all: ๐Ÿ”น




    โœ… sb.solve_captcha()





sb.solve_captcha() handles all of these:



    โœ… Cloudflare Turnstile

    โœ… Friendly Captcha

    โœ… DataDome Slider Captcha

    โœ… Imperva-based hCaptcha


sb.solve_captcha() handles all of these:



    โœ… Cloudflare Turnstile

    โœ… Friendly Captcha

    โœ… DataDome Slider Captcha

    โœ… Imperva-based hCaptcha


sb.solve_captcha() handles all of these:



    โœ… Cloudflare Turnstile

    โœ… Friendly Captcha

    โœ… DataDome Slider Captcha

    โœ… Imperva-based hCaptcha


sb.solve_captcha() handles all of these:



    โœ… Cloudflare Turnstile

    โœ… Friendly Captcha

    โœ… DataDome Slider Captcha

    โœ… Imperva-based hCaptcha


sb.solve_captcha() handles all of these:



    โœ… Cloudflare Turnstile

    โœ… Friendly Captcha

    โœ… DataDome Slider Captcha

    โœ… Imperva-based hCaptcha


It may seem a bit odd or illegal that
we're bypassing all these CAPTCHAs...


but things may not actually be as they seem...
It may seem a bit odd or illegal that
we're bypassing all these CAPTCHAs...

but things may not actually be as they seem...

Important Notice:

(Know the laws and legal implications of scraping!)

Important Notice:

(Know the laws and legal implications of scraping!)
Now that we all know scraping public data is legal,

let's go web-scraping on Facebook's public pages...
Now that we all know scraping public data is legal,

let's go web-scraping on Facebook's public pages...
If a site wants to hide its public data from scrapers,

then it'll need to hide that data from public view...
If a site wants to hide its public data from scrapers,

then it'll need to hide that data from public view...
Note that if automation performs actions too quickly,
websites may detect this as bot-traffic and block you...


To avoid this:

โœ… Space out your actions so that the
automation moves at human-like speed.

โœ… Random sleep() calls can help.
Note that if automation performs actions too quickly,
websites may detect this as bot-traffic and block you...

To avoid this:

โœ… Space out your actions so that the
automation moves at human-like speed.

โœ… Random sleep() calls can help.
Note that if automation performs actions too quickly,
websites may detect this as bot-traffic and block you...

To avoid this:

โœ… Space out your actions so that the
automation moves at human-like speed.


โœ… Random sleep() calls can help.
Note that if automation performs actions too quickly,
websites may detect this as bot-traffic and block you...

To avoid this:

โœ… Space out your actions so that the
automation moves at human-like speed.

โœ… Random sleep() calls can help.

This may happen when some anti-bots detect you:

If that happens, your bot might not get a chance
to solve a CAPTCHA to prove its humanity.


In other cases, your bot may face a challenge...

If that happens, your bot might not get a chance
to solve a CAPTCHA to prove its humanity.


In other cases, your bot may face a challenge...

This is what happens if hCAPTCHA detects bots:

This is what happens if reCAPTCHA detects bots:

And this is what happens when Gandalf blocks you:

Get ready for a web-scraping
live demo on Amazon.com ...

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's take a look at that Amazon-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, ad_block=True) as sb:
    url = "https://www.amazon.com"
    sb.activate_cdp_mode(url)
    sb.sleep(2)
    sb.click_if_visible('button[alt="Continue shopping"]')
    sb.sleep(2)
    sb.press_keys('input[role="searchbox"]', "TI-89\n")
    sb.sleep(3)
    for i in range(16):
        sb.scroll_down(16)
    print(sb.get_page_title())
    sb.save_as_pdf_to_logs()
    sb.save_page_source_to_logs()
    sb.save_screenshot_to_logs()
    print("Logs have been saved to: ./latest_logs/")

Let's run two live demos on Nike.com:



    1. Using Regular Chrome.

    2. Using Headless Chrome.


Let's run two live demos on Nike.com:



    1. Using Regular Chrome.

    2. Using Headless Chrome.


Let's run two live demos on Nike.com:



    1. Using Regular Chrome.

    2. Using Headless Chrome.


Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Let's take a look at that Nike-scraping script:


from seleniumbase import SB

with SB(uc=True, test=True, locale="en", pls="none") as sb:
    url = "https://www.nike.com/"
    sb.activate_cdp_mode(url)
    sb.sleep(2.5)
    sb.click('[data-testid="user-tools-container"] search')
    sb.sleep(1.5)
    search = "Nike Air Force 1"
    sb.press_keys('input[type="search"]', search)
    sb.sleep(4)
    details = 'ul[data-testid*="products"] figure .details'
    elements = sb.select_all(details)
    if elements:
        print('**** Found results for "%s": ****' %% search)
    for element in elements:
        print("* " + element.text)

Here's a friendly reminder that
those sb.sleep() calls are strategic.

Automating too quickly can get you into
trouble on sites with bot-protection...

Here's a friendly reminder that
those sb.sleep() calls are strategic.

Automating too quickly can get you into
trouble on sites with bot-protection...

Sending too much traffic to a website from the same
IP address could also stop your automation runners.

And some IP ranges are flagged by anti-bot services.


โ›” Eg. "Non-residential" IP ranges (such as AWS)

And some IP ranges are flagged by anti-bot services.


โ›” Eg. "Non-residential" IP ranges (such as AWS)

Scraping a site from a non-residential
IP range is a sure way to get caught...

This would not be a real video about
web-scraping unless I mentioned proxies.

SeleniumBase has a proxy option for that.

This would not be a real video about
web-scraping unless I mentioned proxies.

SeleniumBase has a proxy option for that.

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")

Here's how to configure a proxy with SeleniumBase:



  • Proxy via command-line:
    pytest --proxy="host:port"
    pytest --proxy="user:pass@host:port"

  • Proxy via method arg:
    SB(proxy="host:port")
    SB(proxy="user:pass@host:port")
If you don't have a proxy, it's easy to get one.
Lots of providers out there, like Bright Data.
(Bright Data blogs about SeleniumBase a lot.)

You could also launch your own
proxy server with SeleniumBase:

"sbase proxy"

(That's it!)

You could also launch your own
proxy server with SeleniumBase:

"sbase proxy"

(That's it!)

You could also launch your own
proxy server with SeleniumBase:

"sbase proxy"

(That's it!)

More configuration options for "sbase proxy":

Since websites can detect your geolocation,
it may be a good idea to change it at times.

Fortunately, changing your geolocation/timezone
can be done faster than changing your hair color.

Since websites can detect your geolocation,
it may be a good idea to change it at times.

Fortunately, changing your geolocation/timezone
can be done faster than changing your hair color.

๐ŸŒ Here's how to configure those with SeleniumBase:



  • Changing geolocation via method arg:
    geoloc=(26.863, 80.94)
    geoloc=(35.050681, 136.844728)

  • Changing timezone via method arg:
    tzone="Asia/Kolkata"
    tzone="Asia/Tokyo"

๐ŸŒ Here's how to configure those with SeleniumBase:



  • Changing geolocation via method arg:
    geoloc=(26.863, 80.94)
    geoloc=(35.050681, 136.844728)

  • Changing timezone via method arg:
    tzone="Asia/Kolkata"
    tzone="Asia/Tokyo"

๐ŸŒ Here's how to configure those with SeleniumBase:



  • Changing geolocation via method arg:
    geoloc=(26.863, 80.94)
    geoloc=(35.050681, 136.844728)

  • Changing timezone via method arg:
    tzone="Asia/Kolkata"
    tzone="Asia/Tokyo"

๐ŸŒ Timezone/Geolocation ๐ŸŒ


Let's run a live demo to show how that works:



    1. First we'll go to India.

    2. Then we'll go to Japan.


๐ŸŒ Timezone/Geolocation ๐ŸŒ


Let's run a live demo to show how that works:



    1. First we'll go to India.

    2. Then we'll go to Japan.


๐ŸŒ Timezone/Geolocation ๐ŸŒ


Let's run a live demo to show how that works:



    1. First we'll go to India.

    2. Then we'll go to Japan.


Another cool feature of CDP is the ability
to intercept & modify requests in real time.

Time for a live demo of that...


(Intercepting/modifying requests)
As you can see, SeleniumBase has all
the CDP features you're looking for.

If you're looking for powerful multi-threading,
then ThreadPoolExecutor has you covered.

Sometimes you can defeat CAPTCHAs in advance
if you already have the required cookies loaded
in your web browser. (Eg: cf_clearance)

Getting the cf_clearance cookie is easy:

Just go to a CF-protected site and take it!

Getting the cf_clearance cookie is easy:

Just go to a CF-protected site and take it!

Get ready for a live demo of
stealthy mobile emulation...

For more info on stealthy mobile emulation,
check out the YouTube video on it:

There's also a follow-up mobile video
that deals with changing the User Agent:

SeleniumBase has an option for using unbranded
Chromium in place of regular Google Chrome.



Here's how to use unbranded Chromium in scripts:



  • Chromium via command-line:
    pytest --use-chromium

  • Chromium via method arg:
    SB(use_chromium=True)


(Chromium is automatically downloaded
if it hasn't yet been used by SeleniumBase)

Here's how to use unbranded Chromium in scripts:



  • Chromium via command-line:
    pytest --use-chromium

  • Chromium via method arg:
    SB(use_chromium=True)


(Chromium is automatically downloaded
if it hasn't yet been used by SeleniumBase)

Here's how to use unbranded Chromium in scripts:



  • Chromium via command-line:
    pytest --use-chromium

  • Chromium via method arg:
    SB(use_chromium=True)


(Chromium is automatically downloaded
if it hasn't yet been used by SeleniumBase)

Here's how to use unbranded Chromium in scripts:



  • Chromium via command-line:
    pytest --use-chromium

  • Chromium via method arg:
    SB(use_chromium=True)


(Chromium is automatically downloaded
if it hasn't yet been used by SeleniumBase)

SeleniumBase can download Chromium in advance:



โœ… sbase get chromium

SeleniumBase can download Chromium in advance:



โœ… sbase get chromium

Unlike Google Chrome, unbranded Chromium still
supports loading extensions via command flags.

Additionally, unbranded Chromium may be stealthier
than regular Google Chrome on certain websites.



Get ready for a web-scraping
live demo on Reddit.com ...



Additionally, unbranded Chromium may be stealthier
than regular Google Chrome on certain websites.


Get ready for a web-scraping
live demo on Reddit.com ...



reCAPTCHA comes in many flavors:



Some flavors are stronger than others.
reCAPTCHA comes in many flavors:



Some flavors are stronger than others.

๐Ÿ” Google reCAPTCHA ๐Ÿ”




๐Ÿ” Google reCAPTCHA ๐Ÿ”




reCAPTCHA is very effective at catching bots.






๐Ÿ” Google reCAPTCHA ๐Ÿ”




Even though Google reCAPTCHA looks similar
to Cloudflare Turnstile, they are very different!



This may explain why Google reCAPTCHA is better:

Since Google makes both reCAPTCHA and Chrome, reCAPTCHA has access to more data than other CAPTCHAs, and can therefore detect bots better.



This may explain why Google reCAPTCHA is better:

Since Google makes both reCAPTCHA and Chrome, reCAPTCHA has access to more data than other CAPTCHAs, and can therefore detect bots better.



Many websites have an out-of-date reCAPTCHA:

(Sites like these are vulnerable to web-scrapers)
Many websites have an out-of-date reCAPTCHA:

(Sites like these are vulnerable to web-scrapers)

Get ready for a web-scraping
live demo on Pokemon.com ...

๐Ÿ” Google reCAPTCHA ๐Ÿ”



In the case of encountering an Enterprise V3
reCAPTCHA... that's a very different story...





Note: Calling open(url) from UC Mode
automatically activates CDP Mode now.


    This change was made because the original
    UC Mode wasn't stealthy enough anymore.

    This makes older UC Mode scripts work again.

    That's especially important since out-of-date
    Undetectable Automation videos still get views.

    sb.driver.get(url) keeps you in UC Mode.

Note: Calling open(url) from UC Mode
automatically activates CDP Mode now.


    This change was made because the original
    UC Mode wasn't stealthy enough anymore.


    This makes older UC Mode scripts work again.

    That's especially important since out-of-date
    Undetectable Automation videos still get views.

    sb.driver.get(url) keeps you in UC Mode.

Note: Calling open(url) from UC Mode
automatically activates CDP Mode now.


    This change was made because the original
    UC Mode wasn't stealthy enough anymore.

    This makes older UC Mode scripts work again.

    That's especially important since out-of-date
    Undetectable Automation videos still get views.

    sb.driver.get(url) keeps you in UC Mode.

Note: Calling open(url) from UC Mode
automatically activates CDP Mode now.


    This change was made because the original
    UC Mode wasn't stealthy enough anymore.

    This makes older UC Mode scripts work again.

    That's especially important since out-of-date
    Undetectable Automation videos still get views.

    sb.driver.get(url) keeps you in UC Mode.

Note: Calling open(url) from UC Mode
automatically activates CDP Mode now.


    This change was made because the original
    UC Mode wasn't stealthy enough anymore.

    This makes older UC Mode scripts work again.

    That's especially important since out-of-date
    Undetectable Automation videos still get views.

    sb.driver.get(url) keeps you in UC Mode.

Naming conventions used in the SeleniumBase repo:



    โœ… If a file starts with "test_" or ends with
    "_test.py", then it runs with "pytest".

    โœ… If a file starts with "raw_",
    then it runs directly with raw "python".

    โœ… (Note that SeleniumBase was originally just a test framework before it gained stealth abilities.)

Naming conventions used in the SeleniumBase repo:



    โœ… If a file starts with "test_" or ends with
    "_test.py", then it runs with "pytest".


    โœ… If a file starts with "raw_",
    then it runs directly with raw "python".

    โœ… (Note that SeleniumBase was originally just a test framework before it gained stealth abilities.)

Naming conventions used in the SeleniumBase repo:



    โœ… If a file starts with "test_" or ends with
    "_test.py", then it runs with "pytest".

    โœ… If a file starts with "raw_",
    then it runs directly with raw "python".


    โœ… (Note that SeleniumBase was originally just a test framework before it gained stealth abilities.)

Naming conventions used in the SeleniumBase repo:



    โœ… If a file starts with "test_" or ends with
    "_test.py", then it runs with "pytest".

    โœ… If a file starts with "raw_",
    then it runs directly with raw "python".

    โœ… (Note that SeleniumBase was originally just a test framework before it gained stealth abilities.)

Finding stealthy examples in the SeleniumBase repo:



โœ… See "SeleniumBase/examples/cdp_mode/"
for all examples made for stealth & CAPTCHAs.

โœ… For the Stealthy Playwright Mode examples, see:
"SeleniumBase/examples/cdp_mode/playwright/"

Finding stealthy examples in the SeleniumBase repo:



โœ… See "SeleniumBase/examples/cdp_mode/"
for all examples made for stealth & CAPTCHAs.


โœ… For the Stealthy Playwright Mode examples, see:
"SeleniumBase/examples/cdp_mode/playwright/"

Finding stealthy examples in the SeleniumBase repo:



โœ… See "SeleniumBase/examples/cdp_mode/"
for all examples made for stealth & CAPTCHAs.

โœ… For the Stealthy Playwright Mode examples, see:
"SeleniumBase/examples/cdp_mode/playwright/"

Let's make a quick trip to the SeleniumBase repo:


It's time for more CAPTCHA-bypass!

Get ready for a live demo of
bypassing a Cloudflare CAPTCHA
on Cloudflare's own login page ...


It's time for more CAPTCHA-bypass!

Get ready for a live demo of
bypassing a Cloudflare CAPTCHA
on Cloudflare's own login page ...


If bypassing CAPTCHAs isn't exciting enough for you,
then treat every CAPTCHA-bypass like scoring a goal...

๐Ÿค– You've reached the AI section of this video! ๐Ÿค–



๐Ÿค– I've actually been using a lot of AI already! ๐Ÿค–



โœ… Many illustrations were AI-generated.

๐Ÿค– I've actually been using a lot of AI already! ๐Ÿค–



โœ… Many illustrations were AI-generated.

๐Ÿค– Thanks for all the free artwork, Gemini! ๐Ÿค–


Let's use Gemini's power to bypass CAPTCHAs!



Every year the AI models get better and better



Definitely an improvement over last year's models


I also covered AI in my earlier video:

Can AI tools help you with web-scraping & CAPTCHA-bypass?



(Spoiler alert: It depends on which AI you ask!)

I also covered AI in my earlier video:

Can AI tools help you with web-scraping & CAPTCHA-bypass?



(Spoiler alert: It depends on which AI you ask!)

I also covered AI in my earlier video:

Can AI tools help you with web-scraping & CAPTCHA-bypass?



(Spoiler alert: It depends on which AI you ask!)

It's time for the next live demo!




For that, we're scraping LinkedIn...

It's time for the next live demo!




For that, we're scraping LinkedIn...

As you can see ...



SeleniumBase works well for web-scraping.

As you can see ...



SeleniumBase works well for web-scraping.

If you need help creating scripts,
just ask the AI! (maybe Gemini)



โœ… Or you can replicate a bunch of
AI agents to do the work for you...

If you need help creating scripts,
just ask the AI! (maybe Gemini)



โœ… Or you can replicate a bunch of
AI agents to do the work for you...

โ“ Questions? โ“

https://github.com/seleniumbase/SeleniumBase/discussions


๐Ÿ“Œ Found a bug? ๐Ÿž

https://github.com/seleniumbase/SeleniumBase/issues

โ“ Questions? โ“

https://github.com/seleniumbase/SeleniumBase/discussions


๐Ÿ“Œ Found a bug? ๐Ÿž

https://github.com/seleniumbase/SeleniumBase/issues

๐Ÿ“Š Final remarks ๐Ÿ“ฃ



๐Ÿ› ๏ธ SeleniumBase gives you ๐Ÿ› ๏ธ
the tools you need to succeed!


And tools to build lots of bots...


๐Ÿ“Š Final remarks ๐Ÿ“ฃ



๐Ÿ› ๏ธ SeleniumBase gives you ๐Ÿ› ๏ธ
the tools you need to succeed!


And tools to build lots of bots...


๐Ÿ“Š Final remarks ๐Ÿ“ฃ



๐Ÿ› ๏ธ SeleniumBase gives you ๐Ÿ› ๏ธ
the tools you need to succeed!


And tools to build lots of bots...


๐Ÿ The End ๐Ÿ