Skip to content

Fully Asynchronous CDP based Chrome driver over POSIX pipes

License

Notifications You must be signed in to change notification settings

sohaib17/pypecdp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pypecdp

Fully async Chrome DevTools Protocol over POSIX pipes with a high-level Browser/Tab/Elem API for Python 3.12+ on Linux.

Chrome automation using --remote-debugging-pipe (no websockets, no ports, just pipes) with bundled CDP protocol classes.

Inspired by playwright-python, python-cdp and nodriver.

Documentation

Full documentation is auto-generated and published at: https://sohaib17.github.io/pypecdp/

Features

  • Fully Async: Built from ground up with asyncio for concurrent operations
  • Fast: Direct pipe communication via file descriptors - no websockets, no network overhead
  • Minimal dependencies: Only one dependency (deprecated) - lightweight and easy to install
  • Secure: Browser only communicates over local pipes, no open ports accessible to other processes
  • No zombies: No risk of orphaned Chrome processes if code crashes - automatic lifecycle management
  • Linux focused: Leverages POSIX pipes and process management

Install

pip install pypecdp

Install Chromium if needed:

# Ubuntu/Debian
sudo apt-get install chromium-browser

# Fedora
sudo dnf install chromium

# Arch
sudo pacman -S chromium

Quick Start

import asyncio
from pypecdp import Browser

async def main():
    # Launch browser
    browser = await Browser.start(
        chrome_path="chromium",
        headless=True
    )
    
    # Navigate to a URL (creates/reuses a tab)
    tab = await browser.navigate("https://example.com")
    
    # Select and interact with elements
    h1 = await tab.wait_for_elem("h1")
    if h1:
        text = await h1.text()
        print(f"Page heading: {text}")
    
    # Evaluate JavaScript
    result = await tab.eval("document.title")
    print(f"Title: {result.value}")
    
    # Close browser
    await browser.close()

asyncio.run(main())

Usage Guide

Browser Management

from pypecdp import Browser, Config

# Simple start
browser = await Browser.start(chrome_path="chromium", headless=True)

# Advanced configuration
config = Config(
    chrome_path="/usr/bin/google-chrome",
    user_data_dir="/tmp/chrome-profile",
    clean_data_dir=False,  # Preserve existing profile data
    headless=True,
    extra_args=["--no-sandbox", "--disable-gpu"],
    env={"LANG": "en_US.UTF-8"}
)
browser = await Browser.start(config=config)

# Close browser
await browser.close()

Note: By default, clean_data_dir=True which removes any existing user data directory before starting. Set it to False to preserve cookies, cache, and other browser state between runs.

Element Interactions

# Finding and clicking elements
button = await tab.wait_for_elem("button.submit")
if button:
    await button.click()

# Clicking elements that cause navigation
link = await tab.wait_for_elem('a[href="/next-page"]')
if link:
    # click() returns the top-level Tab after navigation
    current_tab = await link.click()
    if current_tab:
        # Wait for the new page to load
        await current_tab.wait_for_event(cdp.page.LoadEventFired, timeout=10.0)
        print(f"Navigated to: {current_tab.url}")

Customization via Inheritance

pypecdp is designed to be easily extended through OOP inheritance:

from pypecdp import Browser, Tab, Elem

class MyElem(Elem):
    async def click_and_wait(self, timeout=10.0):
        """Click and wait for page load."""
        tab = await self.click()
        if tab:
            await tab.wait_for_event(cdp.page.LoadEventFired, timeout=timeout)
        return tab

class MyTab(Tab):
    elem_class = MyElem  # Use custom Elem class
    
    async def search(self, query):
        """Custom search method."""
        search_box = await self.wait_for_elem("#search")
        await search_box.type(query)

class MyBrowser(Browser):
    tab_class = MyTab  # Use custom Tab class

# Use your custom classes
browser = await MyBrowser.start()
tab = await browser.navigate("https://example.com")
# tab is now MyTab instance, elements are MyElem instances

See example/customize_pypecdp.py for working example.

Event Handlers

from pypecdp import cdp

# Tab-level events (requires domain enable!)
await tab.send(cdp.runtime.enable())  # Required for runtime events!

async def on_console(event):
    print(f"Console {event.type_}: {event.args}")

tab.on(cdp.runtime.ConsoleAPICalled, on_console)

# Browser-level events
async def on_target_created(event):
    info = event.target_info
    print(f"Target created: {info.type_} - {info.url}")

browser.on(cdp.target.TargetCreated, on_target_created)

Logging

pypecdp uses Python's standard logging module. Configure via environment variables:

# Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
export PYPECDP_LOGLEVEL=DEBUG

# Set custom logger name
export PYPECDP_LOGGER=myapp.browser

Or configure the logger directly in Python:

from pypecdp import logger
import logging

# Set log level
logger.setLevel(logging.DEBUG)

# Add custom handler
handler = logging.FileHandler("pypecdp.log")
logger.addHandler(handler)

Error Handling

try:
    browser = await Browser.start()
    tab = await browser.navigate("https://example.com")
    
    # Your automation code
    elem = await tab.wait_for_elem("button")
    if elem:
        await elem.click()
    
    result = await tab.eval("document.title")
    
except ReferenceError as e:
    # Element's tab is no longer available (closed/detached)
    print(f"Target Error: {e}")
except RuntimeError as e:
    # CDP protocol errors
    print(f"CDP Error: {e}")
except ConnectionError as e:
    # Connection lost
    print(f"Connection Error: {e}")
except Exception as e:
    # Other errors
    print(f"Error: {e}")
finally:
    # Always cleanup
    await browser.close()

Requirements

  • Python 3.12+
  • Linux (uses POSIX pipes and preexec_fn)
  • Chromium or Google Chrome

Links

License

MIT License - See LICENSE file for details.

Contributing

Contributions welcome! This project aims to provide a clean, type-safe interface to Chrome automation on Linux.

About

Fully Asynchronous CDP based Chrome driver over POSIX pipes

Topics

Resources

License

Stars

Watchers

Forks

Languages