If you've ever tried scraping a JavaScript-heavy website with Python, you know the pain. Regular requests won't cut it. You need a real browser, but managing proxies, handling CAPTCHAs, and dealing with blocks? That's where things get messy. Here's the good news: combining Pyppeteer with ScraperAPI means you get browser automation without the headache of infrastructure management.
This guide shows you the exact setup—no fluff, just working code you can copy-paste and run.
Pyppeteer gives you headless Chrome control in Python. ScraperAPI handles the annoying parts—proxy rotation, CAPTCHA solving, and anti-bot detection. Together, they let you scrape dynamic sites at scale without building your own proxy infrastructure or worrying about getting blocked.
The trick is simple: point Pyppeteer's browser at ScraperAPI's proxy servers. Every request goes through their network automatically. You write scraping logic; they handle the rest.
This is the recommended approach. Configure your browser to route all traffic through ScraperAPI's proxy servers—just like you'd use any other proxy.
First, grab Pyppeteer:
bash
pip install pyppeteer
Then set up the proxy routing:
python
import asyncio
from pyppeteer import launch
API_KEY = 'YOUR_API_KEY'
async def main():
browser = await launch({
'args': [
f'--proxy-server=http://proxy-server.scraperapi.com:8001'
]
})
page = await browser.newPage()
await page.authenticate({
'username': 'scraperapi',
'password': API_KEY
})
await page.goto('http://quotes.toscrape.com/')
quotes = await page.evaluate('''() => {
return Array.from(document.querySelectorAll('.quote')).map(quote => ({
text: quote.querySelector('.text').innerText,
author: quote.querySelector('.author').innerText
}));
}''')
print(quotes)
await browser.close()
asyncio.run(main())
That's it. Every Pyppeteer request now flows through ScraperAPI. No manual proxy switching, no CAPTCHA nightmares. Just clean scraping.
Sample output looks like this:
json
[
{
"text": ""The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking."",
"author": "Albert Einstein"
},
{
"text": ""It is our choices, Harry, that show what we truly are, far more than our abilities."",
"author": "J.K. Rowling"
},
{
"text": ""There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle."",
"author": "Albert Einstein"
}
]
If you just need HTML snapshots without full browser interaction, the ScraperAPI SDK offers a faster path:
Install the SDK:
bash
pip install scraperapi-sdk
Grab HTML and parse with Pyppeteer:
python
import asyncio
from pyppeteer import launch
from scraperapi_sdk import ScraperAPIClient
API_KEY = 'YOUR_API_KEY'
client = ScraperAPIClient(API_KEY)
async def main():
browser = await launch(headless=True)
page = await browser.newPage()
html = client.get('http://quotes.toscrape.com/')
await page.setContent(html)
quotes = await page.evaluate('''() => {
return Array.from(document.querySelectorAll('.quote')).map(quote => ({
text: quote.querySelector('.text').innerText,
author: quote.querySelector('.author').innerText
}));
}''')
print(quotes)
await browser.close()
asyncio.run(main())
This approach is cleaner when you don't need to click buttons or interact with JavaScript events. ScraperAPI fetches rendered HTML; Pyppeteer handles DOM parsing locally.
When dealing with tougher targets, you might need premium proxies or geo-specific IPs. If you're working with sites that demand higher success rates or specific geographic targeting, you can leverage advanced proxy features to improve reliability. 👉 See how ScraperAPI's premium features handle complex scraping scenarios with better success rates and geographic control.
ScraperAPI accepts custom headers for fine-tuned control:
python
await page.setExtraHTTPHeaders({
'X-ScraperAPI-Premium': 'true',
'X-ScraperAPI-Country': 'us',
'X-ScraperAPI-Session': '123'
})
Available parameters:
X-ScraperAPI-Premium: Use premium residential proxies
X-ScraperAPI-Country: Target specific countries
X-ScraperAPI-Session: Maintain session across requests
X-ScraperAPI-Render: Force JavaScript rendering
Free plans give you 5 concurrent threads. Paid plans scale higher. Match your concurrent browser count to your plan's limits:
python
import asyncio
from pyppeteer import launch
API_KEY = 'YOUR_API_KEY'
CONCURRENT_BROWSERS = 5 # Match this to your plan
async def scrape_page(url):
browser = await launch({
'args': [f'--proxy-server=http://proxy-server.scraperapi.com:8001']
})
page = await browser.newPage()
await page.authenticate({
'username': 'scraperapi',
'password': API_KEY
})
await page.goto(url)
quotes = await page.evaluate('''() => {
return Array.from(document.querySelectorAll('.quote')).map(quote => ({
text: quote.querySelector('.text').innerText,
author: quote.querySelector('.author').innerText
}));
}''')
await browser.close()
return {'url': url, 'quotes': quotes}
async def scrape_multiple_pages(urls):
semaphore = asyncio.Semaphore(CONCURRENT_BROWSERS)
async def scrape_with_semaphore(url):
async with semaphore:
return await scrape_page(url)
tasks = [scrape_with_semaphore(url) for url in urls]
results = await asyncio.gather(*tasks)
print(results)
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
'http://quotes.toscrape.com/page/3/',
]
asyncio.get_event_loop().run_until_complete(scrape_multiple_pages(urls))
Handling failures gracefully:
Most requests succeed first try. When they don't, retry logic saves the day:
python
import asyncio
from pyppeteer import launch
API_KEY = 'YOUR_API_KEY'
MAX_RETRIES = 3
async def scrape_with_retry(url, retries=MAX_RETRIES):
for i in range(retries):
try:
browser = await launch({
'args': [f'--proxy-server=http://proxy-server.scraperapi.com:8001']
})
page = await browser.newPage()
await page.authenticate({
'username': 'scraperapi',
'password': API_KEY
})
await page.goto(url, {'timeout': 60000})
quotes = await page.evaluate('''() => {
return Array.from(document.querySelectorAll('.quote')).map(quote => ({
text: quote.querySelector('.text').innerText,
author: quote.querySelector('.author').innerText
}));
}''')
await browser.close()
return quotes
except Exception as e:
print(f"Attempt {i + 1} failed: {e}")
if i == retries - 1:
raise e
async def main():
quotes = await scrape_with_retry('http://quotes.toscrape.com/')
print(quotes)
asyncio.run(main())
Pyppeteer + ScraperAPI solves headless scraping problems you didn't want to deal with:
Proxy mode works best for interactive scraping—clicking buttons, filling forms, triggering JavaScript events
SDK mode excels at grabbing rendered HTML fast, then parsing locally
Configure concurrency based on your plan's thread limits
Add retry logic because occasional failures happen
Custom headers unlock premium features when basic scraping hits walls
JavaScript-heavy sites don't stand a chance against this setup. You get browser automation with enterprise-grade proxy infrastructure, all without managing servers or debugging network issues. That's the beauty of letting ScraperAPI handle the messy parts while you focus on extracting data.
For projects requiring reliable headless automation at scale, ScraperAPI's proxy network and browser rendering capabilities eliminate the infrastructure headaches that usually slow down scraping projects.