Skip to content

Python stealth crawler (Crawlee + Playwright + Camoufox)

A browser crawler built to get past bot protection. Uses Camoufox, a Firefox build that looks like a real user.

Create it locally

$scrapely create my-actor --template python-crawlee-playwright-camoufox

Main file

.PY
my_actor/main.py
from scrapely import Actor
from camoufox import AsyncNewBrowser
from crawlee.browsers import BrowserPool, PlaywrightBrowserController, PlaywrightBrowserPlugin
from crawlee.crawlers import PlaywrightCrawler
from typing_extensions import override

from .routes import router


class CamoufoxPlugin(PlaywrightBrowserPlugin):
    """Browser plugin that uses Camoufox Browser, but otherwise keeps the functionality of PlaywrightBrowserPlugin."""

    @override
    async def new_browser(self) -> PlaywrightBrowserController:
        if not self._playwright:
            raise RuntimeError('Playwright browser plugin is not initialized.')

        return PlaywrightBrowserController(
            browser=await AsyncNewBrowser(self._playwright, headless=True),
            max_open_pages_per_browser=1,  #  Increase, if camoufox can handle it in your use case.
            header_generator=None,  #  This turns off the crawlee header_generation. Camoufox has its own.
        )


async def main() -> None:
    """Define a main entry point for the Scrapely Actor.

    This coroutine is executed using `asyncio.run()`, so it must remain an asynchronous function for proper execution.
    Asynchronous execution is required for communication with Scrapely platform, and it also enhances performance in
    the field of web scraping significantly.
    """
    # Enter the context of the Actor.
    async with Actor:
        # Retrieve the Actor input, and use default values if not provided.
        actor_input = await Actor.get_input() or {}
        start_urls = [
            url.get('url')
            for url in actor_input.get(
                'start_urls',
                [{'url': 'https://crawlee.dev'}],
            )
        ]

        # Exit if no start URLs are provided.
        if not start_urls:
            Actor.log.info('No start URLs specified in Actor input, exiting...')
            await Actor.exit()

        # Create a crawler.
        crawler = PlaywrightCrawler(
            # Limit the crawl to max requests. Remove or increase it for crawling all links.
            max_requests_per_crawl=10,
            browser_pool=BrowserPool(plugins=[CamoufoxPlugin()]),
            # Set the request handler to the request router defined in routes.py.
            request_handler=router,
        )

        # Run the crawler with the starting requests.
        await crawler.run(start_urls)

Python stealth crawler (Crawlee + Playwright + Camoufox)

A browser crawler built to get past bot protection. Uses Camoufox, a Firefox build that looks like a real user.

Included features

  • Scrapely SDK — Actor lifecycle, input, storage and proxies
  • Crawlee — crawling, request queues, retries and storage
  • Playwright — headless browser automation
  • Camoufox — stealth Firefox build
  • Input schema — define and validate the Actor's input (.actor/input_schema.json)
  • Dataset schema — structured results with a preview view (.actor/dataset_schema.json)

Getting started locally

python -m pip install -r requirements.txt
scrapely run      # run the Actor locally

Deploying to Scrapely

Create an Actor from this template in the Scrapely Console (Develop new → template → Git). Scrapely creates a repository in your account, pushes this code, and builds the Actor. Every push to your repository triggers a new build.

G2
4.7/5
Excellent
Trustpilot

The internet is full of data.
Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.

No credit card. No setup calls.