Last active
January 30, 2025 21:59
-
-
Save r614/751a030f28fd234a38fddcc09f496d10 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Setup: | |
# uv pip install playwright plyer | |
# playwright install | |
from playwright.sync_api import sync_playwright | |
import time | |
import random | |
from plyer import notification | |
import sys | |
PRODUCT_URL = "https://www.bestbuy.com/site/nvidia-geforce-rtx-5090-32gb-gddr7-graphics-card-dark-gun-metal/6614151.p?skuId=6614151&irclickid=WiYW2YV%3AoxyKWA234nVPV0QXUks2ye2gEUU52o0&irgwc=1&ref=198&loc=Troposphere%20LLC&acampID=0&mpid=62662&affgroup=%22Deals%22" | |
def send_notification(title, message): | |
notification.notify( | |
title=title, | |
message=message, | |
app_icon=None, | |
timeout=10, | |
) | |
def random_delay(min_seconds=45, max_seconds=75): | |
"""Add random delay between checks to appear more human-like""" | |
delay = random.uniform(min_seconds, max_seconds) | |
return delay | |
def setup_browser_context(playwright): | |
"""Setup browser with anti-bot detection configurations""" | |
# Use a common browser viewport size | |
viewport = {"width": 1920, "height": 1080} | |
# Common user agent for Chrome | |
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" | |
# Browser context with various settings to appear more human-like | |
context = playwright.chromium.launch_persistent_context( | |
user_data_dir="./user-data", # Persist login sessions | |
headless=False, | |
viewport=viewport, | |
user_agent=user_agent, | |
java_script_enabled=True, | |
locale="en-US", | |
timezone_id="America/New_York", | |
geolocation={"latitude": 40.7128, "longitude": -74.0060}, # NYC coordinates | |
permissions=["geolocation"], | |
args=[ | |
"--disable-blink-features=AutomationControlled", | |
"--disable-automation", | |
"--no-sandbox", | |
], | |
) | |
return context | |
def check_and_add_to_cart(page, product_url): | |
try: | |
# Add some randomization to navigation but with shorter timeouts | |
page.set_default_timeout(random.randint(10000, 15000)) | |
# Use a more lenient loading state and catch timeout | |
try: | |
page.goto(product_url, wait_until="domcontentloaded") | |
except Exception as e: | |
print(f"Page load warning (continuing anyway): {str(e)}") | |
# Give the page a moment to settle | |
time.sleep(random.uniform(1, 2)) | |
# Simulate human-like scrolling with more natural timing | |
page.mouse.move(random.randint(0, 800), random.randint(0, 600)) | |
page.mouse.wheel(delta_x=0, delta_y=random.randint(-300, -100)) | |
time.sleep(random.uniform(0.5, 1)) | |
button_locators = [ | |
# Try simpler selectors first | |
page.locator('button[data-button-state="ADD_TO_CART"]'), | |
page.locator('button.add-to-cart-button'), | |
page.get_by_role("button", name="Add to Cart", exact=True), | |
page.locator('button:has-text("Add to Cart")'), | |
] | |
for button in button_locators: | |
try: | |
# Shorter timeout for button checks | |
if button.is_visible(timeout=3000): | |
if not button.is_disabled(): | |
# More natural mouse movement | |
button.hover() | |
time.sleep(random.uniform(0.1, 0.3)) | |
button.click(delay=random.randint(50, 150)) | |
# Wait a moment to confirm the click worked | |
time.sleep(1) | |
send_notification( | |
"Product Available!", | |
"The item has been added to your cart!", | |
) | |
return True | |
except Exception as e: | |
print(f"Button attempt failed: {str(e)}") | |
continue | |
print("Add to Cart button not available") | |
return False | |
except Exception as e: | |
print(f"Error checking product: {str(e)}") | |
return False | |
def main(): | |
with sync_playwright() as p: | |
context = setup_browser_context(p) | |
page = context.new_page() | |
print("Please log into your Best Buy account...") | |
print("After logging in, press Enter to start monitoring...") | |
# Navigate to Best Buy to allow manual login | |
page.goto("https://www.bestbuy.com") | |
input() | |
print("Starting to monitor the product...") | |
try: | |
while True: | |
if check_and_add_to_cart(page, PRODUCT_URL): | |
print("Product added to cart successfully!") | |
print("Browser will remain open for you to complete the purchase.") | |
print("Press Ctrl+C when you're done to close the browser.") | |
# Keep the script running until user interrupts | |
while True: | |
time.sleep(1) | |
delay = random_delay() | |
print( | |
f"Product not available. Checking again in {delay:.1f} seconds..." | |
) | |
time.sleep(delay) | |
except KeyboardInterrupt: | |
print("\nBot stopped by user. Closing browser...") | |
except Exception as e: | |
print(f"An error occurred: {str(e)}") | |
finally: | |
# Close all pages first | |
for page in context.pages: | |
try: | |
page.close() | |
except: | |
pass | |
# Then close the context with a timeout | |
try: | |
context.close() | |
except: | |
print("Force closing browser...") | |
import os | |
import signal | |
# Force kill any remaining browser processes | |
os.kill(context.browser.process.pid, signal.SIGTERM) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment