Playwright crawler with Camoufox
This example demonstrates how to integrate Camoufox into PlaywrightCrawler
using BrowserPool
with custom PlaywrightBrowserPlugin
.
Camoufox is a stealthy minimalistic build of Firefox. For details please visit its homepage https://camoufox.com/ . To be able to run this example you will need to install camoufox, as it is external tool, and it is not part of the crawlee. For installation please see https://pypi.org/project/camoufox/.
Warning! Camoufox is using custom build of firefox. This build can be hundreds of MB large.
You can either pre-download this file using following command python3 -m camoufox fetch
or camoufox will download it automatically once you try to run it, and it does not find existing binary.
For more details please refer to: https://github.com/daijro/camoufox/tree/main/pythonlib#camoufox-python-interface
Project template - It is possible to generate project with Python code which includes Camoufox integration into crawlee through crawlee cli. Call crawlee create
and pick Playwright-camoufox
when asked for Crawler type.
The example code after PlayWrightCrawler instantiation is similar to example describing the use of Playwright Crawler. The main difference is that in this example Camoufox will be used as the browser through BrowserPool.
import asyncio
# Camoufox is external package and needs to be installed. It is not included in crawlee.
from camoufox import AsyncNewBrowser
from typing_extensions import override
from crawlee.browsers import BrowserPool, PlaywrightBrowserController, PlaywrightBrowserPlugin
from crawlee.crawlers import PlaywrightCrawler, PlaywrightCrawlingContext
class CamoufoxPlugin(PlaywrightBrowserPlugin):
"""Example 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, **self._browser_launch_options),
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:
crawler = PlaywrightCrawler(
# Limit the crawl to max requests. Remove or increase it for crawling all links.
max_requests_per_crawl=10,
# Custom browser pool. This gives users full control over browsers used by the crawler.
browser_pool=BrowserPool(plugins=[CamoufoxPlugin()]),
)
# Define the default request handler, which will be called for every request.
@crawler.router.default_handler
async def request_handler(context: PlaywrightCrawlingContext) -> None:
context.log.info(f'Processing {context.request.url} ...')
# Extract some data from the page using Playwright's API.
posts = await context.page.query_selector_all('.athing')
for post in posts:
# Get the HTML elements for the title and rank within each post.
title_element = await post.query_selector('.title a')
# Extract the data we want from the elements.
title = await title_element.inner_text() if title_element else None
# Push the extracted data to the default dataset.
await context.push_data({'title': title})
# Find a link to the next page and enqueue it if it exists.
await context.enqueue_links(selector='.morelink')
# Run the crawler with the initial list of URLs.
await crawler.run(['https://news.ycombinator.com/'])
if __name__ == '__main__':
asyncio.run(main())