优化爬虫,增加缓存,线程池,保存浏览器用户信息
This commit is contained in:
@@ -5,13 +5,9 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const puppeteer = require('puppeteer-extra');
|
||||
|
||||
/** 默认 Chrome 可执行文件路径(生产环境可通过环境变量覆盖) */
|
||||
const DEFAULT_CHROME_EXECUTABLE = '/www/wwwroot/puppeteer-api/.cache/puppeteer/chrome/linux-149.0.7827.22/chrome-linux64/chrome';
|
||||
|
||||
/** 默认 User-Agent,与 standard / real 双轨保持一致 */
|
||||
const DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
||||
|
||||
/** Chrome 启动基础参数:多实例 headless 场景下复用 */
|
||||
const BASE_LAUNCH_ARGS = [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
@@ -26,24 +22,14 @@ const BASE_LAUNCH_ARGS = [
|
||||
'--disable-crash-reporter',
|
||||
];
|
||||
|
||||
/** puppeteer-api 专用运行时目录(与 server.js 同级 .runtime) */
|
||||
const RUNTIME_DIR = path.join(__dirname, '..', '.runtime');
|
||||
|
||||
/**
|
||||
* 确保 .runtime 子目录存在且 www 用户可写
|
||||
* @param {string} name
|
||||
* @returns {string}
|
||||
*/
|
||||
function ensureRuntimeSubdir(name) {
|
||||
const dir = path.join(RUNTIME_DIR, name);
|
||||
fs.mkdirSync(dir, { recursive: true, mode: 0o777 });
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Chrome 可执行文件路径
|
||||
* @returns {string}
|
||||
*/
|
||||
function resolveChromeExecutable() {
|
||||
const candidates = [
|
||||
process.env.PUPPETEER_EXECUTABLE_PATH,
|
||||
@@ -63,16 +49,12 @@ function resolveChromeExecutable() {
|
||||
return bundled;
|
||||
}
|
||||
} catch (_) {
|
||||
/* puppeteer 未内置浏览器时忽略 */
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
return candidates[0] || DEFAULT_CHROME_EXECUTABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产 Linux 常注入无效 DBUS 地址;删除而非设为 /dev/null
|
||||
* @returns {NodeJS.ProcessEnv}
|
||||
*/
|
||||
function buildBrowserLaunchEnv() {
|
||||
const env = { ...process.env };
|
||||
delete env.DBUS_SESSION_BUS_ADDRESS;
|
||||
@@ -87,10 +69,6 @@ function buildBrowserLaunchEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Chrome 可执行文件存在
|
||||
* @returns {string}
|
||||
*/
|
||||
function assertChromeExecutable() {
|
||||
const executablePath = resolveChromeExecutable();
|
||||
if (!fs.existsSync(executablePath)) {
|
||||
@@ -101,23 +79,48 @@ function assertChromeExecutable() {
|
||||
return executablePath;
|
||||
}
|
||||
|
||||
/** 并发与超时配置(可通过环境变量覆盖) */
|
||||
const MAX_CONCURRENT_BROWSERS = Math.max(1, parseInt(process.env.MAX_CONCURRENT_BROWSERS || '4', 10));
|
||||
const MAX_CONCURRENT_BROWSERS_REAL = Math.max(1, parseInt(process.env.MAX_CONCURRENT_BROWSERS_REAL || '2', 10));
|
||||
const QUEUE_TIMEOUT_MS = Math.max(5000, parseInt(process.env.QUEUE_TIMEOUT_MS || '120000', 10));
|
||||
const API_INTERCEPT_TIMEOUT_MS = Math.max(10000, parseInt(process.env.API_INTERCEPT_TIMEOUT_MS || '30000', 10));
|
||||
const API_INTERCEPT_TIMEOUT_MS = Math.max(10000, parseInt(process.env.API_INTERCEPT_TIMEOUT_MS || '60000', 10));
|
||||
const API_INTERCEPT_TIMEOUT_REAL_MS = Math.max(
|
||||
API_INTERCEPT_TIMEOUT_MS,
|
||||
parseInt(process.env.API_INTERCEPT_TIMEOUT_REAL_MS || '90000', 10)
|
||||
);
|
||||
|
||||
/** Captcha API 配置 */
|
||||
const NAVIGATION_TIMEOUT_MS = Math.max(15000, parseInt(process.env.NAVIGATION_TIMEOUT_MS || '60000', 10));
|
||||
|
||||
/** rebrowser-puppeteer-core / puppeteer-core 仅支持 load|domcontentloaded|networkidle0|networkidle2,不支持 commit */
|
||||
const ALLOWED_NAVIGATION_WAIT_UNTIL = ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'];
|
||||
|
||||
function resolveNavigationWaitUntil() {
|
||||
const raw = (process.env.NAVIGATION_WAIT_UNTIL || 'domcontentloaded').trim();
|
||||
if (ALLOWED_NAVIGATION_WAIT_UNTIL.includes(raw)) {
|
||||
return raw;
|
||||
}
|
||||
if (raw === 'commit') {
|
||||
console.warn('[constants] NAVIGATION_WAIT_UNTIL=commit 不被当前 Puppeteer 支持,已降级为 domcontentloaded');
|
||||
return 'domcontentloaded';
|
||||
}
|
||||
console.warn(`[constants] 未知 NAVIGATION_WAIT_UNTIL=${raw},已降级为 domcontentloaded`);
|
||||
return 'domcontentloaded';
|
||||
}
|
||||
|
||||
const NAVIGATION_WAIT_UNTIL = resolveNavigationWaitUntil();
|
||||
const NAVIGATION_MAX_RETRIES = Math.max(0, parseInt(process.env.NAVIGATION_MAX_RETRIES || '1', 10));
|
||||
const PAGE_DEFAULT_TIMEOUT_MS = Math.max(15000, parseInt(process.env.PAGE_DEFAULT_TIMEOUT_MS || '30000', 10));
|
||||
|
||||
const CAPTCHA_PROVIDER = (process.env.CAPTCHA_PROVIDER || 'capsolver').toLowerCase();
|
||||
const CAPTCHA_API_KEY = process.env.CAPTCHA_API_KEY || '';
|
||||
const CAPTCHA_MAX_WAIT_MS = Math.max(30000, parseInt(process.env.CAPTCHA_MAX_WAIT_MS || '120000', 10));
|
||||
|
||||
/** 会话 Cookie 持久化 TTL(毫秒) */
|
||||
const SESSION_TTL_MS = Math.max(60000, parseInt(process.env.SESSION_TTL_MS || '1800000', 10));
|
||||
const SESSION_TTL_MS = Math.max(60000, parseInt(process.env.SESSION_TTL_MS || '7200000', 10));
|
||||
const PERSIST_BROWSER_PROFILE = process.env.PERSIST_BROWSER_PROFILE !== '0';
|
||||
const PROFILE_MAX_AGE_MS = Math.max(0, parseInt(process.env.PROFILE_MAX_AGE_MS || String(7 * 24 * 3600 * 1000), 10));
|
||||
|
||||
const POOL_IDLE_MS = Math.max(60000, parseInt(process.env.POOL_IDLE_MS || '300000', 10));
|
||||
const MAX_POOLED_BROWSERS = Math.max(0, parseInt(process.env.MAX_POOLED_BROWSERS || '4', 10));
|
||||
const POOL_ENABLED = process.env.POOL_ENABLED !== '0' && MAX_POOLED_BROWSERS > 0;
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_CHROME_EXECUTABLE,
|
||||
@@ -133,8 +136,17 @@ module.exports = {
|
||||
QUEUE_TIMEOUT_MS,
|
||||
API_INTERCEPT_TIMEOUT_MS,
|
||||
API_INTERCEPT_TIMEOUT_REAL_MS,
|
||||
NAVIGATION_TIMEOUT_MS,
|
||||
NAVIGATION_WAIT_UNTIL,
|
||||
NAVIGATION_MAX_RETRIES,
|
||||
PAGE_DEFAULT_TIMEOUT_MS,
|
||||
CAPTCHA_PROVIDER,
|
||||
CAPTCHA_API_KEY,
|
||||
CAPTCHA_MAX_WAIT_MS,
|
||||
SESSION_TTL_MS,
|
||||
PERSIST_BROWSER_PROFILE,
|
||||
PROFILE_MAX_AGE_MS,
|
||||
POOL_IDLE_MS,
|
||||
MAX_POOLED_BROWSERS,
|
||||
POOL_ENABLED,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user