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:

  1. A Hyperbrowser API key (sign up at hyperbrowser.ai if you don't have one)
  2. An Anthropic API key with access to Claude Sonnet 3.5/3.7
  3. 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 asyncio
import os
import json
from dotenv import load_dotenv
from hyperbrowser import AsyncHyperbrowser
from hyperbrowser.models import CreateSessionParams, ScreenConfig
from playwright.async_api import async_playwright, Page
from anthropic import AsyncAnthropic
from typing import List
from pydantic import BaseModel
from IPython.display import Markdown, display
load_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 -

  1. Create a Pydantic model to structure our game data with relevant fields like price, sentiment, and tags
  2. Navigate through Hyperbrowser sessions and Playwright to scrape dynamic content
  3. Extract the on-sale games list from the Steam website
  4. 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: str
current_price: str
original_price: str
sentiment: str
tags: 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 content
steam_specials_text = await steam_specials.inner_text()
# Get updated content after clicking
steam_specials = await page.wait_for_selector("._3EdZTDIisUpowxwm6uJ7Iq")
if steam_specials:
steam_specials_text = await steam_specials.inner_text()
return steam_specials_text
else:
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 text
if 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.text
else:
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:

  1. Gets the latest list of games on sale
  2. Feeds this structured data to Claude Sonnet 3.5 along with the user's preferences
  3. 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:

  1. The agent scrapes the current sales from Steam's special offers page
  2. It structures this data into a format our recommendation system can use
  3. It takes the user's query about preferences similar to Stardew Valley
  4. 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: ignore
display(Markdown(response.content[0].text)) # type: ignore
else:
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:

  1. Automatically scrape current special offers from Steam
  2. Extract structured data about games on sale
  3. Match user preferences to available deals
  4. 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! 🎮