Compare commits
No commits in common. "master" and "v4.36.4" have entirely different histories.
|
@ -13,7 +13,7 @@
|
|||
|
||||
<p align="center" class="hero__title"><b>All-in-one Browser Automation Framework:<br />Web Crawling / Testing / Scraping / Stealth</b></p>
|
||||
|
||||
<p align="center"><a href="https://pypi.python.org/pypi/seleniumbase" target="_blank"><img src="https://img.shields.io/pypi/v/seleniumbase.svg?color=3399EE" alt="PyPI version" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/releases" target="_blank"><img src="https://img.shields.io/github/v/release/seleniumbase/SeleniumBase.svg?color=22AAEE" alt="GitHub version" /></a> <a href="https://seleniumbase.io"><img src="https://img.shields.io/badge/docs-seleniumbase.io-11BBAA.svg" alt="SeleniumBase Docs" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/actions" target="_blank"><img src="https://github.com/seleniumbase/SeleniumBase/workflows/CI%20build/badge.svg" alt="SeleniumBase GitHub Actions" /></a> <a href="https://discord.gg/EdhQTn3EyE" target="_blank"><img src="https://img.shields.io/discord/727927627830001734?color=7289DA&label=Discord&logo=discord&logoColor=white"/></a></p>
|
||||
<p align="center"><a href="https://pypi.python.org/pypi/seleniumbase" target="_blank"><img src="https://img.shields.io/pypi/v/seleniumbase.svg?color=3399EE" alt="PyPI version" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/releases" target="_blank"><img src="https://img.shields.io/github/v/release/seleniumbase/SeleniumBase.svg?color=22AAEE" alt="GitHub version" /></a> <a href="https://seleniumbase.io"><img src="https://img.shields.io/badge/docs-seleniumbase.io-11BBAA.svg" alt="SeleniumBase Docs" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/actions" target="_blank"><img src="https://github.com/seleniumbase/SeleniumBase/workflows/CI%20build/badge.svg" alt="SeleniumBase GitHub Actions" /></a> <a href="https://discord.gg/EdhQTn3EyE" target="_blank"><img src="https://img.shields.io/badge/join-discord-infomational" alt="Join the SeleniumBase chat on Discord"/></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#python_installation">🚀 Start</a> |
|
||||
|
@ -1391,6 +1391,6 @@ pytest --reruns=1 --reruns-delay=1
|
|||
<div><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/cdn/img/super_logo_sb3.png" title="SeleniumBase" width="274" /></a></div>
|
||||
<div><a href="https://seleniumbase.io"><img src="https://img.shields.io/badge/docs-seleniumbase.io-11BBAA.svg" alt="SeleniumBase Docs" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-22BBCC.svg" title="SeleniumBase" /></a></div>
|
||||
<div><a href="https://github.com/seleniumbase/SeleniumBase"><img src="https://img.shields.io/badge/tested%20with-SeleniumBase-04C38E.svg" alt="Tested with SeleniumBase" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/stargazers"><img src="https://img.shields.io/github/stars/seleniumbase/seleniumbase.svg?color=19A57B" title="Stargazers" /></a></div>
|
||||
<div><a href="https://hellogithub.com/repository/c6be2d0f1969448697683d11a4ff915e" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=c6be2d0f1969448697683d11a4ff915e&claim_uid=xcrm4p9j3d6JCO5&theme=small" alt="Featured|HelloGitHub" /></a> <a href="https://discord.gg/EdhQTn3EyE" target="_blank"><img src="https://img.shields.io/discord/727927627830001734?color=7289DA&label=Discord&logo=discord&logoColor=white"/></a></div>
|
||||
<div><a href="https://hellogithub.com/repository/c6be2d0f1969448697683d11a4ff915e" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=c6be2d0f1969448697683d11a4ff915e&claim_uid=xcrm4p9j3d6JCO5&theme=small" alt="Featured|HelloGitHub" /></a> <a href="https://discord.gg/EdhQTn3EyE" target="_blank"><img src="https://img.shields.io/badge/join-discord-infomational" alt="Join the SeleniumBase chat on Discord"/></a> <a href="https://gitter.im/seleniumbase/SeleniumBase" target="_blank"><img src="https://img.shields.io/gitter/room/seleniumbase/SeleniumBase.svg" alt="Gitter chat"/></a></div>
|
||||
<div><a href="https://pepy.tech/projects/seleniumbase?timeRange=threeMonths&category=version&includeCIDownloads=true&granularity=daily&viewType=line&versions=*" target="_blank"><img src="https://static.pepy.tech/badge/seleniumbase" alt="SeleniumBase PyPI downloads" /></a> <img src="https://views.whatilearened.today/views/github/seleniumbase/SeleniumBase.svg" width="98px" height="20px" alt="Views" /></div>
|
||||
<div align="left"></div>
|
||||
|
|
|
@ -286,7 +286,6 @@ with SB(uc=True, test=True, ad_block=True) as sb:
|
|||
sb.activate_cdp_mode(url)
|
||||
sb.sleep(2.5)
|
||||
sb.cdp.click_if_visible('[data-automation-id*="close-mark"]')
|
||||
sb.sleep(0.3)
|
||||
sb.cdp.mouse_click('input[aria-label="Search"]')
|
||||
sb.sleep(1.2)
|
||||
search = "Settlers of Catan Board Game"
|
||||
|
@ -301,7 +300,7 @@ with SB(uc=True, test=True, ad_block=True) as sb:
|
|||
for item in items:
|
||||
if required_text in item.text:
|
||||
description = item.querySelector(
|
||||
'[data-automation-id="product-title"]'
|
||||
'[data-automation-id="product-price"] + span'
|
||||
)
|
||||
if description and description.text not in unique_item_text:
|
||||
unique_item_text.append(description.text)
|
||||
|
@ -420,7 +419,7 @@ sb.cdp.js_dumps(obj_name)
|
|||
sb.cdp.maximize()
|
||||
sb.cdp.minimize()
|
||||
sb.cdp.medimize()
|
||||
sb.cdp.set_window_rect(x, y, width, height)
|
||||
sb.cdp.set_window_rect()
|
||||
sb.cdp.reset_window_size()
|
||||
sb.cdp.open_new_window(url=None, switch_to=True)
|
||||
sb.cdp.switch_to_window(window)
|
||||
|
@ -466,9 +465,8 @@ sb.cdp.gui_press_keys(keys)
|
|||
sb.cdp.gui_write(text)
|
||||
sb.cdp.gui_click_x_y(x, y)
|
||||
sb.cdp.gui_click_element(selector)
|
||||
sb.cdp.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.35)
|
||||
sb.cdp.gui_drag_and_drop(drag_selector, drop_selector, timeframe=0.35)
|
||||
sb.cdp.gui_click_and_hold(selector, timeframe=0.35)
|
||||
sb.cdp.gui_drag_drop_points(x1, y1, x2, y2)
|
||||
sb.cdp.gui_drag_and_drop(drag_selector, drop_selector)
|
||||
sb.cdp.gui_hover_x_y(x, y)
|
||||
sb.cdp.gui_hover_element(selector)
|
||||
sb.cdp.gui_hover_and_click(hover_selector, click_selector)
|
||||
|
|
|
@ -16,15 +16,6 @@ with SB(uc=True, test=True, locale="en") as sb:
|
|||
sb.sleep(1)
|
||||
sb.cdp.gui_press_keys("\b" * 10 + formatted_date + "\n")
|
||||
sb.sleep(1)
|
||||
days_ahead = (4 - today.weekday() + 8) % 14
|
||||
following_saturday = today + datetime.timedelta(days=days_ahead)
|
||||
formatted_date = following_saturday.strftime("%m/%d/%Y")
|
||||
sb.cdp.gui_click_element(
|
||||
'[data-att="end-date-toggler"] [aria-describedby*="date-input"]'
|
||||
)
|
||||
sb.sleep(1)
|
||||
sb.cdp.gui_press_keys("\b" * 10 + formatted_date + "\n")
|
||||
sb.sleep(1)
|
||||
sb.cdp.click('button[data-att="done"]')
|
||||
sb.sleep(1)
|
||||
sb.cdp.click('button[data-att="search"]')
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
"""Geolocation example using CDP Mode without WebDriver"""
|
||||
from seleniumbase import decorators
|
||||
from seleniumbase import sb_cdp
|
||||
|
||||
|
||||
@decorators.print_runtime("Geolocation CDP Example")
|
||||
def main():
|
||||
url = "https://www.openstreetmap.org/"
|
||||
sb = sb_cdp.Chrome(url, geoloc=(48.87645, 2.26340))
|
||||
sb.click("span.geolocate")
|
||||
sb.assert_url_contains("48.876450/2.263400")
|
||||
sb.sleep(5)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -3,7 +3,6 @@ from seleniumbase import SB
|
|||
with SB(uc=True, test=True, ad_block=True) as sb:
|
||||
url = "https://www.glassdoor.com/Reviews/index.htm"
|
||||
sb.activate_cdp_mode(url)
|
||||
sb.sleep(2)
|
||||
sb.uc_gui_click_captcha()
|
||||
sb.highlight('[data-test="global-nav-glassdoor-logo"]')
|
||||
sb.highlight('[data-test="site-header-companies"]')
|
||||
|
|
|
@ -2,20 +2,12 @@ from seleniumbase import SB
|
|||
|
||||
with SB(uc=True, incognito=True, test=True) as sb:
|
||||
sb.activate_cdp_mode("https://pixelscan.net/")
|
||||
sb.sleep(2)
|
||||
sb.click('button[class*="startButton"]')
|
||||
sb.sleep(6)
|
||||
sb.remove_elements(".bg-bannerBg") # Remove top banner
|
||||
sb.remove_elements("pxlscn-ad1") # Remove an ad banner
|
||||
sb.remove_elements("pxlscn-ad2") # Remove an ad banner
|
||||
sb.sleep(3)
|
||||
sb.remove_elements(".bg-bannerBg") # Remove the top banner
|
||||
sb.remove_elements("pxlscn-ad1") # Remove the ad banner
|
||||
sb.remove_elements("jdiv") # Remove chat widgets
|
||||
sb.sleep(14)
|
||||
not_masking_text = "You are not masking your fingerprint"
|
||||
sb.assert_text(
|
||||
not_masking_text,
|
||||
"pxlscn-fingerprint-masking",
|
||||
timeout=20,
|
||||
)
|
||||
sb.assert_text(not_masking_text, "pxlscn-fingerprint-masking")
|
||||
no_automation_detected = "No automation framework detected"
|
||||
sb.assert_text(no_automation_detected, "pxlscn-bot-detection")
|
||||
consistent_selector = 'div.bg-consistentBg [alt="Good"]'
|
||||
|
|
|
@ -8,25 +8,28 @@ with SB(uc=True, test=True, ad_block=True, pls="none") as sb:
|
|||
sb.uc_gui_click_captcha()
|
||||
sb.sleep(0.5)
|
||||
channel_name = "michaelmintz"
|
||||
channel_title = "Michael Mintz"
|
||||
sb.cdp.press_keys('input[placeholder*="Search"]', channel_name)
|
||||
sb.sleep(1.5)
|
||||
sb.cdp.click('a:contains("%s")' % channel_title)
|
||||
sb.cdp.press_keys('input[name="query"]', channel_name)
|
||||
sb.cdp.click('form[action*="/search"] button')
|
||||
sb.sleep(2)
|
||||
sb.cdp.click('a[title="%s"] h2' % channel_name)
|
||||
sb.sleep(1.5)
|
||||
sb.cdp.remove_elements("#lngtd-top-sticky")
|
||||
sb.sleep(1.5)
|
||||
name = sb.cdp.get_text("h1")
|
||||
source = sb.get_page_source()
|
||||
base = "https://www.youtube.com/c/"
|
||||
base2 = 'href="/youtube/c/'
|
||||
start = source.find(base2) + len(base2)
|
||||
end = source.find('"', start)
|
||||
link = base + source[start:end]
|
||||
link = sb.cdp.get_attribute("#YouTubeUserTopInfoBlockTop h4 a", "href")
|
||||
subscribers = sb.cdp.get_text("#youtube-stats-header-subs")
|
||||
video_views = sb.cdp.get_text("#youtube-stats-header-views")
|
||||
rankings = sb.cdp.get_text(
|
||||
'#socialblade-user-content [style*="border-bottom"]'
|
||||
).replace("\xa0", "").replace(" ", " ").replace(" ", " ")
|
||||
print("********** SocialBlade Stats for %s: **********" % name)
|
||||
print(">>> (Link: %s) <<<" % link)
|
||||
print(sb.get_text('[class*="grid lg:hidden"]'))
|
||||
print("* YouTube Subscribers: %s" % subscribers)
|
||||
print("* YouTube Video Views: %s" % video_views)
|
||||
print("********** SocialBlade Ranks: **********")
|
||||
print(sb.get_text('[class*="gap-3 flex-1"]'))
|
||||
for row in rankings.split("\n"):
|
||||
if len(row.strip()) > 8:
|
||||
print("--> " + row.strip())
|
||||
for i in range(17):
|
||||
sb.cdp.scroll_down(6)
|
||||
sb.sleep(0.1)
|
||||
|
|
|
@ -5,7 +5,6 @@ with SB(uc=True, test=True, locale="en", ad_block=True) as sb:
|
|||
url = "https://architectureofcities.com/roman-theaters"
|
||||
sb.activate_cdp_mode(url)
|
||||
sb.cdp.click_if_visible("#cn-close-notice")
|
||||
sb.cdp.click_if_visible('span:contains("Continue")')
|
||||
sb.sleep(1)
|
||||
print("*** " + sb.cdp.get_text("h1") + " ***")
|
||||
for item in sb.cdp.find_elements("h3"):
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
"""Timezone example using CDP Mode without WebDriver"""
|
||||
import mycdp
|
||||
from seleniumbase import decorators
|
||||
from seleniumbase import sb_cdp
|
||||
|
||||
|
||||
async def request_paused_handler(event, tab):
|
||||
r = event.request
|
||||
is_image = ".png" in r.url or ".jpg" in r.url or ".gif" in r.url
|
||||
if not is_image: # Let the data through
|
||||
tab.feed_cdp(mycdp.fetch.continue_request(request_id=event.request_id))
|
||||
else: # Block the data (images)
|
||||
TIMED_OUT = mycdp.network.ErrorReason.TIMED_OUT
|
||||
tab.feed_cdp(mycdp.fetch.fail_request(event.request_id, TIMED_OUT))
|
||||
|
||||
|
||||
@decorators.print_runtime("Timezone CDP Example")
|
||||
def main():
|
||||
url = "https://www.randymajors.org/what-time-zone-am-i-in"
|
||||
sb = sb_cdp.Chrome(
|
||||
url,
|
||||
ad_block=True,
|
||||
lang="bn",
|
||||
tzone="Asia/Kolkata",
|
||||
geoloc=(26.855323, 80.937710)
|
||||
)
|
||||
sb.add_handler(mycdp.fetch.RequestPaused, request_paused_handler)
|
||||
sb.remove_elements("#right-sidebar")
|
||||
sb.remove_elements('[id*="Footer"]')
|
||||
sb.sleep(6)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -5,7 +5,6 @@ with SB(uc=True, test=True, ad_block=True) as sb:
|
|||
sb.activate_cdp_mode(url)
|
||||
sb.sleep(2.5)
|
||||
sb.cdp.click_if_visible('[data-automation-id*="close-mark"]')
|
||||
sb.sleep(0.3)
|
||||
sb.cdp.mouse_click('input[aria-label="Search"]')
|
||||
sb.sleep(1.2)
|
||||
search = "Settlers of Catan Board Game"
|
||||
|
@ -20,7 +19,7 @@ with SB(uc=True, test=True, ad_block=True) as sb:
|
|||
for item in items:
|
||||
if required_text in item.text:
|
||||
description = item.querySelector(
|
||||
'[data-automation-id="product-title"]'
|
||||
'[data-automation-id="product-price"] + span'
|
||||
)
|
||||
if description and description.text not in unique_item_text:
|
||||
unique_item_text.append(description.text)
|
||||
|
|
|
@ -521,7 +521,6 @@ class UCPresentationClass(BaseCase):
|
|||
sb.activate_cdp_mode(url)
|
||||
sb.sleep(2.5)
|
||||
sb.cdp.click_if_visible('[data-automation-id*="close-mark"]')
|
||||
sb.sleep(0.3)
|
||||
sb.cdp.mouse_click('input[aria-label="Search"]')
|
||||
sb.sleep(1.2)
|
||||
search = "Settlers of Catan Board Game"
|
||||
|
@ -536,7 +535,7 @@ class UCPresentationClass(BaseCase):
|
|||
for item in items:
|
||||
if required_text in item.text:
|
||||
description = item.querySelector(
|
||||
'[data-automation-id="product-title"]'
|
||||
'[data-automation-id="product-price"] + span'
|
||||
)
|
||||
if (
|
||||
description
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from contextlib import suppress
|
||||
from seleniumbase import BaseCase
|
||||
BaseCase.main(__name__, __file__)
|
||||
|
||||
|
@ -8,54 +7,6 @@ class UCPresentationClass(BaseCase):
|
|||
self.open("data:,")
|
||||
self.set_window_position(4, 40)
|
||||
self._output_file_saves = False
|
||||
self.create_presentation(theme="serif", transition="none")
|
||||
self.add_slide("<h2>Press SPACE to continue!</h2>\n")
|
||||
self.add_slide(
|
||||
"<h3><b>Before we begin</b></h3><hr />"
|
||||
"<p><b>(Here's the GitHub page)</b></p>",
|
||||
image="https://seleniumbase.io/other/sbase_qr_code.png",
|
||||
)
|
||||
self.begin_presentation(filename="uc_presentation.html")
|
||||
|
||||
with suppress(Exception):
|
||||
self.open("https://www.bostoncodecamp.com/CC38/info")
|
||||
self.create_tour(theme="hopscotch")
|
||||
self.add_tour_step(
|
||||
"<h2>Good Afternoon and Welcome!</h2>", 'h1.wow'
|
||||
)
|
||||
self.add_tour_step(
|
||||
"<h4>PSA: Visit our sponsors later.</h4>",
|
||||
'[href*="/Sponsors"]',
|
||||
)
|
||||
self.add_tour_step(
|
||||
"<h4>Let's check out the schedule...</h4>",
|
||||
'[href*="/Schedule/SessionGrid"]'
|
||||
)
|
||||
self.play_tour()
|
||||
|
||||
with suppress(Exception):
|
||||
self.open(
|
||||
"https://www.bostoncodecamp.com/CC38/Schedule/SessionGrid"
|
||||
)
|
||||
self.highlight("h2", loops=8)
|
||||
if self.is_element_visible('[data-sessionid="869465"]'):
|
||||
self.highlight(
|
||||
'div[data-sessionid="869465"]', loops=10, scroll=False
|
||||
)
|
||||
self.create_tour(theme="driverjs")
|
||||
self.add_tour_step(
|
||||
"<h2>Here we are</h2>", '[data-sessionid="869465"]'
|
||||
)
|
||||
self.play_tour()
|
||||
self.click('a[onclick*="869465"]')
|
||||
self.create_tour(theme="hopscotch")
|
||||
self.add_tour_step(
|
||||
"<h2>What to expect</h2>",
|
||||
"div.sz-modal-session",
|
||||
alignment="left",
|
||||
)
|
||||
self.play_tour()
|
||||
|
||||
self.create_presentation(theme="serif", transition="none")
|
||||
self.add_slide("<h2>Press SPACE to begin!</h2>\n")
|
||||
self.add_slide(
|
||||
|
@ -293,9 +244,4 @@ class UCPresentationClass(BaseCase):
|
|||
'<img src="https://seleniumbase.io/other/hackers_at_comp.jpg"'
|
||||
' width="70%">'
|
||||
)
|
||||
self.add_slide(
|
||||
"<h3><b>Live Demo Time!</b></h3><hr />"
|
||||
"<h3>(Let's head over to GitHub...)</h3>",
|
||||
image="https://seleniumbase.io/other/sbase_qr_code.png",
|
||||
)
|
||||
self.begin_presentation(filename="uc_presentation.html")
|
||||
|
|
|
@ -6,7 +6,7 @@ try:
|
|||
url = "seleniumbase.io/apps/turnstile"
|
||||
driver.uc_open_with_reconnect(url, 2)
|
||||
driver.uc_gui_handle_captcha()
|
||||
driver.sleep(2)
|
||||
driver.sleep(3)
|
||||
pprint(driver.get_log("performance"))
|
||||
finally:
|
||||
driver.quit()
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
from seleniumbase import SB
|
||||
|
||||
with SB(uc=True, incognito=True, test=True) as sb:
|
||||
sb.driver.uc_open_with_reconnect("https://pixelscan.net/", 2)
|
||||
sb.uc_click('button[class*="startButton"]', reconnect_time=20)
|
||||
sb.remove_elements(".bg-bannerBg") # Remove top banner
|
||||
sb.remove_elements("pxlscn-ad1") # Remove an ad banner
|
||||
sb.remove_elements("pxlscn-ad2") # Remove an ad banner
|
||||
sb.driver.uc_open_with_reconnect("https://pixelscan.net/", 7)
|
||||
sb.remove_elements(".bg-bannerBg") # Remove the top banner
|
||||
sb.remove_elements("pxlscn-ad1") # Remove the ad banner
|
||||
sb.remove_elements("jdiv") # Remove chat widgets
|
||||
no_automation_detected = "No automation framework detected"
|
||||
sb.assert_text(
|
||||
no_automation_detected,
|
||||
"pxlscn-bot-detection",
|
||||
timeout=20,
|
||||
)
|
||||
sb.assert_text(no_automation_detected, "pxlscn-bot-detection")
|
||||
not_masking_text = "You are not masking your fingerprint"
|
||||
sb.assert_text(not_masking_text, "pxlscn-fingerprint-masking")
|
||||
consistent_selector = 'div.bg-consistentBg [alt="Good"]'
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
regex>=2024.11.6
|
||||
pymdown-extensions>=10.14.3
|
||||
pipdeptree>=2.26.1
|
||||
pipdeptree>=2.26.0
|
||||
python-dateutil>=2.8.2
|
||||
Markdown==3.8
|
||||
Markdown==3.7
|
||||
click==8.1.8
|
||||
ghp-import==2.1.0
|
||||
watchdog==6.0.0
|
||||
|
@ -14,7 +14,7 @@ pathspec==0.12.1
|
|||
Babel==2.17.0
|
||||
paginate==0.5.7
|
||||
mkdocs==1.6.1
|
||||
mkdocs-material==9.6.12
|
||||
mkdocs-material==9.6.9
|
||||
mkdocs-exclude-search==0.6.6
|
||||
mkdocs-simple-hooks==0.1.5
|
||||
mkdocs-material-extensions==1.3.1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pip>=25.0.1
|
||||
packaging>=25.0
|
||||
packaging>=24.2
|
||||
setuptools~=70.2;python_version<"3.10"
|
||||
setuptools>=79.0.1;python_version>="3.10"
|
||||
setuptools>=78.1.0;python_version>="3.10"
|
||||
wheel>=0.45.1
|
||||
attrs>=25.3.0
|
||||
certifi>=2025.1.31
|
||||
|
@ -11,11 +11,11 @@ websockets>=15.0.1;python_version>="3.9"
|
|||
filelock~=3.16.1;python_version<"3.9"
|
||||
filelock>=3.18.0;python_version>="3.9"
|
||||
fasteners>=0.19
|
||||
mycdp>=1.2.0
|
||||
mycdp>=1.1.1
|
||||
pynose>=1.5.4
|
||||
platformdirs>=4.3.6;python_version<"3.9"
|
||||
platformdirs>=4.3.7;python_version>="3.9"
|
||||
typing-extensions>=4.13.2
|
||||
typing-extensions>=4.13.0
|
||||
sbvirtualdisplay>=1.4.0
|
||||
MarkupSafe==2.1.5;python_version<"3.9"
|
||||
MarkupSafe>=3.0.2;python_version>="3.9"
|
||||
|
@ -33,18 +33,18 @@ idna==3.10
|
|||
chardet==5.2.0
|
||||
charset-normalizer==3.4.1
|
||||
urllib3>=1.26.20,<2;python_version<"3.10"
|
||||
urllib3>=1.26.20,<2.5.0;python_version>="3.10"
|
||||
urllib3>=1.26.20,<2.4.0;python_version>="3.10"
|
||||
requests==2.32.3
|
||||
sniffio==1.3.1
|
||||
h11==0.16.0
|
||||
h11==0.14.0
|
||||
outcome==1.3.0.post0
|
||||
trio==0.27.0;python_version<"3.9"
|
||||
trio==0.30.0;python_version>="3.9"
|
||||
trio==0.29.0;python_version>="3.9"
|
||||
trio-websocket==0.12.2
|
||||
wsproto==1.2.0
|
||||
websocket-client==1.8.0
|
||||
selenium==4.27.1;python_version<"3.9"
|
||||
selenium==4.31.0;python_version>="3.9"
|
||||
selenium==4.30.0;python_version>="3.9"
|
||||
cssselect==1.2.0;python_version<"3.9"
|
||||
cssselect==1.3.0;python_version>="3.9"
|
||||
sortedcontainers==2.4.0
|
||||
|
@ -60,25 +60,25 @@ pytest-rerunfailures==15.0;python_version>="3.9"
|
|||
pytest-xdist==3.6.1
|
||||
parameterized==0.9.0
|
||||
behave==1.2.6
|
||||
soupsieve==2.7
|
||||
beautifulsoup4==4.13.4
|
||||
soupsieve==2.6
|
||||
beautifulsoup4==4.13.3
|
||||
pyotp==2.9.0
|
||||
python-xlib==0.33;platform_system=="Linux"
|
||||
markdown-it-py==3.0.0
|
||||
mdurl==0.1.2
|
||||
rich>=14.0.0,<15
|
||||
rich==13.9.4
|
||||
|
||||
# --- Testing Requirements --- #
|
||||
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
|
||||
|
||||
coverage>=7.6.1;python_version<"3.9"
|
||||
coverage>=7.8.0;python_version>="3.9"
|
||||
coverage>=7.7.1;python_version>="3.9"
|
||||
pytest-cov>=5.0.0;python_version<"3.9"
|
||||
pytest-cov>=6.1.1;python_version>="3.9"
|
||||
pytest-cov>=6.0.0;python_version>="3.9"
|
||||
flake8==5.0.4;python_version<"3.9"
|
||||
flake8==7.2.0;python_version>="3.9"
|
||||
flake8==7.1.2;python_version>="3.9"
|
||||
mccabe==0.7.0
|
||||
pyflakes==2.5.0;python_version<"3.9"
|
||||
pyflakes==3.3.2;python_version>="3.9"
|
||||
pyflakes==3.2.0;python_version>="3.9"
|
||||
pycodestyle==2.9.1;python_version<"3.9"
|
||||
pycodestyle==2.13.0;python_version>="3.9"
|
||||
pycodestyle==2.12.1;python_version>="3.9"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# seleniumbase package
|
||||
__version__ = "4.37.7"
|
||||
__version__ = "4.36.4"
|
||||
|
|
|
@ -682,7 +682,6 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|||
cdp.gui_click_element = CDPM.gui_click_element
|
||||
cdp.gui_drag_drop_points = CDPM.gui_drag_drop_points
|
||||
cdp.gui_drag_and_drop = CDPM.gui_drag_and_drop
|
||||
cdp.gui_click_and_hold = CDPM.gui_click_and_hold
|
||||
cdp.gui_hover_x_y = CDPM.gui_hover_x_y
|
||||
cdp.gui_hover_element = CDPM.gui_hover_element
|
||||
cdp.gui_hover_and_click = CDPM.gui_hover_and_click
|
||||
|
|
|
@ -92,5 +92,5 @@ class S3LoggingBucket(object):
|
|||
"""Keep a record of all file names that have been uploaded.
|
||||
Upload log files related to each test after its execution.
|
||||
Once done, use already_uploaded_files to create an index file."""
|
||||
global already_uploaded_files # noqa
|
||||
global already_uploaded_files
|
||||
already_uploaded_files.extend(files)
|
||||
|
|
|
@ -1227,20 +1227,12 @@ class CDPMethods():
|
|||
if not timeout:
|
||||
timeout = settings.SMALL_TIMEOUT
|
||||
selector = self.__convert_to_css_if_xpath(selector)
|
||||
element = self.select(selector, timeout=timeout)
|
||||
self.select(selector, timeout=timeout)
|
||||
self.__add_light_pause()
|
||||
coordinates = None
|
||||
if ":contains(" in selector:
|
||||
position = element.get_position()
|
||||
x = position.x
|
||||
y = position.y
|
||||
width = position.width
|
||||
height = position.height
|
||||
coordinates = {"x": x, "y": y, "width": width, "height": height}
|
||||
else:
|
||||
coordinates = self.loop.run_until_complete(
|
||||
self.page.js_dumps(
|
||||
"""document.querySelector('%s').getBoundingClientRect()"""
|
||||
"""document.querySelector"""
|
||||
"""('%s').getBoundingClientRect()"""
|
||||
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
||||
)
|
||||
)
|
||||
|
@ -1622,8 +1614,6 @@ class CDPMethods():
|
|||
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
||||
|
||||
def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35):
|
||||
"""Use PyAutoGUI to drag-and-drop from one point to another.
|
||||
Can simulate click-and-hold when using the same point twice."""
|
||||
gui_lock = fasteners.InterProcessLock(
|
||||
constants.MultiBrowser.PYAUTOGUILOCK
|
||||
)
|
||||
|
@ -1663,8 +1653,6 @@ class CDPMethods():
|
|||
self.loop.run_until_complete(self.page.wait())
|
||||
|
||||
def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35):
|
||||
"""Use PyAutoGUI to drag-and-drop from one selector to another.
|
||||
Can simulate click-and-hold when using the same selector twice."""
|
||||
self.__slow_mode_pause_if_set()
|
||||
self.bring_active_window_to_front()
|
||||
x1, y1 = self.get_gui_element_center(drag_selector)
|
||||
|
@ -1673,14 +1661,6 @@ class CDPMethods():
|
|||
self.__add_light_pause()
|
||||
self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=timeframe)
|
||||
|
||||
def gui_click_and_hold(self, selector, timeframe=0.35):
|
||||
"""Use PyAutoGUI to click-and-hold a selector."""
|
||||
self.__slow_mode_pause_if_set()
|
||||
self.bring_active_window_to_front()
|
||||
x, y = self.get_gui_element_center(selector)
|
||||
self.__add_light_pause()
|
||||
self.gui_drag_drop_points(x, y, x, y, timeframe=timeframe)
|
||||
|
||||
def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False):
|
||||
self.__install_pyautogui_if_missing()
|
||||
import pyautogui
|
||||
|
|
|
@ -162,7 +162,6 @@ class BaseCase(unittest.TestCase):
|
|||
self.__jqc_default_theme = None
|
||||
self.__jqc_default_color = None
|
||||
self.__jqc_default_width = None
|
||||
self.__saved_id = None
|
||||
# Requires self._* instead of self.__* for external class use
|
||||
self._language = "English"
|
||||
self._presentation_slides = {}
|
||||
|
@ -4872,13 +4871,6 @@ class BaseCase(unittest.TestCase):
|
|||
|
||||
def activate_cdp_mode(self, url=None):
|
||||
if hasattr(self.driver, "_is_using_uc") and self.driver._is_using_uc:
|
||||
if self.__is_cdp_swap_needed():
|
||||
return # CDP Mode is already active
|
||||
if not self.is_connected():
|
||||
self.driver.connect()
|
||||
current_url = self.get_current_url()
|
||||
if not current_url.startswith(("about", "data", "chrome")):
|
||||
self.get_new_driver(undetectable=True)
|
||||
self.driver.uc_open_with_cdp_mode(url)
|
||||
else:
|
||||
self.get_new_driver(undetectable=True)
|
||||
|
@ -15677,24 +15669,19 @@ class BaseCase(unittest.TestCase):
|
|||
test_id = "%s.%s" % (file_name, scenario_name)
|
||||
return test_id
|
||||
elif hasattr(self, "is_context_manager") and self.is_context_manager:
|
||||
if hasattr(self, "_manager_saved_id"):
|
||||
self.__saved_id = self._manager_saved_id
|
||||
if self.__saved_id:
|
||||
return self.__saved_id
|
||||
filename = self.__class__.__module__.split(".")[-1] + ".py"
|
||||
methodname = self._testMethodName
|
||||
context_id = None
|
||||
if filename == "base_case.py" or methodname == "runTest":
|
||||
import traceback
|
||||
stack_base = traceback.format_stack()[0].split(os.sep)[-1]
|
||||
test_base = stack_base.split(", in ")[0]
|
||||
stack_base = traceback.format_stack()[0].split(", in ")[0]
|
||||
test_base = stack_base.split(", in ")[0].split(os.sep)[-1]
|
||||
if hasattr(self, "cm_filename") and self.cm_filename:
|
||||
filename = self.cm_filename
|
||||
else:
|
||||
filename = test_base.split('"')[0]
|
||||
methodname = ".line_" + test_base.split(", line ")[-1]
|
||||
context_id = filename.split(".")[0] + methodname
|
||||
self.__saved_id = context_id
|
||||
return context_id
|
||||
test_id = "%s.%s.%s" % (
|
||||
self.__class__.__module__,
|
||||
|
|
|
@ -23,7 +23,7 @@ with SB(uc=True) as sb: # Many args! Eg. SB(browser="edge")
|
|||
|
||||
#########################################
|
||||
"""
|
||||
from contextlib import contextmanager, suppress
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
@contextmanager # Usage: -> ``with SB() as sb:``
|
||||
|
@ -258,7 +258,6 @@ def SB(
|
|||
time_limit (float): SECONDS (Safely fail tests that exceed the time limit)
|
||||
"""
|
||||
import colorama
|
||||
import gc
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
@ -1232,15 +1231,6 @@ def SB(
|
|||
sb.cap_file = sb_config.cap_file
|
||||
sb.cap_string = sb_config.cap_string
|
||||
sb._has_failure = False # This may change
|
||||
|
||||
with suppress(Exception):
|
||||
stack_base = traceback.format_stack()[0].split(os.sep)[-1]
|
||||
test_base = stack_base.split(", in ")[0]
|
||||
filename = test_base.split('"')[0]
|
||||
methodname = ".line_" + test_base.split(", line ")[-1]
|
||||
context_id = filename.split(".")[0] + methodname
|
||||
sb._manager_saved_id = context_id
|
||||
|
||||
if hasattr(sb_config, "headless_active"):
|
||||
sb.headless_active = sb_config.headless_active
|
||||
else:
|
||||
|
@ -1367,13 +1357,6 @@ def SB(
|
|||
"%s%s%s%s%s"
|
||||
% (c1, left_space, end_text, right_space, cr)
|
||||
)
|
||||
if hasattr(sb_config, "_cdp_aclose"):
|
||||
import asyncio
|
||||
with suppress(Exception):
|
||||
loop = asyncio.get_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(sb_config._cdp_aclose())
|
||||
gc.collect()
|
||||
if test and test_name and not test_passed and raise_test_failure:
|
||||
raise exception
|
||||
elif (
|
||||
|
|
|
@ -145,12 +145,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
debug_port = 9222
|
||||
special_port_free = False # If the port isn't free, don't use 9222
|
||||
try:
|
||||
with requests.Session() as session:
|
||||
res = session.get(
|
||||
"http://127.0.0.1:9222",
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
res = requests.get("http://127.0.0.1:9222", timeout=1)
|
||||
if res.status_code != 200:
|
||||
raise Exception("The port is free! It will be used!")
|
||||
except Exception:
|
||||
|
@ -402,8 +397,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
|
||||
def add_cdp_listener(self, event_name, callback):
|
||||
if (
|
||||
hasattr(self, "reactor")
|
||||
and self.reactor
|
||||
self.reactor
|
||||
and self.reactor is not None
|
||||
and isinstance(self.reactor, Reactor)
|
||||
):
|
||||
|
@ -412,11 +406,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
return False
|
||||
|
||||
def clear_cdp_listeners(self):
|
||||
if (
|
||||
hasattr(self, "reactor")
|
||||
and self.reactor
|
||||
and isinstance(self.reactor, Reactor)
|
||||
):
|
||||
if self.reactor and isinstance(self.reactor, Reactor):
|
||||
self.reactor.handlers.clear()
|
||||
|
||||
def window_new(self, url=None):
|
||||
|
@ -451,13 +441,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
with suppress(Exception):
|
||||
if self.service.is_connectable():
|
||||
self.stop_client()
|
||||
try:
|
||||
self.service.send_remote_shutdown_command()
|
||||
except TypeError:
|
||||
pass
|
||||
finally:
|
||||
with suppress(Exception):
|
||||
self.service._terminate_process()
|
||||
self.service.stop()
|
||||
if isinstance(timeout, str):
|
||||
if timeout.lower() == "breakpoint":
|
||||
breakpoint() # To continue:
|
||||
|
@ -472,7 +456,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
with suppress(Exception):
|
||||
for window_handle in self.window_handles:
|
||||
self.switch_to.window(window_handle)
|
||||
if self.current_url.startswith("chrome-extension://"):
|
||||
if self.current_url.startswith(
|
||||
"chrome-extension://"
|
||||
):
|
||||
# https://issues.chromium.org/issues/396611138
|
||||
# (Remove the Linux conditional when resolved)
|
||||
# (So that close() is always called)
|
||||
|
@ -480,13 +466,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
self.close()
|
||||
if self.service.is_connectable():
|
||||
self.stop_client()
|
||||
try:
|
||||
self.service.send_remote_shutdown_command()
|
||||
except TypeError:
|
||||
pass
|
||||
finally:
|
||||
with suppress(Exception):
|
||||
self.service._terminate_process()
|
||||
self.service.stop()
|
||||
self.service.start()
|
||||
self.start_session()
|
||||
time.sleep(0.003)
|
||||
|
@ -502,13 +482,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
if self.service.is_connectable():
|
||||
self.stop_client()
|
||||
time.sleep(0.003)
|
||||
try:
|
||||
self.service.send_remote_shutdown_command()
|
||||
except TypeError:
|
||||
pass
|
||||
finally:
|
||||
with suppress(Exception):
|
||||
self.service._terminate_process()
|
||||
self.service.stop()
|
||||
self._is_connected = False
|
||||
|
||||
def connect(self):
|
||||
|
@ -533,13 +507,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
self.close()
|
||||
if self.service.is_connectable():
|
||||
self.stop_client()
|
||||
try:
|
||||
self.service.send_remote_shutdown_command()
|
||||
except TypeError:
|
||||
pass
|
||||
finally:
|
||||
with suppress(Exception):
|
||||
self.service._terminate_process()
|
||||
self.service.stop()
|
||||
self.service.start()
|
||||
self.start_session()
|
||||
time.sleep(0.003)
|
||||
|
@ -567,35 +535,15 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
|
|||
logger.debug(e, exc_info=True)
|
||||
except Exception:
|
||||
pass
|
||||
with suppress(Exception):
|
||||
self.stop_client()
|
||||
with suppress(Exception):
|
||||
if hasattr(self, "command_executor") and self.command_executor:
|
||||
self.command_executor.close()
|
||||
|
||||
# Remove instance reference to allow garbage collection
|
||||
Chrome._instances.discard(self)
|
||||
|
||||
if hasattr(self, "service") and getattr(self.service, "process", None):
|
||||
logger.debug("Stopping webdriver service")
|
||||
with suppress(Exception):
|
||||
try:
|
||||
self.service.send_remote_shutdown_command()
|
||||
except TypeError:
|
||||
pass
|
||||
finally:
|
||||
self.stop_client()
|
||||
self.service.stop()
|
||||
with suppress(Exception):
|
||||
self.service._terminate_process()
|
||||
if (
|
||||
hasattr(self, "reactor")
|
||||
and self.reactor
|
||||
and hasattr(self.reactor, "event")
|
||||
):
|
||||
if self.reactor and isinstance(self.reactor, Reactor):
|
||||
logger.debug("Shutting down Reactor")
|
||||
with suppress(Exception):
|
||||
self.reactor.event.set()
|
||||
self.reactor.join(timeout=2)
|
||||
self.reactor = None
|
||||
if (
|
||||
hasattr(self, "keep_user_data_dir")
|
||||
and hasattr(self, "user_data_dir")
|
||||
|
|
|
@ -53,58 +53,28 @@ class CDP:
|
|||
self._session = requests.Session()
|
||||
self._last_resp = None
|
||||
self._last_json = None
|
||||
with requests.Session() as session:
|
||||
resp = session.get(
|
||||
self.server_addr + self.endpoints.json,
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
self.sessionId = resp.json()[0]["id"]
|
||||
self.wsurl = resp.json()[0]["webSocketDebuggerUrl"]
|
||||
resp = self.get(self.endpoints.json)
|
||||
self.sessionId = resp[0]["id"]
|
||||
self.wsurl = resp[0]["webSocketDebuggerUrl"]
|
||||
|
||||
def tab_activate(self, id=None):
|
||||
if not id:
|
||||
active_tab = self.tab_list()[0]
|
||||
id = active_tab.id
|
||||
self.wsurl = active_tab.webSocketDebuggerUrl
|
||||
with requests.Session() as session:
|
||||
resp = session.post(
|
||||
self.server_addr + self.endpoints["activate"].format(id=id),
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
return resp.json()
|
||||
return self.post(self.endpoints["activate"].format(id=id))
|
||||
|
||||
def tab_list(self):
|
||||
with requests.Session() as session:
|
||||
resp = session.get(
|
||||
self.server_addr + self.endpoints["list"],
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
retval = resp.json()
|
||||
retval = self.get(self.endpoints["list"])
|
||||
return [PageElement(o) for o in retval]
|
||||
|
||||
def tab_new(self, url):
|
||||
with requests.Session() as session:
|
||||
resp = session.post(
|
||||
self.server_addr + self.endpoints["new"].format(url=url),
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
return resp.json()
|
||||
return self.post(self.endpoints["new"].format(url=url))
|
||||
|
||||
def tab_close_last_opened(self):
|
||||
sessions = self.tab_list()
|
||||
opentabs = [s for s in sessions if s["type"] == "page"]
|
||||
with requests.Session() as session:
|
||||
endp_close = self.endpoints["close"]
|
||||
resp = session.post(
|
||||
self.server_addr + endp_close.format(id=opentabs[-1]["id"]),
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
return resp.json()
|
||||
return self.post(self.endpoints["close"].format(id=opentabs[-1]["id"]))
|
||||
|
||||
async def send(self, method, params):
|
||||
pip_find_lock = fasteners.InterProcessLock(
|
||||
|
@ -131,12 +101,7 @@ class CDP:
|
|||
from urllib.parse import unquote
|
||||
|
||||
uri = unquote(uri, errors="strict")
|
||||
with requests.Session() as session:
|
||||
resp = session.get(
|
||||
self.server_addr + uri,
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
resp = self._session.get(self.server_addr + uri)
|
||||
try:
|
||||
self._last_resp = resp
|
||||
self._last_json = resp.json()
|
||||
|
@ -151,13 +116,7 @@ class CDP:
|
|||
uri = unquote(uri, errors="strict")
|
||||
if not data:
|
||||
data = {}
|
||||
with requests.Session() as session:
|
||||
resp = session.post(
|
||||
self.server_addr + uri,
|
||||
json=data,
|
||||
headers={"Connection": "close"},
|
||||
timeout=2,
|
||||
)
|
||||
resp = self._session.post(self.server_addr + uri, json=data)
|
||||
try:
|
||||
self._last_resp = resp
|
||||
self._last_json = resp.json()
|
||||
|
|
|
@ -287,63 +287,10 @@ class Browser:
|
|||
connection: tab.Tab = next(
|
||||
filter(lambda item: item.type_ == "page", self.targets)
|
||||
)
|
||||
if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale:
|
||||
await connection.send(cdp.page.navigate("about:blank"))
|
||||
if (
|
||||
hasattr(sb_config, "_cdp_user_agent")
|
||||
and sb_config._cdp_user_agent
|
||||
):
|
||||
pass
|
||||
elif (
|
||||
hasattr(sb_config, "_cdp_platform")
|
||||
and sb_config._cdp_platform
|
||||
):
|
||||
pass
|
||||
else:
|
||||
await connection.set_locale(sb_config._cdp_locale)
|
||||
if hasattr(sb_config, "_cdp_timezone") and sb_config._cdp_timezone:
|
||||
await connection.send(cdp.page.navigate("about:blank"))
|
||||
await connection.set_timezone(sb_config._cdp_timezone)
|
||||
if (
|
||||
hasattr(sb_config, "_cdp_user_agent")
|
||||
and sb_config._cdp_user_agent
|
||||
):
|
||||
await connection.send(cdp.page.navigate("about:blank"))
|
||||
if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale:
|
||||
_cdp_platform = None
|
||||
if (
|
||||
hasattr(sb_config, "_cdp_platform")
|
||||
and sb_config._cdp_platform
|
||||
):
|
||||
_cdp_platform = sb_config._cdp_platform
|
||||
await connection.set_user_agent(
|
||||
sb_config._cdp_user_agent,
|
||||
sb_config._cdp_locale,
|
||||
_cdp_platform,
|
||||
)
|
||||
else:
|
||||
await connection.set_user_agent(sb_config._cdp_user_agent)
|
||||
elif (
|
||||
hasattr(sb_config, "_cdp_platform") and sb_config._cdp_platform
|
||||
):
|
||||
await connection.send(cdp.page.navigate("about:blank"))
|
||||
if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale:
|
||||
_cdp_platform = sb_config._cdp_platform
|
||||
await connection.set_user_agent(
|
||||
accept_language=sb_config._cdp_locale,
|
||||
platform=_cdp_platform,
|
||||
)
|
||||
else:
|
||||
await connection.set_user_agent(
|
||||
platform=sb_config._cdp_platform
|
||||
)
|
||||
if (
|
||||
hasattr(sb_config, "_cdp_geolocation")
|
||||
and sb_config._cdp_geolocation
|
||||
):
|
||||
await connection.send(cdp.page.navigate("about:blank"))
|
||||
await connection.set_geolocation(sb_config._cdp_geolocation)
|
||||
# Use the tab to navigate to new url
|
||||
if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale:
|
||||
await connection.send(cdp.page.navigate("about:blank"))
|
||||
await connection.set_locale(sb_config._cdp_locale)
|
||||
frame_id, loader_id, *_ = await connection.send(
|
||||
cdp.page.navigate(url)
|
||||
)
|
||||
|
|
|
@ -10,10 +10,8 @@ import types
|
|||
import typing
|
||||
from contextlib import suppress
|
||||
from seleniumbase import config as sb_config
|
||||
from seleniumbase import extensions
|
||||
from seleniumbase.config import settings
|
||||
from seleniumbase.core import detect_b_ver
|
||||
from seleniumbase.core import download_helper
|
||||
from seleniumbase.core import proxy_helper
|
||||
from seleniumbase.fixtures import constants
|
||||
from seleniumbase.fixtures import shared_utils
|
||||
|
@ -27,10 +25,7 @@ import mycdp as cdp
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
IS_LINUX = shared_utils.is_linux()
|
||||
DOWNLOADS_FOLDER = download_helper.get_downloads_folder()
|
||||
PROXY_DIR_LOCK = proxy_helper.PROXY_DIR_LOCK
|
||||
EXTENSIONS_DIR = os.path.dirname(os.path.realpath(extensions.__file__))
|
||||
AD_BLOCK_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "ad_block.zip")
|
||||
T = typing.TypeVar("T")
|
||||
|
||||
|
||||
|
@ -173,19 +168,6 @@ def __add_chrome_ext_dir(extension_dir, dir_path):
|
|||
return extension_dir
|
||||
|
||||
|
||||
def __unzip_to_new_folder(zip_file, folder):
|
||||
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
|
||||
with proxy_dir_lock:
|
||||
with suppress(Exception):
|
||||
shared_utils.make_writable(PROXY_DIR_LOCK)
|
||||
if not os.path.exists(folder):
|
||||
import zipfile
|
||||
zip_ref = zipfile.ZipFile(zip_file, "r")
|
||||
os.makedirs(folder)
|
||||
zip_ref.extractall(folder)
|
||||
zip_ref.close()
|
||||
|
||||
|
||||
def __add_chrome_proxy_extension(
|
||||
extension_dir,
|
||||
proxy_string,
|
||||
|
@ -249,7 +231,6 @@ async def start(
|
|||
browser_executable_path: Optional[PathLike] = None,
|
||||
browser_args: Optional[List[str]] = None,
|
||||
xvfb_metrics: Optional[List[str]] = None, # "Width,Height" for Linux
|
||||
ad_block: Optional[bool] = False,
|
||||
sandbox: Optional[bool] = True,
|
||||
lang: Optional[str] = None, # Set the Language Locale Code
|
||||
host: Optional[str] = None, # Chrome remote-debugging-host
|
||||
|
@ -257,10 +238,7 @@ async def start(
|
|||
xvfb: Optional[int] = None, # Use a special virtual display on Linux
|
||||
headed: Optional[bool] = None, # Override default Xvfb mode on Linux
|
||||
expert: Optional[bool] = None, # Open up closed Shadow-root elements
|
||||
agent: Optional[str] = None, # Set the user-agent string
|
||||
proxy: Optional[str] = None, # "host:port" or "user:pass@host:port"
|
||||
tzone: Optional[str] = None, # Eg "America/New_York", "Asia/Kolkata"
|
||||
geoloc: Optional[list | tuple] = None, # Eg (48.87645, 2.26340)
|
||||
extension_dir: Optional[str] = None, # Chrome extension directory
|
||||
**kwargs: Optional[dict],
|
||||
) -> Browser:
|
||||
|
@ -318,13 +296,6 @@ async def start(
|
|||
proxy_user,
|
||||
proxy_pass,
|
||||
)
|
||||
if ad_block:
|
||||
incognito = False
|
||||
guest = False
|
||||
ad_block_zip = AD_BLOCK_ZIP_PATH
|
||||
ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
|
||||
__unzip_to_new_folder(ad_block_zip, ad_block_dir)
|
||||
extension_dir = __add_chrome_ext_dir(extension_dir, ad_block_dir)
|
||||
if not config:
|
||||
config = Config(
|
||||
user_data_dir,
|
||||
|
@ -358,26 +329,6 @@ async def start(
|
|||
sb_config._cdp_locale = kwargs["locale_code"]
|
||||
else:
|
||||
sb_config._cdp_locale = None
|
||||
if tzone:
|
||||
sb_config._cdp_timezone = tzone
|
||||
elif "timezone" in kwargs:
|
||||
sb_config._cdp_timezone = kwargs["timezone"]
|
||||
else:
|
||||
sb_config._cdp_timezone = None
|
||||
if geoloc:
|
||||
sb_config._cdp_geolocation = geoloc
|
||||
elif "geolocation" in kwargs:
|
||||
sb_config._cdp_geolocation = kwargs["geolocation"]
|
||||
else:
|
||||
sb_config._cdp_geolocation = None
|
||||
if agent:
|
||||
sb_config._cdp_user_agent = agent
|
||||
elif "user_agent" in kwargs:
|
||||
sb_config._cdp_user_agent = kwargs["user_agent"]
|
||||
else:
|
||||
sb_config._cdp_user_agent = None
|
||||
if "platform" in kwargs:
|
||||
sb_config._cdp_platform = kwargs["platform"]
|
||||
return driver
|
||||
|
||||
|
||||
|
@ -455,13 +406,7 @@ async def create_from_driver(driver) -> Browser:
|
|||
browser = await start(conf)
|
||||
browser._process_pid = driver.browser_pid
|
||||
# Stop chromedriver binary
|
||||
try:
|
||||
driver.service.send_remote_shutdown_command()
|
||||
except TypeError:
|
||||
pass
|
||||
finally:
|
||||
with suppress(Exception):
|
||||
driver.service._terminate_process()
|
||||
driver.service.stop()
|
||||
driver.browser_pid = -1
|
||||
driver.user_data_dir = None
|
||||
return browser
|
||||
|
|
|
@ -19,7 +19,6 @@ from typing import (
|
|||
)
|
||||
import websockets
|
||||
from websockets.protocol import State
|
||||
from seleniumbase import config as sb_config
|
||||
from . import cdp_util as util
|
||||
import mycdp as cdp
|
||||
import mycdp.network
|
||||
|
@ -271,7 +270,6 @@ class Connection(metaclass=CantTouchThis):
|
|||
max_size=MAX_SIZE,
|
||||
)
|
||||
self.listener = Listener(self)
|
||||
sb_config._cdp_aclose = self.aclose
|
||||
except (Exception,) as e:
|
||||
logger.debug("Exception during opening of websocket: %s", e)
|
||||
if self.listener:
|
||||
|
@ -292,6 +290,10 @@ class Connection(metaclass=CantTouchThis):
|
|||
Closes the websocket connection. Shouldn't be called manually by users.
|
||||
"""
|
||||
if self.websocket and self.websocket.state is not State.CLOSED:
|
||||
if self.listener and self.listener.running:
|
||||
self.listener.cancel()
|
||||
self.enabled_domains.clear()
|
||||
await asyncio.sleep(0.015)
|
||||
try:
|
||||
await self.websocket.close()
|
||||
except Exception:
|
||||
|
@ -299,9 +301,6 @@ class Connection(metaclass=CantTouchThis):
|
|||
"\n❌ Error closing websocket connection to %s",
|
||||
self.websocket_url
|
||||
)
|
||||
if self.listener and self.listener.running:
|
||||
self.listener.cancel()
|
||||
self.enabled_domains.clear()
|
||||
logger.debug(
|
||||
"\n❌ Closed websocket connection to %s", self.websocket_url
|
||||
)
|
||||
|
@ -348,37 +347,7 @@ class Connection(metaclass=CantTouchThis):
|
|||
|
||||
async def set_locale(self, locale: Optional[str] = None):
|
||||
"""Sets the Language Locale code via set_user_agent_override."""
|
||||
await self.set_user_agent(user_agent="", accept_language=locale)
|
||||
|
||||
async def set_timezone(self, timezone: Optional[str] = None):
|
||||
"""Sets the Timezone via set_timezone_override."""
|
||||
await self.send(cdp.emulation.set_timezone_override(timezone))
|
||||
|
||||
async def set_user_agent(
|
||||
self,
|
||||
user_agent: Optional[str] = "",
|
||||
accept_language: Optional[str] = None,
|
||||
platform: Optional[str] = None, # navigator.platform
|
||||
):
|
||||
"""Sets the User Agent via set_user_agent_override."""
|
||||
if not user_agent:
|
||||
user_agent = ""
|
||||
await self.send(cdp.network.set_user_agent_override(
|
||||
user_agent=user_agent,
|
||||
accept_language=accept_language,
|
||||
platform=platform,
|
||||
))
|
||||
|
||||
async def set_geolocation(self, geolocation: Optional[tuple] = None):
|
||||
"""Sets the User Agent via set_geolocation_override."""
|
||||
await self.send(cdp.browser.grant_permissions(
|
||||
permissions=["geolocation"],
|
||||
))
|
||||
await self.send(cdp.emulation.set_geolocation_override(
|
||||
latitude=geolocation[0],
|
||||
longitude=geolocation[1],
|
||||
accuracy=100,
|
||||
))
|
||||
await self.send(cdp.network.set_user_agent_override("", locale))
|
||||
|
||||
def __getattr__(self, item):
|
||||
""":meta private:"""
|
||||
|
@ -446,7 +415,6 @@ class Connection(metaclass=CantTouchThis):
|
|||
if not _is_update:
|
||||
await self._register_handlers()
|
||||
await self.websocket.send(tx.message)
|
||||
sb_config._cdp_aclose = self.aclose
|
||||
try:
|
||||
return await tx
|
||||
except ProtocolException as e:
|
||||
|
@ -579,7 +547,8 @@ class Listener:
|
|||
except asyncio.TimeoutError:
|
||||
self.idle.set()
|
||||
# Pause for a moment.
|
||||
await asyncio.sleep(self.time_before_considered_idle / 10)
|
||||
# await asyncio.sleep(self.time_before_considered_idle / 10)
|
||||
await asyncio.sleep(0.015)
|
||||
continue
|
||||
except (Exception,) as e:
|
||||
logger.debug(
|
||||
|
|
38
setup.py
38
setup.py
|
@ -34,7 +34,7 @@ if sys.argv[-1] == "publish":
|
|||
print("\nERROR! Publishing to PyPI requires Python>=3.9")
|
||||
sys.exit()
|
||||
print("\n*** Checking code health with flake8:\n")
|
||||
os.system("python -m pip install 'flake8==7.2.0'")
|
||||
os.system("python -m pip install 'flake8==7.1.2'")
|
||||
flake8_status = os.system("flake8 --exclude=recordings,temp")
|
||||
if flake8_status != 0:
|
||||
print("\nERROR! Fix flake8 issues before publishing to PyPI!\n")
|
||||
|
@ -148,9 +148,9 @@ setup(
|
|||
python_requires=">=3.8",
|
||||
install_requires=[
|
||||
'pip>=25.0.1',
|
||||
'packaging>=25.0',
|
||||
'packaging>=24.2',
|
||||
'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues
|
||||
'setuptools>=79.0.1;python_version>="3.10"',
|
||||
'setuptools>=78.1.0;python_version>="3.10"',
|
||||
'wheel>=0.45.1',
|
||||
'attrs>=25.3.0',
|
||||
"certifi>=2025.1.31",
|
||||
|
@ -160,11 +160,11 @@ setup(
|
|||
'filelock~=3.16.1;python_version<"3.9"',
|
||||
'filelock>=3.18.0;python_version>="3.9"',
|
||||
'fasteners>=0.19',
|
||||
"mycdp>=1.2.0",
|
||||
"mycdp>=1.1.1",
|
||||
"pynose>=1.5.4",
|
||||
'platformdirs>=4.3.6;python_version<"3.9"',
|
||||
'platformdirs>=4.3.7;python_version>="3.9"',
|
||||
'typing-extensions>=4.13.2',
|
||||
'typing-extensions>=4.13.0',
|
||||
"sbvirtualdisplay>=1.4.0",
|
||||
'MarkupSafe==2.1.5;python_version<"3.9"',
|
||||
'MarkupSafe>=3.0.2;python_version>="3.9"',
|
||||
|
@ -182,18 +182,18 @@ setup(
|
|||
'chardet==5.2.0',
|
||||
'charset-normalizer==3.4.1',
|
||||
'urllib3>=1.26.20,<2;python_version<"3.10"',
|
||||
'urllib3>=1.26.20,<2.5.0;python_version>="3.10"',
|
||||
'urllib3>=1.26.20,<2.4.0;python_version>="3.10"',
|
||||
'requests==2.32.3',
|
||||
'sniffio==1.3.1',
|
||||
'h11==0.16.0',
|
||||
'h11==0.14.0',
|
||||
'outcome==1.3.0.post0',
|
||||
'trio==0.27.0;python_version<"3.9"',
|
||||
'trio==0.30.0;python_version>="3.9"',
|
||||
'trio==0.29.0;python_version>="3.9"',
|
||||
'trio-websocket==0.12.2',
|
||||
'wsproto==1.2.0',
|
||||
'websocket-client==1.8.0',
|
||||
'selenium==4.27.1;python_version<"3.9"',
|
||||
'selenium==4.31.0;python_version>="3.9"',
|
||||
'selenium==4.30.0;python_version>="3.9"',
|
||||
'cssselect==1.2.0;python_version<"3.9"',
|
||||
'cssselect==1.3.0;python_version>="3.9"',
|
||||
"sortedcontainers==2.4.0",
|
||||
|
@ -209,13 +209,13 @@ setup(
|
|||
'pytest-xdist==3.6.1',
|
||||
'parameterized==0.9.0',
|
||||
"behave==1.2.6",
|
||||
'soupsieve==2.7',
|
||||
"beautifulsoup4==4.13.4",
|
||||
'soupsieve==2.6',
|
||||
"beautifulsoup4==4.13.3",
|
||||
'pyotp==2.9.0',
|
||||
'python-xlib==0.33;platform_system=="Linux"',
|
||||
'markdown-it-py==3.0.0',
|
||||
'mdurl==0.1.2',
|
||||
'rich>=14.0.0,<15',
|
||||
'rich==13.9.4',
|
||||
],
|
||||
extras_require={
|
||||
# pip install -e .[allure]
|
||||
|
@ -230,20 +230,20 @@ setup(
|
|||
# Usage: coverage run -m pytest; coverage html; coverage report
|
||||
"coverage": [
|
||||
'coverage>=7.6.1;python_version<"3.9"',
|
||||
'coverage>=7.8.0;python_version>="3.9"',
|
||||
'coverage>=7.7.1;python_version>="3.9"',
|
||||
'pytest-cov>=5.0.0;python_version<"3.9"',
|
||||
'pytest-cov>=6.1.1;python_version>="3.9"',
|
||||
'pytest-cov>=6.0.0;python_version>="3.9"',
|
||||
],
|
||||
# pip install -e .[flake8]
|
||||
# Usage: flake8
|
||||
"flake8": [
|
||||
'flake8==5.0.4;python_version<"3.9"',
|
||||
'flake8==7.2.0;python_version>="3.9"',
|
||||
'flake8==7.1.2;python_version>="3.9"',
|
||||
"mccabe==0.7.0",
|
||||
'pyflakes==2.5.0;python_version<"3.9"',
|
||||
'pyflakes==3.3.2;python_version>="3.9"',
|
||||
'pyflakes==3.2.0;python_version>="3.9"',
|
||||
'pycodestyle==2.9.1;python_version<"3.9"',
|
||||
'pycodestyle==2.13.0;python_version>="3.9"',
|
||||
'pycodestyle==2.12.1;python_version>="3.9"',
|
||||
],
|
||||
# pip install -e .[ipdb]
|
||||
# (Not needed for debugging anymore. SeleniumBase now includes "pdbp".)
|
||||
|
@ -261,7 +261,7 @@ setup(
|
|||
# (An optional library for parsing PDF files.)
|
||||
"pdfminer": [
|
||||
'pdfminer.six==20250324;python_version<"3.9"',
|
||||
'pdfminer.six==20250416;python_version>="3.9"',
|
||||
'pdfminer.six==20250327;python_version>="3.9"',
|
||||
'cryptography==39.0.2;python_version<"3.9"',
|
||||
'cryptography==44.0.2;python_version>="3.9"',
|
||||
'cffi==1.17.1',
|
||||
|
@ -271,7 +271,7 @@ setup(
|
|||
# (An optional library for image-processing.)
|
||||
"pillow": [
|
||||
'Pillow>=10.4.0;python_version<"3.9"',
|
||||
'Pillow>=11.2.1;python_version>="3.9"',
|
||||
'Pillow>=11.1.0;python_version>="3.9"',
|
||||
],
|
||||
# pip install -e .[pip-system-certs]
|
||||
# (If you see [SSL: CERTIFICATE_VERIFY_FAILED], then get this.)
|
||||
|
|
Loading…
Reference in New Issue