最佳实践
使用 Browserman 构建可靠高效的浏览器自动化指南。
账号管理
使用描述性名称
为账号选择清晰、有意义的名称:
好:
personal-twitter
work-twitter-official
xueqiu-trading-main
eastmoney-analysis不好:
account1
test
a
twitter123组织账号
使用一致的命名约定:
{用途}-{平台}-{标识符}
示例:
personal-twitter-main
work-xueqiu-primary
brand-twitter-official
testing-twitter-dev速率限制
尊重平台限制
每个平台都有不同的速率限制:
javascript
const RATE_LIMITS = {
twitter: {
tweets: { count: 300, window: 3 * 60 * 60 * 1000 }, // 3 小时
likes: { count: 1000, window: 24 * 60 * 60 * 1000 } // 24 小时
},
xueqiu: {
posts: { count: 50, window: 24 * 60 * 60 * 1000 }
}
};实施请求节流
使用队列控制请求速率:
javascript
class RateLimiter {
constructor(maxRequests, windowMs) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.requests = [];
}
async acquire() {
const now = Date.now();
this.requests = this.requests.filter(
time => now - time < this.windowMs
);
if (this.requests.length >= this.maxRequests) {
const oldestRequest = this.requests[0];
const waitTime = this.windowMs - (now - oldestRequest);
await sleep(waitTime);
return this.acquire();
}
this.requests.push(now);
}
}
// 使用
const limiter = new RateLimiter(300, 3 * 60 * 60 * 1000);
for (const tweet of tweets) {
await limiter.acquire();
await postTweet(tweet);
}在请求之间添加延迟
用随机延迟模拟人类行为:
javascript
async function randomDelay(min = 1000, max = 5000) {
const delay = Math.random() * (max - min) + min;
await sleep(delay);
}
async function postMultipleTweets(tweets) {
for (const tweet of tweets) {
await postTweet(tweet);
await randomDelay(2000, 5000); // 2-5 秒
}
}错误处理
实施重试逻辑
使用指数退避处理瞬态错误:
javascript
async function retryWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
const isLastAttempt = i === maxRetries - 1;
if (isLastAttempt) {
throw error;
}
if (!isRetryableError(error)) {
throw error;
}
const waitTime = Math.pow(2, i) * 1000;
console.log(`重试 ${i + 1}/${maxRetries},等待 ${waitTime}ms`);
await sleep(waitTime);
}
}
}
function isRetryableError(error) {
const retryableCodes = [
'RATE_LIMITED',
'TIMEOUT',
'PLATFORM_ERROR',
'CONNECTION_ERROR'
];
return retryableCodes.includes(error.code);
}正确记录错误
在错误日志中包含上下文:
javascript
try {
await client.tasks.create(task);
} catch (error) {
console.error('任务创建失败:', {
error: error.message,
code: error.code,
task: {
platform: task.platform,
tool: task.tool,
accountName: task.accountName
},
timestamp: new Date().toISOString()
});
}安全
永远不要暴露 API 密钥
javascript
// ❌ 不好
const API_KEY = 'sk_live_abc123';
// ✅ 好
const API_KEY = process.env.BROWSERMAN_API_KEY;
if (!API_KEY) {
throw new Error('未设置 BROWSERMAN_API_KEY');
}验证输入
在创建任务前始终验证用户输入:
javascript
function validateTweet(text) {
if (!text || typeof text !== 'string') {
throw new Error('需要推文文本');
}
if (text.length > 280) {
throw new Error('推文太长(最多 280 个字符)');
}
if (text.length === 0) {
throw new Error('推文不能为空');
}
return text.trim();
}
const tweet = validateTweet(userInput);
await postTweet(tweet);性能
批处理操作
高效处理多个任务:
javascript
async function batchPostTweets(tweets, accountName) {
const taskPromises = tweets.map(async (tweet, index) => {
// 错开请求以避免速率限制
await sleep(index * 2000);
return client.tasks.create({
platform: 'twitter',
tool: 'createTweet',
accountName,
parameters: { text: tweet }
});
});
const results = await Promise.allSettled(taskPromises);
const succeeded = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');
console.log(`发布了 ${succeeded.length}/${tweets.length} 条推文`);
return { succeeded, failed };
}