Skip to content

最佳实践

使用 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 };
}

下一步