Building a Steam Sale Recommendation Agent with Hyperbrowser and Anthropic Claude
In this cookbook, we'll build a smart agent that can recommend games on sale from Steam that match your preferences. This tool combines:
- Hyperbrowser for accessing dynamic web content on Steam's store pages
- Playwright for navigating and extracting data from Steam's special offers
- Anthropic's Claude Sonnet 3.5/3.7 for understanding user preferences and analyzing game recommendations
We'll use a hybrid approach where we first manually filter the data by navigating to specific Steam pages and selecting relevant elements, and then leverage Claude to extract and structure the information—combining the efficiency of targeted scraping with the intelligence of language models.
By the end of this cookbook, you'll have an intelligent tool that can find discounted games tailored to your gaming preferences!
Prerequisites
Before starting, make sure you have:
- A Hyperbrowser API key (sign up at hyperbrowser.ai if you don't have one)
- An Anthropic API key with access to Claude Sonnet 3.5/3.7
- Python 3.9+ installed with Playwright and other required packages
Both API keys should be stored in a .env
file in the same directory as this notebook with the following format:
HYPERBROWSER_API_KEY=your_hyperbrowser_key_here
ANTHROPIC_API_KEY=your_anthropic_key_here
Step 1: Set up imports and load environment variables
import asyncioimport osimport jsonfrom dotenv import load_dotenvfrom hyperbrowser import AsyncHyperbrowserfrom hyperbrowser.models import CreateSessionParams, ScreenConfigfrom playwright.async_api import async_playwright, Pagefrom anthropic import AsyncAnthropicfrom typing import Listfrom pydantic import BaseModelfrom IPython.display import Markdown, displayload_dotenv()
True
Step 2: Initialize API clients
We'll create instances of the Hyperbrowser and Anthropic clients to access their APIs throughout our notebook.
hb = AsyncHyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))llm = AsyncAnthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
Step 3: Define data models and scraping functions
Now we'll define the data structures and functions needed to extract sale information from Steam. This steps for this are -
- Create a Pydantic model to structure our game data with relevant fields like price, sentiment, and tags
- Navigate through Hyperbrowser sessions and Playwright to scrape dynamic content
- Extract the on-sale games list from the Steam website
- Parse unstructured text into structured game information using Claude
Steam's website has a dynamic interface, so we use Playwright to interact with it and access the special offers tab.
Our approach is hybrid: we use Playwright for targeted manual filtering—navigating directly to the specials page and extracting specific HTML elements using selectors. This narrows down the data to just what we need before passing it to Claude, which handles the complex task of understanding and structuring the data. This combination provides both efficiency and intelligence.
class SteamSpecials(BaseModel):game: strcurrent_price: stroriginal_price: strsentiment: strtags: List[str]class SteamSpecialsList(BaseModel):specials: List[SteamSpecials]async def get_specials(page: Page):await asyncio.sleep(5)await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")await asyncio.sleep(5)steam_specials = await page.wait_for_selector("._3EdZTDIisUpowxwm6uJ7Iq")# steam_specials_list = await page.query_selector_all(".gASJ2lL_xmVNuZkWGvrWg")if steam_specials is not None:# First get the initial text contentsteam_specials_text = await steam_specials.inner_text()# Get updated content after clickingsteam_specials = await page.wait_for_selector("._3EdZTDIisUpowxwm6uJ7Iq")if steam_specials:steam_specials_text = await steam_specials.inner_text()return steam_specials_textelse:raise ValueError("No specials found")async def get_steam_specials_text():async with async_playwright() as p:session = await hb.sessions.create(CreateSessionParams(use_proxy=True, proxy_state="MN"))if session is None or session.ws_endpoint is None:raise ValueError("No session found")browser = await p.chromium.connect_over_cdp(session.ws_endpoint)page = await browser.new_page()await page.goto("https://store.steampowered.com/specials#tab=TopSellers")return await get_specials(page)async def process_steam_specials(steam_specials_text: str):response = await llm.messages.create(model="claude-3-7-sonnet-latest",messages=[{"role": "user","content": f"Please extract game information from this Steam specials text into JSON format. Follow this structure exactly:\n\n{SteamSpecialsList.model_json_schema()}\n\nHere's the text to parse:\n\n{steam_specials_text}\n\nPlease respond with valid JSON only. Do not add any other text or comments, like ```json or ```. Provide me only the JSON response formatted as a string",}],max_tokens=4000,)# Check the content type and extract the textif isinstance(response.content, list) and len(response.content) > 0:content_block = response.content[0]if hasattr(content_block, "type") and content_block.type == "text":content = content_block.textelse:content_type = getattr(content_block, "type", "unknown")print(f"Unexpected content type: {content_type}")raise ValueError(f"Invalid response from Claude: expected text content but got {content_type}")else:print(f"Unexpected response structure: {response.content}")raise ValueError("Invalid response from Claude: empty or invalid content")data = json.loads(content)return SteamSpecialsList.model_validate(data)async def get_steam_specials():specials_text = await get_steam_specials_text()specials_list = await process_steam_specials(specials_text)return specials_list
Step 4: Create the recommendation agent
Now we'll build the recommendation agent that combines the scraped Steam specials with user queries to suggest relevant games. This function:
- Gets the latest list of games on sale
- Feeds this structured data to Claude Sonnet 3.5 along with the user's preferences
- Returns personalized game recommendations based on the available specials
This agent acts as a personal game shopper, understanding your tastes and matching them with current deals.
async def get_steam_specials_agent(user_query: str):specials = await get_steam_specials()if specials is None:raise ValueError("No specials found")response = await llm.messages.create(model="claude-3-5-sonnet-latest",messages=[{"role": "assistant","content": "You are a helpful assistant that recommends games on sale from Steam. You will be given a list of specials and a user query. Answer the user query based on the specials. Return the answer in markdown format.",},{"role": "assistant","content": f"Here are the current specials on Steam: {(specials.model_dump_json())}",},{"role": "user","content": f"Based on the provided specials, can you answer this query? {user_query}",},],max_tokens=4000,)return response
Step 5: Test the recommendation agent
Let's test our agent by asking for recommendations similar to a popular game. This will demonstrate the full workflow:
- The agent scrapes the current sales from Steam's special offers page
- It structures this data into a format our recommendation system can use
- It takes the user's query about preferences similar to Stardew Valley
- It returns personalized game recommendations from the current sale items
The response is formatted in Markdown for easy reading with game details and reasoning.
response = await get_steam_specials_agent("I like Cyberpunk 2077. Is there any game that is similar to it on sale?")if response.content[0].text is not None: # type: ignoredisplay(Markdown(response.content[0].text)) # type: ignoreelse:print("No response from the agent")
Here are some similar games to Cyberpunk 2077 from the current sales:
Cyberpunk 2077: Phantom Liberty
-
Price: $22.49 (25% off)
-
Original Price: $29.99
-
Sentiment: Very Positive
-
Tags: RPG, Sexual Content, Cyberpunk, Open World, Story Rich
-
Note: This is the expansion DLC for Cyberpunk 2077
NieR:Automata™
-
Price: $15.99 (60% off)
-
Original Price: $39.99
-
Sentiment: Very Positive
-
Tags: Great Soundtrack, Story Rich, Female Protagonist, Hack and Slash, Action
-
Note: While not strictly cyberpunk, it shares similar themes of a dark future, RPG elements, and strong story focus.
Both games feature strong story-driven narratives in futuristic settings. If you enjoyed Cyberpunk 2077, Phantom Liberty is a direct expansion that adds new content to the base game. NieR:Automata offers a different take on the sci-fi genre with similar deep storytelling and action elements.
Conclusion
In this cookbook, we built a Steam sale recommendation agent using Hyperbrowser, Playwright, and Claude Sonnet 3.5. This agent can:
- Automatically scrape current special offers from Steam
- Extract structured data about games on sale
- Match user preferences to available deals
- Generate personalized game recommendations with relevant details
Our hybrid approach—where we first manually filter content using Playwright selectors before applying Claude for intelligent extraction—gives us the best of both worlds: the precision of targeted scraping with the cognitive abilities of language models. This allows us to handle Steam's complex dynamic interface efficiently while still getting high-quality structured data.
This approach demonstrates how AI can enhance the game discovery process by filtering through sales to find titles that match specific player preferences.
Next Steps
To take this further, you might consider:
- Adding support for multiple game platforms beyond Steam
- Implementing more detailed filtering (price range, genre, etc.)
- Creating a web interface for easier interaction
- Adding historical price tracking to identify particularly good deals
- Incorporating game reviews and critic scores into recommendations
Happy gaming! 🎮