Recommended Free Tools
Use await page.title() after navigation to read one document title. To inspect tabs already open in the same browser, call await browser.pages() and gather each page’s title. That method returns visible page objects, not every possible Chromium target or background page.
What “every page title” means in Pyppeteer
There are two common jobs behind this question:
- Fetch a title from a URL: open a page, navigate with
page.goto(), then awaitpage.title(). - Fetch titles from tabs already open: ask the browser for its page objects with
browser.pages(), then calltitle()on each object.
Pyppeteer’s documented Browser.pages() method enumerates page objects that are visible to that API. It excludes certain non-visible pages, such as background pages. Therefore, “every” means every page object returned by browser.pages(), not every browser target Chromium may have created.
| Goal | Call | Important condition |
|---|---|---|
| Read one title | await page.title() |
Navigate first when the URL is not already loaded. |
| Read all currently exposed pages | await browser.pages() |
Background and other non-visible pages are not included. |
| Inspect a title set by JavaScript later | Use a site-specific wait condition before title() |
There is no universal delay that works for every site. |
Install Pyppeteer and prepare Chromium
Install the package into the Python environment that will run your script:
python -m pip install pyppeteer
The project README describes Pyppeteer as requiring Python 3.8 or newer. It may download a compatible Chromium binary the first time it runs if it cannot find a suitable browser. To download Chromium before running your program, use:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
pyppeteer-install
You can also point launch() at an installed Chrome or Chromium executable. The exact executable path is machine-specific, so verify the path for your operating system and installed browser version.
The same README currently describes Pyppeteer as unmaintained and points new projects toward Playwright Python. That is a maintenance consideration rather than a statement that existing Pyppeteer scripts cannot run. If you are maintaining an established script, test the installed package and browser combination before changing libraries.
Fetch one page title
This is the smallest complete program. It starts a browser, opens a tab, navigates, reads the title, prints it, and closes the browser even if an exception occurs.
import asyncio
from pyppeteer import launch
async def main():
browser = await launch()
try:
page = await browser.newPage()
await page.goto('https://example.com')
title = await page.title()
print(title)
finally:
await browser.close()
asyncio.run(main())
Save it as title.py and run python title.py. The navigation is awaited before the title call, so the document has reached the point reported by page.goto() before the script reads document.title through Pyppeteer’s title method.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Use a known browser executable
If you do not want Pyppeteer to locate or download Chromium, pass the installed executable path:
browser = await launch(executablePath='/absolute/path/to/chrome')
Keep the rest of the program unchanged. On a shared or automated machine, make the path a configuration value rather than hard-coding a path that exists only on a developer laptop.
Fetch titles from all pages already open
When another part of your program has already created tabs, enumerate those page objects and read their titles concurrently:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch()
try:
first = await browser.newPage()
second = await browser.newPage()
await asyncio.gather(
first.goto('https://example.com'),
second.goto('https://example.org'),
)
pages = await browser.pages()
titles = await asyncio.gather(*(page.title() for page in pages))
for page, title in zip(pages, titles):
print(f'{page.url}t{title}')
finally:
await browser.close()
asyncio.run(main())
pages and titles have matching order in this program, so zip() keeps each URL paired with its title. Reusing one browser also lets you inspect tabs that were opened by earlier work in that same browser instance.
Keep one failed title from hiding the others
If you want a result for every page even when one title call raises an exception, request return values for exceptions and report them individually:
pages = await browser.pages()
results = await asyncio.gather(
*(page.title() for page in pages),
return_exceptions=True,
)
for page, result in zip(pages, results):
if isinstance(result, Exception):
print(f'{page.url}tERROR: {result}')
else:
print(f'{page.url}t{result}')
This pattern does not make an inaccessible page readable; it prevents one failure from cancelling the whole collection and tells you which page needs attention.
Titles that change after navigation
Some sites set an initial title in the HTML and replace it later with JavaScript. Reading immediately after await page.goto() can therefore capture the initial value.
Do not add an arbitrary sleep and assume it is reliable for all sites. Instead, identify the condition that signals completion for the site you are automating, then wait for that condition before calling page.title(). For example, if the application has a stable title once a dashboard heading appears, wait for that heading using the page-waiting API supported by the Pyppeteer version you installed, then read the title:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →await page.goto('https://example.com/dashboard')
# Replace this with a condition that is meaningful for your application.
await page.waitForFunction(
"document.querySelector('[data-dashboard-ready]') !== null"
)
title = await page.title()
The selector and condition above are illustrative; they must match the application you control or the site’s documented behavior. A delay may be appropriate when the site offers no observable readiness signal, but its value is site-specific and should be validated.
When DOM evaluation is appropriate
Page.title() is the straightforward API for a normal document title. Use page.evaluate() when you specifically need to inspect custom page content or a title-like value that is not the document title. Pyppeteer’s README notes that an evaluate() input can be interpreted as a function or an expression; when an expression is mistakenly treated as a function, the documented guidance is to use force_expr=True.
custom_value = await page.evaluate(
"document.querySelector('[data-custom-title]')?.textContent",
force_expr=True,
)
For ordinary titles, prefer await page.title(); it is clearer and avoids unnecessary DOM scripting.
Collect titles from a list of URLs
If “every” means every URL in an input list rather than every already-open tab, create a page for each URL and keep the browser lifetime outside the loop:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #3
import asyncio
from pyppeteer import launch
URLS = [
'https://example.com',
'https://example.org',
]
async def fetch_title(browser, url):
page = await browser.newPage()
try:
await page.goto(url)
return url, await page.title()
finally:
await page.close()
async def main():
browser = await launch()
try:
results = await asyncio.gather(
*(fetch_title(browser, url) for url in URLS),
return_exceptions=True,
)
for result in results:
print(result)
finally:
await browser.close()
asyncio.run(main())
This reuses one Chromium process while giving each URL its own page. For a large list, limit how many navigation coroutines run at once; opening an unlimited number of tabs can exhaust memory or network resources. A semaphore is a simple way to impose that limit:
limit = asyncio.Semaphore(5)
async def fetch_title_limited(browser, url):
async with limit:
return await fetch_title(browser, url)
Choose the limit from the resources available to your runner and the load your target sites permit. The sources for this procedure do not establish a universal concurrency number.
What Pyppeteer will and will not discover
Visible page objects
await browser.pages() is the documented way to obtain the browser’s page objects. It is suitable for normal tabs and pages created with newPage().
Background and non-visible targets
The method excludes certain non-visible pages, including background pages. If your application depends on those targets, page-title enumeration alone is not a complete inventory of browser activity. Treat the result as the set Pyppeteer exposes through Browser.pages().
Frames are not separate pages
An iframe belongs to its containing page; it is not another item returned by browser.pages(). If you need text inside a frame, that is a different extraction task from fetching the top-level document title.
Troubleshooting
“No module named pyppeteer”
Install Pyppeteer with python -m pip install pyppeteer using the same Python interpreter that runs the script. Virtual environments commonly cause a package to be installed into one interpreter and executed with another.
Chromium download or launch failure
Pyppeteer may download Chromium on first use. Run pyppeteer-install in advance, check that the process can write to its browser cache, or pass the path to an installed Chrome/Chromium binary with executablePath. Also confirm that the binary is executable in the account running the script.
The title is blank or unexpectedly old
- Confirm that
await page.goto(url)completed for the intended URL. - Print
page.urlto detect redirects. - If client-side code changes the title, wait for an application-specific readiness condition before calling
page.title(). - Check that you are reading the top-level page rather than expecting an iframe’s text to become the document title.
One page stops the all-pages run
Use asyncio.gather(..., return_exceptions=True) and log the exception alongside that page’s URL. This preserves successful results while leaving the failed page available for a separate retry or investigation.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
- Used Book in Good Condition
Navigation never finishes
Inspect the URL, redirect chain and browser logs, then apply a timeout policy appropriate to your script. A timeout is a navigation failure, not evidence that a title does not exist; record it separately from a successful page with an empty or unchanged title.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance and reliability practices
- Reuse the browser: start Chromium once and create pages as needed instead of launching a new browser for every URL.
- Close pages: call
page.close()after each URL in a batch so tab count does not grow without bound. - Bound concurrency: use a semaphore or another queue when processing many URLs.
- Pair results with URLs: return both values so redirects and failures can be diagnosed later.
- Wait for real readiness: use a condition tied to the application rather than a universal sleep.
- Always close Chromium: put
browser.close()in afinallyblock to avoid orphaned browser processes.
Because the project README currently calls Pyppeteer unmaintained, pin and test the package and browser versions used by an existing automation job. For a new project, evaluate the alternative named by that README, Playwright Python, instead of assuming Pyppeteer will receive future maintenance.
Or skip the browser setup
If your real deliverable is a rendered screenshot or PDF rather than the title string, ScreenshotNeo provides a website screenshot API. One GET request returns a PNG, JPEG, WebP or PDF, so it does not replace page.title() for extracting text; it replaces the browser-installation and rendering work when you need a visual capture.
The API accepts a URL and access key. See the parameter reference in the ScreenshotNeo documentation.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
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}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to try it without a card.
Frequently Asked Questions
Does ScreenshotNeo return a page title instead of an image?
No. ScreenshotNeo is for rendered screenshots and PDFs. Use Pyppeteer’s page.title() when the value you need is the document title text.
Can one Pyppeteer script mix existing tabs and URLs from a file?
Yes. Call browser.pages() for the existing page objects, then create additional pages with browser.newPage() for file-based URLs while keeping the same browser open.
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.

