Loading...
Loading...
Compare original and translation side by side
playwright-e2e-testingplaywright-console-monitorplaywright-network-analyzerplaywright-e2e-testingplaywright-console-monitorplaywright-network-analyzer// 1. Validate URLs
python scripts/validate_urls.py urls.txt
// 2. Scrape pages with rate limiting
const results = [];
for (const url of urls) {
await browser_navigate({ url });
await browser_wait_for({ time: Math.random() * 2 + 1 }); // 1-3s delay
const data = await browser_evaluate({
function: `
Array.from(document.querySelectorAll('.product')).map(el => ({
title: el.querySelector('.title')?.textContent?.trim(),
price: el.querySelector('.price')?.textContent?.trim(),
url: el.querySelector('a')?.getAttribute('href')
}))
`
});
results.push(...data);
}
// 3. Process results
python scripts/process_results.py scraped.json -o products.csv// 1. 校验URL
python scripts/validate_urls.py urls.txt
// 2. 带速率限制的页面爬取
const results = [];
for (const url of urls) {
await browser_navigate({ url });
await browser_wait_for({ time: Math.random() * 2 + 1 }); // 1-3秒延迟
const data = await browser_evaluate({
function: `
Array.from(document.querySelectorAll('.product')).map(el => ({
title: el.querySelector('.title')?.textContent?.trim(),
price: el.querySelector('.price')?.textContent?.trim(),
url: el.querySelector('a')?.getAttribute('href')
}))
`
});
results.push(...data);
}
// 3. 处理结果
python scripts/process_results.py scraped.json -o products.csvhttps://example.com/products?page=1
https://example.com/products?page=2
https://example.com/products?page=3python scripts/validate_urls.py urls.txt --user-agent "MyBot/1.0"https://example.com/products?page=1
https://example.com/products?page=2
https://example.com/products?page=3python scripts/validate_urls.py urls.txt --user-agent "MyBot/1.0"await browser_navigate({ url: firstUrl });
await browser_snapshot();await browser_navigate({ url: firstUrl });
await browser_snapshot();const results = [];
for (const url of urlList) {
// Navigate to page
await browser_navigate({ url });
// Wait for content to load
await browser_wait_for({ text: 'Expected content marker' });
// Add respectful delay (1-3 seconds)
const delay = Math.random() * 2 + 1;
await browser_wait_for({ time: delay });
// Extract data
const pageData = await browser_evaluate({
function: `/* extraction code */`
});
results.push(...pageData);
// Check console for errors/warnings
const console = await browser_console_messages();
// Monitor for rate limit warnings
}const results = [];
for (const url of urlList) {
// 跳转至目标页面
await browser_navigate({ url });
// 等待内容加载完成
await browser_wait_for({ text: 'Expected content marker' });
// 添加友好延迟(1-3秒)
const delay = Math.random() * 2 + 1;
await browser_wait_for({ time: delay });
// 提取数据
const pageData = await browser_evaluate({
function: `/* 提取代码 */`
});
results.push(...pageData);
// 检查控制台错误/警告
const console = await browser_console_messages();
// 监控速率限制警告
}browser_evaluateconst data = await browser_evaluate({
function: `
try {
return Array.from(document.querySelectorAll('.item')).map(el => ({
title: el.querySelector('.title')?.textContent?.trim(),
price: el.querySelector('.price')?.textContent?.trim(),
rating: el.querySelector('.rating')?.textContent?.trim(),
url: el.querySelector('a')?.getAttribute('href')
})).filter(item => item.title && item.price); // Filter incomplete records
} catch (e) {
console.error('Extraction failed:', e);
return [];
}
`
});references/extraction-patterns.mdbrowser_evaluateconst data = await browser_evaluate({
function: `
try {
return Array.from(document.querySelectorAll('.item')).map(el => ({
title: el.querySelector('.title')?.textContent?.trim(),
price: el.querySelector('.price')?.textContent?.trim(),
rating: el.querySelector('.rating')?.textContent?.trim(),
url: el.querySelector('a')?.getAttribute('href')
})).filter(item => item.title && item.price); // 过滤不完整记录
} catch (e) {
console.error('提取失败:', e);
return [];
}
`
});references/extraction-patterns.md// Check HTTP responses via browser_network_requests
const requests = await browser_network_requests();
const rateLimited = requests.some(r => r.status === 429 || r.status === 503);
if (rateLimited) {
// Back off exponentially
await browser_wait_for({ time: 10 }); // Wait 10 seconds
// Retry or skip
}
// Check console for blocking messages
const console = await browser_console_messages({ pattern: 'rate limit|blocked|captcha' });
if (console.length > 0) {
// Handle blocking
}// 通过browser_network_requests检查HTTP响应
const requests = await browser_network_requests();
const rateLimited = requests.some(r => r.status === 429 || r.status === 503);
if (rateLimited) {
// 指数退避
await browser_wait_for({ time: 10 }); // 等待10秒
// 重试或跳过
}
// 检查控制台拦截消息
const console = await browser_console_messages({ pattern: 'rate limit|blocked|captcha' });
if (console.length > 0) {
// 处理拦截
}// In your scraping script
fs.writeFileSync('scraped.json', JSON.stringify({ results }, null, 2));undefined// 在爬取脚本中
fs.writeFileSync('scraped.json', JSON.stringify({ results }, null, 2));undefinedundefinedundefined// Random delay between 1-3 seconds
const randomDelay = () => Math.random() * 2 + 1;
await browser_wait_for({ time: randomDelay() });
// Exponential backoff after rate limit
let backoffSeconds = 5;
for (let retry = 0; retry < 3; retry++) {
try {
await browser_navigate({ url });
break; // Success
} catch (e) {
await browser_wait_for({ time: backoffSeconds });
backoffSeconds *= 2; // Double delay each retry
}
}// 1-3秒随机延迟
const randomDelay = () => Math.random() * 2 + 1;
await browser_wait_for({ time: randomDelay() });
// 触发速率限制后的指数退避
let backoffSeconds = 5;
for (let retry = 0; retry < 3; retry++) {
try {
await browser_navigate({ url });
break; // 访问成功
} catch (e) {
await browser_wait_for({ time: backoffSeconds });
backoffSeconds *= 2; // 每次重试延迟翻倍
}
}| Response Code | Action |
|---|---|
| 200 OK | Continue with normal delay (1-3s) |
| 429 Too Many Requests | Increase delay to 10s, retry |
| 503 Service Unavailable | Wait 60s, then retry |
| 403 Forbidden | Stop scraping this domain |
references/ethical-scraping.md| 响应码 | 处理动作 |
|---|---|
| 200 OK | 保持常规延迟(1-3秒)继续爬取 |
| 429 Too Many Requests | 延迟提升至10秒后重试 |
| 503 Service Unavailable | 等待60秒后重试 |
| 403 Forbidden | 停止爬取该域名 |
references/ethical-scraping.mdvalidate_urls.pyundefinedvalidate_urls.pyundefined
**Output includes**:
- URL format validation
- Domain grouping
- robots.txt compliance check
- Summary statistics
**输出包含**:
- URL格式校验结果
- 域名分组
- robots.txt合规检查结果
- 统计摘要// Single page extraction
const data = await browser_evaluate({
function: `
Array.from(document.querySelectorAll('.item')).map(el => ({
field1: el.querySelector('.selector1')?.textContent?.trim(),
field2: el.querySelector('.selector2')?.getAttribute('href')
}))
`
});// 单页面提取
const data = await browser_evaluate({
function: `
Array.from(document.querySelectorAll('.item')).map(el => ({
field1: el.querySelector('.selector1')?.textContent?.trim(),
field2: el.querySelector('.selector2')?.getAttribute('href')
}))
`
});let hasMore = true;
let page = 1;
while (hasMore) {
await browser_navigate({ url: `${baseUrl}?page=${page}` });
await browser_wait_for({ time: randomDelay() });
const pageData = await browser_evaluate({ function: extractionCode });
results.push(...pageData);
// Check for next page
hasMore = await browser_evaluate({
function: `document.querySelector('.next:not(.disabled)') !== null`
});
page++;
}references/extraction-patterns.mdlet hasMore = true;
let page = 1;
while (hasMore) {
await browser_navigate({ url: `${baseUrl}?page=${page}` });
await browser_wait_for({ time: randomDelay() });
const pageData = await browser_evaluate({ function: extractionCode });
results.push(...pageData);
// 检查是否存在下一页
hasMore = await browser_evaluate({
function: `document.querySelector('.next:not(.disabled)') !== null`
});
page++;
}references/extraction-patterns.mdtry {
await browser_navigate({ url });
} catch (e) {
console.error(`Failed to load ${url}:`, e);
failedUrls.push(url);
continue; // Skip to next URL
}try {
await browser_navigate({ url });
} catch (e) {
console.error(`加载${url}失败:`, e);
failedUrls.push(url);
continue; // 跳过当前URL,处理下一个
}const data = await browser_evaluate({ function: extractionCode });
if (!data || data.length === 0) {
console.warn(`No data extracted from ${url}`);
// Log for manual review
}
// Validate data structure
const validData = data.filter(item =>
item.title && item.price // Ensure required fields exist
);const data = await browser_evaluate({ function: extractionCode });
if (!data || data.length === 0) {
console.warn(`从${url}未提取到任何数据`);
// 记录日志供人工复核
}
// 校验数据结构
const validData = data.filter(item =>
item.title && item.price // 确保必填字段存在
);// Monitor console
const console = await browser_console_messages({
pattern: 'error|rate|limit|captcha',
onlyErrors: true
});
if (console.length > 0) {
console.log('Warnings detected:', console);
}
// Monitor network
const requests = await browser_network_requests();
const errors = requests.filter(r => r.status >= 400);// 监控控制台
const console = await browser_console_messages({
pattern: 'error|rate|limit|captcha',
onlyErrors: true
});
if (console.length > 0) {
console.log('检测到警告:', console);
}
// 监控网络
const requests = await browser_network_requests();
const errors = requests.filter(r => r.status >= 400);python scripts/process_results.py scraped.json --stats📊 Statistics:
Total records: 150
Fields (5): title, price, rating, url, image
Sample record: {...}python scripts/process_results.py scraped.json --stats📊 统计信息:
总记录数: 150
字段数 (5): title, price, rating, url, image
示例记录: {...}undefinedundefinedundefinedundefinedpython scripts/process_results.py scraped.json -o products.csv --statspython scripts/process_results.py scraped.json -o products.csv --statsscripts/validate_urls.pyscripts/process_results.pyscripts/validate_urls.pyscripts/process_results.pyreferences/ethical-scraping.mdreferences/extraction-patterns.mdreferences/ethical-scraping.mdreferences/extraction-patterns.md✅ Validated 50 URLs
✅ Scraped 50 pages in 5 minutes (6 req/min)
✅ Extracted 1,250 products
✅ Zero rate limit errors
✅ Exported to products.csv (1,250 rows)✅ 已校验50个URL
✅ 5分钟内完成50个页面爬取(每分钟6次请求)
✅ 提取到1250条商品数据
✅ 无速率限制错误
✅ 已导出至products.csv(共1250行)⚠️ Validated 50 URLs (2 disallowed by robots.txt)
✅ Scraped 48 pages
⚠️ 3 pages returned no data (logged for review)
✅ Extracted 1,100 products
⚠️ 1 rate limit warning (backed off successfully)
✅ Exported to products.csv (1,100 rows)⚠️ 已校验50个URL(2个被robots.txt禁止访问)
✅ 完成48个页面爬取
⚠️ 3个页面未提取到数据(已记录待复核)
✅ 提取到1100条商品数据
⚠️ 1次速率限制警告(退避重试成功)
✅ 已导出至products.csv(共1100行)❌ Rate limited after 20 pages (429 responses)
✅ Backed off exponentially (5s → 10s → 20s)
✅ Resumed scraping successfully
✅ Extracted 450 products from 25 pages❌ 爬取20个页面后触发速率限制(返回429响应)
✅ 已执行指数退避(5秒 → 10秒 → 20秒)
✅ 已成功恢复爬取
✅ 从25个页面提取到450条商品数据| Metric | Before | After |
|---|---|---|
| Setup time | 30-45 min | 5-10 min |
| Rate limit errors | Common | Rare |
| robots.txt violations | Possible | Prevented |
| Data format conversion | Manual | Automated |
| Error detection | Manual review | Automated monitoring |
| 指标 | 使用前 | 使用后 |
|---|---|---|
| 配置时间 | 30-45分钟 | 5-10分钟 |
| 速率限制错误 | 频繁出现 | 极少出现 |
| robots.txt违规 | 可能发生 | 完全避免 |
| 数据格式转换 | 手动处理 | 自动完成 |
| 错误检测 | 人工复核 | 自动监控 |
validate_urls.pyvalidate_urls.py