Use HTMLSession only in a normal synchronous script; use AsyncHTMLSession with await arender() whenever an asyncio event loop is already running. The usual synchronous sequence is to fetch a page, call response.html.render(), and then read the updated HTML. If rendering fails, first determine whether the problem is missing JavaScript execution, an active event loop, an incomplete Chromium download, or a page that needs more time or interaction.
What requests-html rendering actually does
An ordinary HTMLSession.get() request downloads the server response. It does not execute the page’s client-side JavaScript, so content inserted by React, Vue, Angular, deferred scripts, or other browser code may be absent from response.html.
requests-html adds JavaScript support through Chromium, launched by pyppeteer. The documented render() operation reloads the response in Chromium, executes JavaScript, and replaces the parsed HTML with the updated version. Rendering is therefore a second browser-backed request, not a different selector method.
Check whether JavaScript is the real issue
- Fetch the page without rendering.
- Print or inspect
response.html.html. - Look for the text, element, or data attribute you expected.
- If the shell exists but the data is missing, render before selecting it.
If the content is already present in the initial HTML, a selector, encoding, redirect, authentication, or ordinary HTTP problem is more likely than a JavaScript-rendering problem.
#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Working synchronous example
Run this in a regular Python script, not inside a running notebook or async web handler:
from requests_html import HTMLSession
session = HTMLSession()
response = session.get("https://example.com")
response.html.render()
print(response.html.html)
print(response.html.text)
The first call to render() may download Chromium into pyppeteer’s home directory. Allow that download to finish, and make sure the process can launch the downloaded browser. On Linux, the package documentation warns that additional system libraries may be required; the exact list depends on the distribution and environment.
Select after rendering
from requests_html import HTMLSession
session = HTMLSession()
r = session.get("https://example.com/app")
r.html.render()
headline = r.html.find("h1", first=True)
if headline:
print(headline.text)
else:
print("The h1 was not present after rendering")
Do not cache a selector result obtained before rendering and expect it to update automatically. Render first, then call find(), xpath(), or read .text and .html.
Fix: “Cannot use HTMLSession within an existing event loop”
This traceback means the synchronous session is being used where asyncio is already running. Common examples include Jupyter notebooks, asynchronous web frameworks, async task runners, and applications whose entry point already uses asyncio. It is not a selector error and adding a longer sleep will not solve it.
Use AsyncHTMLSession in async code
from requests_html import AsyncHTMLSession
async def load_page():
session = AsyncHTMLSession()
response = await session.get("https://example.com")
await response.html.arender()
print(response.html.html)
return response
Call load_page() from your existing async application. In a notebook, use await load_page() in a cell that supports top-level await. The API shape changes in two places: create AsyncHTMLSession, await session.get(), and await response.html.arender().
Rank #2
Choosing between the two APIs
| Situation | Session | Render call | Reason |
|---|---|---|---|
| Standalone synchronous script | HTMLSession |
response.html.render() |
No event loop is running. |
| Notebook or async framework | AsyncHTMLSession |
await response.html.arender() |
Works with the active asyncio loop. |
Do not mix a synchronous HTMLSession into an already-running loop, and do not call the asynchronous method without awaiting it. If the surrounding application is synchronous, keep the complete flow synchronous rather than creating an unnecessary loop.
Handle content that appears after the first render
Some pages execute JavaScript immediately but fetch their data later, reveal content only after scrolling, or require a small browser action. The rendering API documents three relevant controls.
Wait with sleep
response.html.render(sleep=2)
This waits after the browser loads the page. Choose a delay based on the page’s observed behavior; there is no universal value that guarantees every site has finished its network requests.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsScroll with scrolldown
response.html.render(scrolldown=5, sleep=1)
Repeated scrolling can trigger lazy-loaded images or infinite-scroll requests. It does not repair a missing Chromium installation or an event-loop mismatch.
Run a page script
script = """
() => {
const button = document.querySelector('.load-more');
if (button) button.click();
}
"""
response.html.render(script=script, sleep=2)
Use a script only when the page needs a browser action. Confirm that the selector and action match the current page; a script that runs without an exception can still do nothing if the element is absent.
Rank #3
Chromium download and startup failures
On the first render, pyppeteer normally downloads a Chromium build. A blocked network, interrupted download, restricted home directory, or insufficient disk space can leave an incomplete browser and cause startup errors.
Diagnostic sequence
- Run a minimal render against a simple public page.
- Watch the first-run output and verify that the download completes rather than stopping partway through.
- Run the same script again; a completed browser should not need to be downloaded each time.
- Check that the process user can read and execute the browser files.
- On Linux, consult the package documentation and your distribution’s requirements for shared libraries and display-related dependencies.
Do not assume one universal package list or browser flag applies to every operating system. Capture the complete traceback, Python version, operating system, and pyppeteer/browser details before changing launch settings.
Free tools Windows power users keep installed
One-click scans. No signup required.
When Chromium closes or the protocol disappears
Messages about an unexpected browser exit or a lost protocol connection have several possible causes: an incomplete browser installation, missing platform libraries, runtime incompatibility, resource limits, or behavior on the target page. Historical issue reports show that these failures occur, but they do not establish one repair that works everywhere.
- Reproduce with a simple page to separate environment failure from site-specific behavior.
- Check the full traceback instead of acting on the final line alone.
- Verify that the browser executable exists and is runnable by the same user as the Python process.
- Check memory, disk, sandbox, and container restrictions imposed by your host.
- Test the package in a clean virtual environment if the application has conflicting dependencies.
Avoid presenting a browser flag or operating-system command as a guaranteed fix unless it has been validated in your own deployment environment.
Compatibility expectations and version caution
The package documentation is old. Its PyPI description lists Python 3.6 support, and the stable documentation identifies version 0.3.4. Those statements do not prove that current Python releases, Chromium builds, or operating systems are supported. Treat compatibility with newer runtimes as an environment-specific question.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Record a reproducible environment
- Python version and architecture.
requests-html, pyppeteer, and related package versions.- Operating system, distribution, and container image.
- Whether code runs in a notebook, worker, web server, or command-line process.
- Target URL and whether it requires authentication, a proxy, or a special user agent.
These details turn a vague “render failed” report into a testable compatibility case.
A complete troubleshooting flow
- Inspect the unrendered response. Confirm status, final URL, and whether the expected content is missing from the initial HTML.
- Match the session to the execution context. Use
HTMLSessionin a plain script; useAsyncHTMLSessionandarender()in an active loop. - Render a minimal page. This identifies Chromium installation and platform problems independently of the target site.
- Render the target page. Only then add
sleep,scrolldown, or a script if the page demonstrably needs them. - Inspect the resulting HTML. Confirm that the data exists before debugging selectors.
- Compare environments. If a simple page works locally but not in deployment, investigate libraries, permissions, resource limits, and runtime differences.
Performance, reliability, and operational trade-offs
Browser rendering is heavier than an ordinary HTTP request because it starts or controls Chromium and executes page code. Use the non-rendered request when the data is present in server HTML. Render only the pages or steps that require JavaScript.
Reduce unnecessary work
- Fetch once and render once; do not repeatedly render the same response while experimenting with selectors.
- Use a targeted selector after rendering rather than processing the entire document when possible.
- Keep waits short but observable, then increase them only when page behavior requires it.
- Separate browser startup checks from production crawling so failures are easier to classify.
Know what rendering cannot solve
render() does not bypass authentication, bot protection, CAPTCHAs, broken page JavaScript, unavailable APIs, or a selector that no longer matches. It also cannot compensate for a browser that cannot start. Diagnose those conditions separately.
Or skip the browser setup
If your goal is simply to obtain a clean screenshot rather than parse a page inside Python, ScreenshotNeo provides a website screenshot API and MCP server. A single request returns PNG, JPEG, WebP, or PDF output:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for parameters and response details. The same call in Python is:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Best Value
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
And in Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Before capture, ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server includes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.
Frequently asked questions
Frequently Asked Questions
Why is my selector empty even though the page looks populated in a browser?
A normal HTTP fetch may contain only the page shell. Inspect the unrendered HTML, call the appropriate render method, and run the selector afterward.
Can I use HTMLSession in Jupyter?
Use AsyncHTMLSession with awaited get() and arender() when the notebook already has an asyncio loop.
Does increasing sleep fix every rendering error?
No. It helps content that appears later, but it cannot fix an active-loop mismatch, missing Chromium libraries, an incomplete download, or a broken target page.
Is requests-html guaranteed to support current Python versions?
No guarantee is established. The package documentation is old and lists Python 3.6, so verify the combination of Python, Chromium, operating system, and dependencies in your environment.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

