Skip to content

Commit 15d7d23

Browse files
committed
Add ad-use demo
1 parent d595221 commit 15d7d23

File tree

4 files changed

+281
-0
lines changed

4 files changed

+281
-0
lines changed

examples/apps/ad-use/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Ad-Use
2+
3+
Automatically generate Instagram ads from any landing page using browser agents and Google's Nano Banana 🍌 image generation model.
4+
5+
[!CAUTION]
6+
This demo requires browser-use v0.7.4+.
7+
8+
## Features
9+
10+
1. Agent visits your target website
11+
2. Captures brand name, tagline, and key selling points
12+
3. Takes a clean screenshot for design reference
13+
4. Creates a scroll-stopping Instagram ad with 🍌
14+
15+
## Setup
16+
17+
Make sure the newest version of browser-use is installed (with screenshot functionality):
18+
```bash
19+
pip install -U browser-use
20+
```
21+
22+
Export your Gemini API key, get it from: [Google AI Studio](https://makersuite.google.com/app/apikey)
23+
```
24+
export GOOGLE_API_KEY='your-google-api-key-here'
25+
```
26+
27+
## Normal Usage
28+
29+
```bash
30+
# Basic - Generate ad from any website
31+
python ad_generator.py https://www.apple.com/iphone-16-pro/
32+
33+
# Debug Mode - See the browser in action
34+
python ad_generator.py https://www.apple.com/iphone-16-pro/ --debug
35+
```
36+
37+
## Programmatic Usage
38+
```python
39+
import asyncio
40+
from ad_generator import create_ad_from_landing_page
41+
42+
async def main():
43+
results = await create_ad_from_landing_page(
44+
url="https://your-landing-page.com",
45+
debug=False
46+
)
47+
print(f"Generated ads: {results}")
48+
49+
asyncio.run(main())
50+
```
51+
52+
## Output
53+
54+
Generated ads are saved in the `output/` directory with:
55+
- **PNG image files** (ad_style_timestamp.png) - Actual generated ads from Gemini 2.5 Flash Image
56+
- **Prompt files** (ad_style_timestamp_prompt.txt) - The prompts used for generation
57+
- **Landing page screenshots** for reference
58+
59+
## Source Code
60+
61+
Full implementation: [https://github.com/browser-use/browser-use/tree/main/examples/apps/ad-use](https://github.com/browser-use/browser-use/tree/main/examples/apps/ad-use)
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import asyncio
2+
import argparse
3+
import logging
4+
import os
5+
import subprocess
6+
import sys
7+
from datetime import datetime
8+
from pathlib import Path
9+
from typing import Dict
10+
11+
def setup_environment(debug: bool):
12+
if not debug:
13+
os.environ['BROWSER_USE_SETUP_LOGGING'] = 'false'
14+
os.environ['BROWSER_USE_LOGGING_LEVEL'] = 'critical'
15+
logging.getLogger().setLevel(logging.CRITICAL)
16+
else:
17+
os.environ['BROWSER_USE_SETUP_LOGGING'] = 'true'
18+
os.environ['BROWSER_USE_LOGGING_LEVEL'] = 'info'
19+
20+
parser = argparse.ArgumentParser(description='Generate ads from landing pages using browser-use + 🍌')
21+
parser.add_argument('url', nargs='?', help='Landing page URL to analyze')
22+
parser.add_argument('--debug', action='store_true', default=False, help='Enable debug mode (show browser, verbose logs)')
23+
args = parser.parse_args()
24+
setup_environment(args.debug)
25+
26+
from PIL import Image
27+
from google import genai
28+
from browser_use import Agent, BrowserSession
29+
from browser_use.llm.google import ChatGoogle
30+
import aiofiles
31+
32+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
33+
34+
class LandingPageAnalyzer:
35+
def __init__(self, debug: bool = False):
36+
self.debug = debug
37+
self.llm = ChatGoogle(
38+
model="gemini-2.0-flash-exp",
39+
api_key=GOOGLE_API_KEY
40+
)
41+
self.output_dir = Path("output")
42+
self.output_dir.mkdir(exist_ok=True)
43+
44+
async def analyze_landing_page(self, url: str) -> Dict:
45+
browser_session = BrowserSession(
46+
headless=not self.debug, # headless=False only when debug=True
47+
disable_security=True
48+
)
49+
50+
agent = Agent(
51+
task=f"""Go to {url} and quickly extract key brand information for Instagram ad creation.
52+
53+
Steps:
54+
1. Navigate to the website
55+
2. From the initial view, extract ONLY these essentials:
56+
- Brand/Product name
57+
- Main tagline or value proposition (one sentence)
58+
- Primary call-to-action text
59+
- Any visible pricing or special offer
60+
3. Scroll down half a page, twice (0.5 pages each) to check for any key info
61+
4. Done - keep it simple and focused on the brand
62+
63+
Return ONLY the key brand info, not page structure details.""",
64+
llm=self.llm,
65+
browser_session=browser_session,
66+
max_actions_per_step=2,
67+
step_timeout=30,
68+
use_thinking=False,
69+
vision_detail_level='high',
70+
)
71+
72+
screenshot_path = None
73+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
74+
75+
# Take screenshot after page fully loads
76+
async def screenshot_callback(agent_instance):
77+
nonlocal screenshot_path
78+
import asyncio
79+
await asyncio.sleep(4)
80+
screenshot_path = self.output_dir / f"landing_page_{timestamp}.png"
81+
active_session = agent_instance.browser_session
82+
screenshot_data = await active_session.take_screenshot(path=str(screenshot_path), full_page=False)
83+
84+
import asyncio
85+
screenshot_task = asyncio.create_task(screenshot_callback(agent))
86+
87+
history = await agent.run()
88+
89+
try:
90+
await screenshot_task
91+
except Exception as e:
92+
print(f"Screenshot task failed: {e}")
93+
94+
analysis = history.final_result()
95+
if not analysis:
96+
analysis = "No analysis content extracted"
97+
98+
return {
99+
'url': url,
100+
'analysis': analysis,
101+
'screenshot_path': screenshot_path,
102+
'timestamp': timestamp
103+
}
104+
105+
class AdGenerator:
106+
def __init__(self, api_key: str = GOOGLE_API_KEY):
107+
self.client = genai.Client(api_key=api_key)
108+
self.output_dir = Path("output")
109+
self.output_dir.mkdir(exist_ok=True)
110+
111+
def create_ad_prompt(self, browser_analysis: str) -> str:
112+
prompt = f"""Create an Instagram ad for this brand:
113+
114+
{browser_analysis}
115+
116+
Create a vibrant, eye-catching Instagram ad image with:
117+
- Try to use the colors and style of the logo or brand, else:
118+
- Bold, modern gradient background with bright colors
119+
- Large, playful sans-serif text with the product/service name from the analysis
120+
- Trendy design elements: geometric shapes, sparkles, emojis
121+
- Fun bubbles or badges for any pricing or special offers mentioned
122+
- Call-to-action button with text from the analysis
123+
- Emphasizes the key value proposition from the analysis
124+
- Uses visual elements that match the brand personality
125+
- Square format (1:1 ratio)
126+
- Use color psychology to drive action
127+
128+
Style: Modern Instagram advertisement, (1:1), scroll-stopping, professional but playful, conversion-focused"""
129+
return prompt
130+
131+
async def generate_ad_image(self, prompt: str, screenshot_path: Path = None) -> bytes:
132+
try:
133+
contents = [prompt]
134+
135+
if screenshot_path and screenshot_path.exists():
136+
screenshot_prompt = f"\n\nHere is the actual landing page screenshot to reference for design inspiration, colors, layout, and visual style:"
137+
text_part=prompt+screenshot_prompt
138+
img=Image.open(screenshot_path)
139+
w,h=img.size
140+
img=img.crop(((w-min(w,h))//2,(h-min(w,h))//2,(w+min(w,h))//2,(h+min(w,h))//2))
141+
contents=[text_part,img]
142+
143+
response = self.client.models.generate_content(
144+
model="gemini-2.5-flash-image-preview",
145+
contents=contents
146+
)
147+
148+
for part in response.candidates[0].content.parts:
149+
if hasattr(part, 'inline_data') and part.inline_data:
150+
return part.inline_data.data
151+
152+
except Exception as e:
153+
print(f"❌ Image generation failed: {e}")
154+
155+
async def save_results(self, ad_image: bytes, prompt: str, analysis: str, url: str, timestamp: str) -> str:
156+
image_path = self.output_dir / f"ad_{timestamp}.png"
157+
with open(image_path, 'wb') as f:
158+
f.write(ad_image)
159+
160+
analysis_path = self.output_dir / f"analysis_{timestamp}.txt"
161+
async with aiofiles.open(analysis_path, 'w', encoding='utf-8') as f:
162+
await f.write(f"URL: {url}\n\n")
163+
await f.write("BROWSER-USE ANALYSIS:\n")
164+
await f.write(analysis)
165+
await f.write("\n\nGENERATED PROMPT:\n")
166+
await f.write(prompt)
167+
168+
return str(image_path)
169+
170+
def open_image(image_path: str):
171+
"""Open image with default system viewer"""
172+
try:
173+
if sys.platform.startswith('darwin'):
174+
# macOS
175+
subprocess.run(['open', image_path], check=True)
176+
elif sys.platform.startswith('win'):
177+
# Windows
178+
os.startfile(image_path)
179+
else:
180+
# Linux
181+
subprocess.run(['xdg-open', image_path], check=True)
182+
except Exception as e:
183+
print(f"❌ Could not open image: {e}")
184+
185+
async def create_ad_from_landing_page(url: str, debug: bool = False):
186+
analyzer = LandingPageAnalyzer(debug=debug)
187+
generator = AdGenerator()
188+
189+
try:
190+
print(f"🚀 Analyzing {url}...")
191+
page_data = await analyzer.analyze_landing_page(url)
192+
193+
prompt = generator.create_ad_prompt(page_data['analysis'])
194+
ad_image = await generator.generate_ad_image(prompt, page_data.get('screenshot_path'))
195+
result_path = await generator.save_results(
196+
ad_image,
197+
prompt,
198+
page_data['analysis'],
199+
url,
200+
page_data['timestamp']
201+
)
202+
203+
print(f"🎨 Generated ad: {result_path}")
204+
if page_data.get('screenshot_path'):
205+
print(f"📸 Page screenshot: {page_data['screenshot_path']}")
206+
open_image(result_path)
207+
208+
return result_path
209+
210+
except Exception as e:
211+
print(f"❌ Error: {e}")
212+
raise
213+
214+
215+
if __name__ == "__main__":
216+
url = args.url
217+
if not url:
218+
url = input("🔗 Enter URL: ").strip() or "https://www.apple.com/iphone-16-pro/"
219+
220+
asyncio.run(create_ad_from_landing_page(url, debug=args.debug))
1.36 MB
Loading
1.45 MB
Loading

0 commit comments

Comments
 (0)