许映洲A friend asked me last week: "I want to search Google from the command line and get the results. How...
A friend asked me last week: "I want to search Google from the command line and get the results. How hard can it be?"
"Easy," I said. "Just use Playwright."
He came back the next day: "I spent all afternoon reading Playwright docs and I still haven't gotten it working. I'm at 30 lines of code and I don't even have the search results yet."
I looked at his code:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://www.google.com');
await page.fill('input[name="q"]', 'node.js best practices');
await page.press('input[name="q"]', 'Enter');
await page.waitForSelector('#search');
const results = await page.$$eval('.g', (els) =>
els.map((el) => ({
title: el.querySelector('h3')?.textContent,
url: el.querySelector('a')?.href,
}))
);
console.log(results);
await browser.close();
})();
23 lines. Just to search Google.
And that code is fragile — Google's search result page structure changes frequently, and those CSS selectors could break at any moment. When they do, you have to open the browser, debug the DOM, find new selectors, update the code, and run it again.
"Isn't there a simpler way?"
There is. But before we get to that, we need to clear up a fundamental misunderstanding.
This is a distinction most people miss. Playwright, Selenium, Cypress — they are all test frameworks, not automation tools.
A test framework's core design goals are:
These features matter when you're writing tests. But if you just want to "search Google and get results"?
You don't need assertions. You don't need test reports. You don't need parallel execution. You need: open page → extract data → move on.
An automation tool's core design goals are completely different:
In other words: test frameworks care about "is it correct?" Automation tools care about "is it fast?"
Let's compare three approaches using the same task: Google search for "node.js tutorial", extract the top 10 results.
// search.js
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://www.google.com/search?q=node.js+tutorial');
await page.waitForSelector('#search');
const results = await page.$$eval('.g', (elements) =>
elements.slice(0, 10).map((el) => ({
title: el.querySelector('h3')?.textContent || '',
url: el.querySelector('a')?.href || '',
snippet: el.querySelector('.VwiC3b')?.textContent || '',
}))
);
console.log(JSON.stringify(results, null, 2));
await browser.close();
})();
To run:
npm install playwright # Install dependencies, download browser (~300MB)
node search.js # Run
Cost: 300MB browser download + 23 lines of code + ongoing selector maintenance.
# search.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import json
driver = webdriver.Chrome()
driver.get("https://www.google.com/search?q=node.js+tutorial")
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "search"))
)
results = []
elements = driver.find_elements(By.CSS_SELECTOR, ".g")[:10]
for el in elements:
title = el.find_element(By.CSS_SELECTOR, "h3").text if el.find_elements(By.CSS_SELECTOR, "h3") else ""
url = el.find_element(By.CSS_SELECTOR, "a").get_attribute("href") if el.find_elements(By.CSS_SELECTOR, "a") else ""
snippet = el.find_element(By.CSS_SELECTOR, ".VwiC3b").text if el.find_elements(By.CSS_SELECTOR, ".VwiC3b") else ""
results.append({"title": title, "url": url, "snippet": snippet})
print(json.dumps(results, indent=2))
driver.quit()
To run:
pip install selenium # Install dependency
# Also need to download ChromeDriver separately
python search.py # Run
Cost: Install dependencies + download ChromeDriver + 27 lines of code + ongoing selector maintenance.
xbrowser google search "node.js tutorial" --json --limit 10
To run:
npx xbrowser google search "node.js tutorial" --json --limit 10
Cost: One command.
Output:
[
{
"title": "Node.js Tutorial - W3Schools",
"url": "https://www.w3schools.com/nodejs/",
"snippet": "Learn Node.js with our comprehensive tutorial..."
},
{
"title": "The Node.js Handbook - FreeCodeCamp",
"url": "https://www.freecodecamp.org/news/the-nodejs-handbook/",
"snippet": "This handbook is a getting-started guide to Node.js..."
}
]
No browser to install. No code to write. No selectors to maintain.
| Dimension | Playwright | Selenium | CLI Tool |
|---|---|---|---|
| Purpose | Test framework | Test framework | Automation tool |
| Install size | ~300MB | ~150MB + Driver | ~50MB |
| Code per operation | 20-50 lines | 20-50 lines | 1 line |
| Learning curve | High (Page Objects, Locators, Contexts) | High (WebDriver protocol, wait strategies) | Low (command-line args) |
| Selector maintenance | Manual | Manual | Built-in |
| Output format | Raw (format yourself) | Raw (format yourself) | Native JSON |
| Pipe support | Requires extra handling | Requires extra handling | Native |
| Best for | Regression tests, E2E tests | Compatibility tests, cross-browser | Daily automation, data extraction |
| Target audience | QA engineers | QA engineers | Developers, ops, content creators |
| CI integration | Built-in | Built-in | Via shell scripts |
| Assertion capability | Powerful | Powerful | None (use jq/awk) |
I'm not here to knock Playwright or Selenium. They're top-tier tools in their domains.
If you're doing regression testing — verifying 50 pages worth of core functionality before every release — Playwright is the right choice. You need assertions. You need test reports. You need the Trace Viewer to debug failing cases.
If you're doing compatibility testing — making sure your product works on Chrome, Firefox, and Safari — Selenium's cross-browser capabilities are unmatched.
But if you're doing any of these:
You don't need a test framework. You need an automation tool.
Using a test framework for automation is like using a cannon to kill a mosquito. It works, but the cost is way too high.
Here's a simple heuristic:
You need to verify "is it correct?" → Test framework
You need to quickly "get something done" → Automation tool
More specifically:
Use Playwright / Selenium when:
Use CLI automation tools when:
With shell scripts + CLI tools, you can even do things test frameworks aren't designed for:
# Monitor competitor price every hour, auto-alert on Slack when it drops
watch -n 3600 'xbrowser crawl "$COMPETITOR_URL" --extract ".price" \
| xargs -I{} bash -c "[[ {} < 99 ]] && echo Price dropped to {} | slacksend"'
# Daily ranking report at 9 AM, generate Markdown
0 9 * * * for kw in "js tutorial" "node guide" "react tips"; do \
echo "### $kw"; xbrowser google search "$kw" --json --limit 3 \
| jq -r ".[] | \"- [\(.title)](\(.url))\""; done > /tmp/rank-report.md
Playwright and Selenium can't do this — not because it's technically impossible, but because it's not what they were designed for. They weren't built for "quickly execute one-off tasks."
If you're a developer whose daily work involves browser operations but not testing:
If you're a QA engineer whose primary job is testing:
If you're an ops / content creator / SEO engineer whose daily work is repetitive browser operations:
If you're looking for a CLI browser automation tool, xbrowser is worth checking out. It wraps Playwright's browser control capabilities into a command-line interface — Google search, web scraping, SEO ping, all done in one line, with native JSON output and pipe support. Built for daily automation workflows and cron-friendly scheduled tasks.