Skip to content

How to Run Playwright in Jupyter Notebooks: The Ultimate Guide

Are you a web developer or data scientist looking to automate browsing and scrape websites using Jupyter notebooks? Playwright is a powerful tool for web automation, but running its default synchronous API in the asynchronous Jupyter environment poses some challenges. Don‘t worry though – with a few adjustments, we can get Playwright working smoothly in Jupyter and unlock its full potential.

In this ultimate guide, I‘ll walk you through everything you need to know to run Playwright in Jupyter notebooks like a pro. We‘ll cover installation, integrating with Jupyter‘s asyncio event loop, converting Playwright scripts to async, debugging tips, best practices, and more. By the end, you‘ll be ready to create robust and efficient web automation notebooks with Playwright. Let‘s dive in!

What is Playwright?

Playwright is an open-source library for automating web browsers, developed by Microsoft. It allows you to programmatically interact with web pages in Chromium, Firefox, and WebKit browsers. With Playwright, you can automate tasks like scraping data, testing web apps, generating PDFs, and more, all with a single API.

Some key benefits of Playwright include:

  • Cross-browser support
  • Fast and reliable execution
  • Strong auto-waiting and timeout mechanisms
  • Powerful selector options like CSS, XPath, and text
  • Automatic pages, domains and permissions
  • Ability to emulate mobile devices and geolocation
  • Simple setup and no external dependencies

Playwright has quickly gained popularity due to its ease of use and rich feature set. It‘s an excellent choice for both beginners and experienced developers looking to automate web interactions.

The Challenge with Running Playwright in Jupyter

While Playwright is simple to use in standalone Python scripts, running it in Jupyter notebooks presents a hurdle. By default, Playwright uses a synchronous API, meaning it blocks execution until each command completes. However, Jupyter notebooks run on an asyncio event loop to manage multiple tasks concurrently.

If we use the synchronous Playwright API in a Jupyter notebook, we‘ll encounter errors like:

RuntimeError: Timeout context manager should be used inside a task

This occurs because the synchronous API blocks the event loop, preventing Jupyter from running other tasks. To fix this, we need to use Playwright‘s async API instead, which integrates properly with the event loop.

Installing Playwright in Jupyter

Before we start coding, let‘s install the necessary packages in our Jupyter environment. Open a new notebook and run:

!pip install playwright
!pip install nest_asyncio

This will install the Playwright library and nest_asyncio, which we‘ll use later to integrate with the event loop.

Enabling Playwright‘s Async API

To use the async API, we first need to import it along with asyncio:

import asyncio 
from playwright.async_api import async_playwright

We‘ll also enable the asyncio event loop integration with nest_asyncio:

import nest_asyncio
nest_asyncio.apply()

This allows us to use async/await syntax in the notebook without issues.

Launching a Browser with the Async API

Now let‘s see how to launch a browser with the async API:

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        page = await browser.new_page()
        await page.goto("https://example.com")
        await page.screenshot(path="example.png")
        await browser.close()

await main()

Let‘s break this down:

  1. We define an async function called main() to hold our Playwright code.

  2. Inside main(), we use async with to create an async_playwright context manager. This handles automatically launching and closing the browser for us.

  3. We launch a Chromium browser with p.chromium.launch(), passing headless=False so we can see the browser window.

  4. We create a new page with browser.new_page() and navigate to a URL with page.goto().

  5. We take a screenshot of the page with page.screenshot().

  6. Finally, we close the browser with browser.close() (this is handled by the context manager when using async with).

After defining main(), we await it to run our async code.

The key differences from the synchronous API are:

  • Using async def to define an async function
  • Awaiting all Playwright commands with await
  • Using async with to manage the browser‘s lifecycle

By structuring our code this way, it will run smoothly in the Jupyter notebook without blocking the event loop.

Locating Elements on the Page

Playwright provides several methods for locating elements on a page, such as:

  • page.query_selector(): Find an element by CSS selector
  • page.query_selector_all(): Find all elements matching a CSS selector
  • page.xpath(): Find an element by XPath
  • page.text_content(): Get the text content of an element

For example, to find and click a button:

button = await page.query_selector("button")
await button.click()

Or to extract text from a heading:

heading = await page.query_selector("h1")
text = await heading.text_content()
print(text)

When locating elements, Playwright will auto-wait for them to be attached to the page, so you don‘t need explicit waits in most cases. This makes element handling very concise.

Debugging Tips

Debugging async code in notebooks can be tricky. Here are a few tips:

  • Use the Jupyter magic command %%debug to open an interactive debugger if an exception occurs in an async function.

  • Set headless=False when launching the browser to see what‘s happening.

  • Use print() statements liberally to check variable values.

  • Consult the Playwright debug logs by setting the PWDEBUG environment variable:

PWDEBUG=1 jupyter notebook

This will print verbose logs to the terminal where you started Jupyter.

Best Practices

Here are some best practices to keep in mind when using Playwright in Jupyter:

  • Use context managers (async with) to automatically manage the browser and pages. This ensures resources are closed properly.

  • Keep your code modular by defining reusable functions for common tasks.

  • Use relative URLs when possible to make your code more portable.

  • Avoid hard-coded delays with time.sleep(). Instead, use Playwright‘s built-in waiting mechanisms.

  • Preserve user data like cookies and local storage between sessions if needed for your use case.

  • Close browsers and pages when finished to free up resources.

Advanced Topics

We‘ve covered the basics of running Playwright in Jupyter, but there‘s much more you can do! Some advanced topics to explore:

  • Handling multiple pages and browser contexts
  • Interacting with iframes and shadow DOM elements
  • Filling and submitting forms
  • Capturing and inspecting network traffic
  • Emulating mobile devices
  • Generating PDFs of pages
  • Using proxies for web scraping
  • Integrating with other libraries like Pandas for data analysis

I encourage you to consult the Playwright documentation to learn about all the possibilities.

Putting It All Together

Here‘s a complete example that demonstrates several Playwright concepts in a Jupyter notebook:

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        page = await browser.new_page()

        # Navigate to a URL
        await page.goto("https://scrapingbee.com/blog/")

        # Find the first h2 element and print its text
        h2 = await page.query_selector("h2")
        text = await h2.text_content()
        print(f"First h2 text: {text}")

        # Find the search input and enter a query
        search_input = await page.query_selector(‘input[type="search"]‘)
        await search_input.fill("python")
        await search_input.press("Enter")
        await page.wait_for_url("**/search/**")

        # Take a screenshot of the search results
        await page.screenshot(path="search_results.png")

        await browser.close()

await main()

This script navigates to a blog, extracts text from a heading, enters a search query, and takes a screenshot of the search results. Feel free to adapt it for your own purposes!

Conclusion

In this guide, we‘ve learned how to run Playwright effectively in Jupyter notebooks using the async API. We covered installation, event loop integration, browser launching, element locating, debugging, best practices, and more.

With this knowledge, you‘re ready to create powerful and efficient web automation notebooks. The possibilities are endless – from scraping data for analysis to automating complex user flows.

Remember, this is just the beginning. Keep exploring Playwright‘s features and integrating it with other tools to build even more impressive projects. Don‘t be afraid to experiment and consult the documentation when stuck.

Happy coding, and may your Playwright notebooks run smoothly!

Further Reading

Join the conversation

Your email address will not be published. Required fields are marked *