Web Automation Suite - 网页自动化套件
整合爬虫 + 浏览器自动化 + 定时任务,一套搞定所有网页自动化需求。
核心能力
| 功能 | 说明 |
|---|
| 🌐 Chrome CDP连接 | 远程调试模式稳定连接,自动重连 |
| 🖱️ 元素操作 |
点击、输入、滚动、拖拽、悬停 |
| 📸 截图取证 | 页面快照、全页截图、元素截图 |
| 📊 数据抓取 | 文本、链接、图片、表格、JSON |
| 📝 表单填写 | 批量填充、自动提交 |
| ⏰ 定时任务 | Cron式定时执行监控任务 |
| 🔄 状态管理 | 保存/加载登录状态,多账号切换 |
Chrome远程调试连接
启动Chrome调试模式
bash
chrome.exe --remote-debugging-port=9222
获取WebSocket URL(必须)
javascript
const http = require(http);
const wsUrl = await new Promise((resolve, reject) => {
http.get(http://localhost:9222/json/version, res => {
let data = ;
res.on(data, c => data += c);
res.on(end, () => resolve(JSON.parse(data).webSocketDebuggerUrl));
}).on(error, reject);
});
连接浏览器
javascript
const { chromium } = require(playwright);
const browser = await chromium.connectOverCDP(wsUrl);
工作流程
- 1. 启动Chrome调试模式(用户执行)
- 获取WebSocket URL
- 连接CDP
- 创建页面
- 执行操作(导航/点击/输入/截图)
- 提取数据
- 保存状态(如需要)
- 关闭连接
常用操作
打开网页
javascript
await page.goto(https://example.com, {
waitUntil: networkidle,
timeout: 30000
});
点击元素
javascript
await page.click(#submit-button);
await page.click(button:has-text(登录));
输入文本
javascript
await page.fill(input[name=username], your_text);
await page.type(textarea, content here, { delay: 100 });
滚动页面
javascript
await page.evaluate(() => window.scrollBy(0, 500));
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
截图
javascript
// 普通截图
await page.screenshot({ path: screenshot.png });
// 全页截图
await page.screenshot({ path: fullpage.png, fullPage: true });
提取数据
javascript
// 提取所有链接
const links = await page.evaluate(() =>
Array.from(document.querySelectorAll(a))
.map(a => ({ text: a.innerText, href: a.href }))
);
// 提取表格数据
const tableData = await page.evaluate(() => {
const rows = document.querySelectorAll(table tr);
return Array.from(rows).map(row =>
Array.from(row.querySelectorAll(td)).map(td => td.innerText)
);
});
// 提取JSON数据
const data = await page.evaluate(() => {
const el = document.querySelector(script#NEXT_DATA);
return el ? JSON.parse(el.textContent) : null;
});
等待元素
javascript
await page.waitForSelector(#content, { timeout: 5000 });
await page.waitForLoadState(networkidle);
表单填写
javascript
const formData = {
input[name=username]: user123,
input[name=password]: pass123,
select[name=country]: CN,
textarea: 内容文本
};
for (const [selector, value] of Object.entries(formData)) {
await page.fill(selector, value);
await page.waitForTimeout(100);
}
await page.click(button[type=submit]);
数据抓取模板
列表页抓取
javascript
async function scrapeListPage(page, itemSelector) {
await page.waitForLoadState(networkidle);
return await page.evaluate((selector) => {
const items = document.querySelectorAll(selector);
return Array.from(items).map(item => ({
title: item.querySelector(h2, h3, .title)?.innerText || ,
link: item.querySelector(a)?.href || ,
price: item.querySelector(.price)?.innerText || ,
desc: item.querySelector(.desc, p)?.innerText ||
}));
}, itemSelector);
}
分页抓取
javascript
async function scrapeWithPagination(page, baseUrl, maxPages = 5) {
const results = [];
for (let i = 1; i <= maxPages; i++) {
await page.goto(${baseUrl}?page=${i});
await page.waitForLoadState(networkidle);
const data = await scrapeListPage(page, .item);
results.push(...data);
if (await page.$(.next-page:disabled)) break;
await page.click(.next-page);
await page.waitForTimeout(1000);
}
return results;
}
动态内容抓取(滚动加载)
javascript
async function scrapeInfiniteScroll(page, itemSelector) {
await page.waitForLoadState(networkidle);
let lastHeight = 0;
const results = new Set();
while (true) {
const items = await page.$$eval(itemSelector, els =>
els.map(el => el.href || el.src || el.innerText)
);
items.forEach(i => results.add(i));
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(1000);
const newHeight = await page.evaluate(() => document.body.scrollHeight);
if (newHeight === lastHeight) break;
lastHeight = newHeight;
}
return Array.from(results);
}
定时任务
创建定时任务脚本
javascript
// scripts/scheduled-task.js
const { chromium } = require(playwright);
const fs = require(fs);
async function runTask() {
const wsUrl = await getChromeWSUrl();
const browser = await chromium.connectOverCDP(wsUrl);
const page = await browser.newPage();
try {
await page.goto(https://example.com/prices);
await page.waitForLoadState(networkidle);
const data = await page.evaluate(() => ({
price: document.querySelector(.price)?.innerText,
timestamp: new Date().toISOString()
}));
const logFile = price_log.json;
const logs = fs.existsSync(logFile)
? JSON.parse(fs.readFileSync(logFile, utf8))
: [];
logs.push(data);
fs.writeFileSync(logFile, JSON.stringify(logs, null, 2));
console.log(Task completed:, data);
} finally {
await browser.close();
}
}
runTask().catch(console.error);
Cron表达式
- - 0 - 每小时
- 0 9 - 每天上午9点
- /15 - 每15分钟
状态管理
保存登录状态
javascript
const statePath = ./browser-state.json;
await page.context().storageState({ path: statePath });
console.log(State saved to, statePath);
加载登录状态
javascript
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({
storageState: ./browser-state.json
});
const page = await context.newPage();
使用场景
- - 数据采集:商品信息、股票数据、新闻内容
- 内容监控:竞品动态、价格变化、内容更新
- 自动化测试:网页功能测试、表单验证
- 批量操作:批量发帖、批量填表、批量下载
- 定时任务:定期抓取、定期监控、定期提交
注意事项
- 1. 必须先启动Chrome调试模式:chrome.exe --remote-debugging-port=9222
- 禁止关闭Chrome - 自动化期间保持Chrome运行
- 等待页面加载 - 操作前等待 networkidle
- 复用连接 - 不要频繁断开重连
- 反爬策略 - 可用 --disable-blink-features=AutomationControlled 减少检测
错误处理
| 错误 | 解决方法 |
|---|
| WebSocket 404 | Chrome调试端口未启动 |
| Timeout |
增加等待时间或检查网络 |
| Element not found | 等待元素出现或检查选择器 |
| Blocked | 使用隐身上下文或更换User-Agent |
应用示例
抓取