Skill: qirongzhang/audtools-shopify-collector (version 1.0.0)

--- name: audtools-shopify-collector description: AudTools Shopify 批量采集器 - 读取 CSV 中的分类链接，批量自动提交到 https://www.audtools.com，自动填充链接、设置商品数量为 9999，间隔 2 秒提交，完成批量采集商品。 allowed-tools: exec, read, write, edit --- # AudTools Shopify 批量采集器 自动批量采集 Shopify 分类商品到 AudTools 网站，支持从 CSV 读取链接，自动填充、设置数量，间隔提交。 ## 功能特点 - ✅ 读取 CSV 文件中的"完整链接"列 - ✅ 自动打开 AudTools 网站 - ✅ 检测是否需要登录，需要登录会提醒你手动登录 - ✅ 依次在输入框填入每个链接 - ✅ 自动设置商品数量为 9999 - ✅ 点击提交，每条操作间隔 2 秒 - ✅ 完全自动化，不需要手动操作 ## 使用方法 ```bash # 默认 CSV 读取默认路径 C:\workspace\caiji\shop-futvortexstore-com-categories.csv node batch-collect.js # 指定 CSV 文件路径 node batch-collect.js C:\path\to\your\categories.csv ``` ## 工作流程 1. **自动流程： 1. 打开 `https://www.audtools.com/users/shopns#/users/shopns/collecs` 2. 检测是否有登录表单，如果有会暂停，请你手动登录 3. 登录完成后继续执行 4. 读取 CSV 中"完整链接"列的每个链接 5. 在输入框填入链接，设置商品数量为 9999 6. 点击提交 7. 等待 2 秒 8. 处理下一个链接 9. 全部完成后暂停，你可以在浏览器导出结果 ## CSV 格式要求 CSV 需要有一列叫 **完整链接**，每个单元格是分类页面的完整 URL。就是 category-collector 输出的 CSV 格式正好匹配，可以直接使用。 ## 示例 配合 category-collector 使用： 1. 先用 `category-collector` 采集 Shopify 分类得到 CSV 2. 然后用 `audtools-shopify-collector` 批量提交到 AudTools 采集商品 ```bash # 第一步：采集分类 node category-collector/collect.js https://shop-futvortexstore.com/ C:\workspace\caiji # 第二步：批量提交到 AudTools node audtools-shopify-collector/batch-collect.js C:\workspace\caiji\shop-futvortexstore-com-categories.csv ``` ## 安装依赖 ```bash npm install ``` 依赖： - playwright (已经安装) - csv-parser (新增，需要安装) ## 作者 Created by OpenClaw 自动生成，根据需求定制

path: batch-collect-fixed.js
content: #!/usr/bin/env node

/**
 * AudTools Shopify 批量采集脚本
 * 
 * 功能：
 * 1. 打开 https://www.audtools.com/users/shopns#/users/shopns/collecs
 * 2. 读取 CSV 文件中的完整链接列
 * 3. 在输入框中依次填入每个链接
 * 4. 商品数设置成 9999
 * 5. 点击提交，每条间隔 2 秒
 * 
 * 使用方法：
 *   node batch-collect-fixed.js [csv-file-path]
 * 
 * 示例：
 *   node batch-collect-fixed.js C:\workspace\caiji\shop-futvortexstore-com-categories.csv
 */

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');

// 手动解析 CSV，处理带引号的 CSV
function parseCSV(content) {
  const lines = content.split('\n').filter(line => line.trim());
  if (lines.length === 0) {
    return [];
  }
  
  // 读取表头
  const headers = parseCSVLine(lines[0]);
  console.log(`📊 表头: ${headers.join(', ')}`);
  
  const results = [];
  for (let i = 1; i < lines.length; i++) {
    const line = lines[i];
    if (!line.trim()) continue;
    
    const values = parseCSVLine(line);
    const row = {};
    headers.forEach((header, index) => {
      row[header] = values[index] || '';
    });
    results.push(row);
  }
  
  return results;
}

// 解析 CSV 一行，处理引号
function parseCSVLine(line) {
  const result = [];
  let current = '';
  let inQuotes = false;
  
  for (let i = 0; i < line.length; i++) {
    const char = line[i];
    
    if (char === '"' && inQuotes && line[i+1] === '"') {
      current += '"';
      i++;
    } else if (char === '"') {
      inQuotes = !inQuotes;
    } else if (char === ',' && !inQuotes) {
      result.push(current.trim());
      current = '';
    } else {
      current += char;
    }
  }
  
  result.push(current.trim());
  return result;
}

async function batchCollect(csvFilePath) {
  // 检查文件是否存在
  if (!fs.existsSync(csvFilePath)) {
    console.error(`❌ 文件不存在: ${csvFilePath}`);
    process.exit(1);
  }
  
  // 读取 CSV 文件
  console.log(`📖 正在读取 CSV 文件: ${csvFilePath}`);
  const content = fs.readFileSync(csvFilePath, 'utf-8');
  const results = parseCSV(content);
  
  console.log(`✅ 读取完成，共 ${results.length} 条链接`);
  
  // 提取完整链接列 - 尝试多种匹配
  const links = [];
  results.forEach(row => {
    let link = null;
    // 尝试不同的键名
    for (const key in row) {
      if (key.includes('完整链接')) {
        link = row[key];
        break;
      }
    }
    if (link && link.trim()) {
      links.push(link.trim());
    }
  });
  
  console.log(`🔗 有效链接数量: ${links.length}`);
  
  if (links.length === 0) {
    console.log('⚠️  调试 - 所有行:');
    results.forEach((row, i) => {
      console.log(`  行 ${i+1}: 键 = ${Object.keys(row).join(', ')}`);
    });
    console.error('❌ 没有找到有效的链接');
    process.exit(1);
  }
  
  // 输出前 5 条预览
  console.log('\n📋 前 5 条链接预览:');
  links.slice(0, 5).forEach((link, i) => {
    console.log(`  ${i+1}. ${link}`);
  });
  
  // 启动浏览器
  const browser = await chromium.launch({ 
    headless: false,
    timeout: 60000
  });
  
  const context = await browser.newContext({
    viewport: { width: 1920, height: 1080 },
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  });
  
  const page = await context.newPage();
  page.setDefaultTimeout(60000);
  
  const url = 'https://www.audtools.com/users/shopns#/users/shopns/collecs';
  console.log(`\n🌐 正在打开: ${url}`);
  
  try {
    await page.goto(url, { 
      waitUntil: 'domcontentloaded',
      timeout: 60000 
    });
    
    console.log('✅ 页面加载完成');
    
    // 等待页面加载，检查是否需要登录
    await page.waitForTimeout(3000);
    
    // 检查是否有登录表单
    const hasLoginForm = await page.$('form input[type="password"], input[name="password"]');
    
    if (hasLoginForm) {
      console.log('\n🔐 需要登录，请输入账户密码后继续...');
      console.log('请在浏览器中完成登录，然后告诉我继续执行...');
      // 这里需要用户手动登录
      // 我们保持浏览器打开，让用户操作
      console.log('\n⚠️  需要你手动输入账户密码登录，登录完成后告诉我继续执行。');
      return {
        status: 'need-login',
        browser: browser,
        page: page,
        links: links
      };
    }
    
    // 如果不需要登录，直接开始批量提交
    await processBatch(page, links);
    
    await browser.close();
    
    console.log('\n🎉 批量采集完成！');
    return {
      status: 'completed',
      total: links.length
    };
    
  } catch (error) {
    console.error('❌ 错误:', error.message);
    await browser.close();
    throw error;
  }
}

async function processBatch(page, links) {
  console.log('\n🚀 开始批量处理...');
  
  // 等待页面加载完成，找到输入框
  console.log('🔍 查找输入框...');
  
  // 等待输入框出现
  const inputSelector = await findInputSelector(page);
  console.log(`✅ 找到输入框，使用选择器: ${inputSelector}`);
  
  // 找到商品数量输入框
  const quantitySelector = await findQuantityInput(page);
  console.log(`✅ 找到商品数量输入框: ${quantitySelector || '未找到，将使用默认'}`);
  
  // 处理每个链接
  for (let i = 0; i < links.length; i++) {
    const link = links[i];
    console.log(`\n[${i+1}/${links.length}] 处理: ${link}`);
    
    try {
      // 清空输入框，填入链接
      await page.fill(inputSelector, '');
      await page.fill(inputSelector, link);
      console.log(`  ✅ 已填入链接`);
      
      // 设置商品数量为 9999
      if (quantitySelector) {
        await page.fill(quantitySelector, '');
        await page.fill(quantitySelector, '9999');
        console.log(`  ✅ 商品数量已设置为 9999`);
      }
      
      // 查找提交按钮并点击
      const submitButton = await findSubmitButton(page);
      if (submitButton) {
        await submitButton.click();
        console.log(`  ✅ 已点击提交`);
        
        // 等待 2 秒
        console.log(`  ⏳ 等待 2 秒...`);
        await page.waitForTimeout(2000);
      }
      
    } catch (error) {
      console.error(`  ❌ 处理失败: ${error.message}`);
      // 继续下一个
      await page.waitForTimeout(2000);
    }
  }
  
  console.log(`\n✅ 所有 ${links.length} 条链接处理完成`);
  console.log('请在浏览器中查看采集结果，导出数据...');
}

async function findInputSelector(page) {
  // 尝试多种选择器找链接输入框
  const selectors = [
    'input[name="collection_url"]',
    'input[name="url"]',
    'input[placeholder*="url"]',
    'input[placeholder*="link"]',
    'input[placeholder*="collection"]',
    'textarea[name*="collection"]',
    'textarea',
    '.form-control',
    'input[type="text"]'
  ];
  
  for (const selector of selectors) {
    const element = await page.$(selector);
    if (element) {
      return selector;
    }
  }
  
  // 如果都没找到，返回第一个 text input
  return 'input[type="text"]';
}

async function findQuantityInput(page) {
  // 尝试多种选择器找商品数量输入框
  const selectors = [
    'input[name="limit"]',
    'input[name="quantity"]',
    'input[name="count"]',
    'input[name="products"]',
    'input[placeholder*="limit"]',
    'input[placeholder*="quantity"]',
    'input[placeholder*="count"]',
    'input[placeholder*="product"]',
    'input[type="number"]'
  ];
  
  for (const selector of selectors) {
    const element = await page.$(selector);
    if (element) {
      return selector;
    }
  }
  
  return null;
}

async function findSubmitButton(page) {
  // 尝试多种选择器找提交按钮
  const selectors = [
    'button[type="submit"]',
    'input[type="submit"]',
    'button:has-text("提交")',
    'button:has-text("Submit")',
    'button:has-text("采集")',
    'button:has-text("Start")',
    'button:has-text("开始")',
    '.btn-primary',
    '.btn-success',
    'button'
  ];
  
  for (const selector of selectors) {
    const element = await page.$(selector);
    if (element) {
      return element;
    }
  }
  
  return null;
}

// 主函数
async function main() {
  const args = process.argv.slice(2);
  
  let csvFilePath = args[0];
  
  if (!csvFilePath) {
    // 默认路径
    csvFilePath = 'C:\\workspace\\caiji\\full\\shop-futvortexstore-com-categories.csv';
  }
  
  console.log(`🚀 AudTools Shopify 批量采集
  CSV 文件: ${csvFilePath}
  设置商品数: 9999
  操作间隔: 2 秒
`);
  
  try {
    const result = await batchCollect(csvFilePath);
    
    if (result.status === 'need-login') {
      console.log('\n⏸️  已暂停，请在打开的浏览器中登录账户，登录完成后告诉我继续...');
      // 不关闭浏览器，保持打开让用户登录
      // 用户继续后会继续执行
    } else {
      console.log('\n✅ 批量处理完成！请在浏览器中导出采集结果。');
      process.exit(0);
    }
    
  } catch (error) {
    console.error('\n💥 处理失败:', error.message);
    process.exit(1);
  }
}

// 如果直接运行
if (require.main === module) {
  main();
}

module.exports = batchCollect;


path: batch-collect.js
content: #!/usr/bin/env node

/**
 * AudTools Shopify 批量采集脚本
 * 
 * 功能：
 * 1. 打开 https://www.audtools.com/users/shopns#/users/shopns/collecs
 * 2. 读取 CSV 文件中的完整链接列
 * 3. 在输入框中依次填入每个链接
 * 4. 商品数设置成 9999
 * 5. 点击提交，每条间隔 2 秒
 * 
 * 使用方法：
 *   node batch-collect.js [csv-file-path]
 * 
 * 示例：
 *   node batch-collect.js C:\workspace\caiji\shop-futvortexstore-com-categories.csv
 */

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
const csv = require('csv-parser');

async function batchCollect(csvFilePath) {
  // 检查文件是否存在
  if (!fs.existsSync(csvFilePath)) {
    console.error(`❌ 文件不存在: ${csvFilePath}`);
    process.exit(1);
  }
  
  // 读取 CSV 文件
  const results = [];
  console.log(`📖 正在读取 CSV 文件: ${csvFilePath}`);
  
  await new Promise((resolve, reject) => {
    fs.createReadStream(csvFilePath)
      .pipe(csv())
      .on('data', (data) => results.push(data))
      .on('end', resolve)
      .on('error', reject);
  });
  
  console.log(`✅ 读取完成，共 ${results.length} 条链接`);
  
  // 提取完整链接列
  const links = results.map(row => row['完整链接']).filter(link => link && link.trim());
  console.log(`🔗 有效链接数量: ${links.length}`);
  
  if (links.length === 0) {
    console.error('❌ 没有找到有效的链接');
    process.exit(1);
  }
  
  // 输出前 5 条预览
  console.log('\n📋 前 5 条链接预览:');
  links.slice(0, 5).forEach((link, i) => {
    console.log(`  ${i+1}. ${link}`);
  });
  
  // 启动浏览器
  const browser = await chromium.launch({ 
    headless: false,
    timeout: 60000
  });
  
  const context = await browser.newContext({
    viewport: { width: 1920, height: 1080 },
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  });
  
  const page = await context.newPage();
  page.setDefaultTimeout(60000);
  
  const url = 'https://www.audtools.com/users/shopns#/users/shopns/collecs';
  console.log(`\n🌐 正在打开: ${url}`);
  
  try {
    await page.goto(url, { 
      waitUntil: 'domcontentloaded',
      timeout: 60000 
    });
    
    console.log('✅ 页面加载完成');
    
    // 等待页面加载，检查是否需要登录
    await page.waitForTimeout(3000);
    
    // 检查是否有登录表单
    const hasLoginForm = await page.$('form input[type="password"], input[name="password"]');
    
    if (hasLoginForm) {
      console.log('\n🔐 需要登录，请输入账户密码后继续...');
      console.log('请在浏览器中完成登录，然后按回车继续...');
      // 这里需要用户手动登录
      // 我们保持浏览器打开，让用户操作
      console.log('\n⚠️  需要你手动输入账户密码登录，登录完成后告诉我继续执行。');
      return {
        status: 'need-login',
        browser: browser,
        page: page,
        links: links
      };
    }
    
    // 如果不需要登录，直接开始批量提交
    await processBatch(page, links);
    
    await browser.close();
    
    console.log('\n🎉 批量采集完成！');
    return {
      status: 'completed',
      total: links.length
    };
    
  } catch (error) {
    console.error('❌ 错误:', error.message);
    await browser.close();
    throw error;
  }
}

async function processBatch(page, links) {
  console.log('\n🚀 开始批量处理...');
  
  // 等待页面加载完成，找到输入框
  console.log('🔍 查找输入框...');
  
  // 等待输入框出现
  const inputSelector = await findInputSelector(page);
  console.log(`✅ 找到输入框，使用选择器: ${inputSelector}`);
  
  // 找到商品数量输入框
  const quantitySelector = await findQuantityInput(page);
  console.log(`✅ 找到商品数量输入框: ${quantitySelector || '未找到，将使用默认'}`);
  
  // 处理每个链接
  for (let i = 0; i < links.length; i++) {
    const link = links[i];
    console.log(`\n[${i+1}/${links.length}] 处理: ${link}`);
    
    try {
      // 清空输入框，填入链接
      await page.fill(inputSelector, '');
      await page.fill(inputSelector, link);
      console.log(`  ✅ 已填入链接`);
      
      // 设置商品数量为 9999
      if (quantitySelector) {
        await page.fill(quantitySelector, '');
        await page.fill(quantitySelector, '9999');
        console.log(`  ✅ 商品数量已设置为 9999`);
      }
      
      // 查找提交按钮并点击
      const submitButton = await findSubmitButton(page);
      if (submitButton) {
        await submitButton.click();
        console.log(`  ✅ 已点击提交`);
        
        // 等待 2 秒
        console.log(`  ⏳ 等待 2 秒...`);
        await page.waitForTimeout(2000);
      }
      
    } catch (error) {
      console.error(`  ❌ 处理失败: ${error.message}`);
      // 继续下一个
      await page.waitForTimeout(2000);
    }
  }
  
  console.log(`\n✅ 所有 ${links.length} 条链接处理完成`);
  console.log('请在浏览器中查看采集结果，导出数据...');
}

async function findInputSelector(page) {
  // 尝试多种选择器找链接输入框
  const selectors = [
    'input[name="collection_url"]',
    'input[name="url"]',
    'input[placeholder*="url"]',
    'input[placeholder*="link"]',
    'input[placeholder*="collection"]',
    'textarea[name*="collection"]',
    'textarea',
    '.form-control',
    'input[type="text"]'
  ];
  
  for (const selector of selectors) {
    const element = await page.$(selector);
    if (element) {
      return selector;
    }
  }
  
  // 如果都没找到，返回第一个 text input
  return 'input[type="text"]';
}

async function findQuantityInput(page) {
  // 尝试多种选择器找商品数量输入框
  const selectors = [
    'input[name="limit"]',
    'input[name="quantity"]',
    'input[name="count"]',
    'input[name="products"]',
    'input[placeholder*="limit"]',
    'input[placeholder*="quantity"]',
    'input[placeholder*="count"]',
    'input[placeholder*="product"]',
    'input[type="number"]'
  ];
  
  for (const selector of selectors) {
    const element = await page.$(selector);
    if (element) {
      return selector;
    }
  }
  
  return null;
}

async function findSubmitButton(page) {
  // 尝试多种选择器找提交按钮
  const selectors = [
    'button[type="submit"]',
    'input[type="submit"]',
    'button:has-text("提交")',
    'button:has-text("Submit")',
    'button:has-text("采集")',
    'button:has-text("Start")',
    'button:has-text("开始")',
    '.btn-primary',
    '.btn-success',
    'button'
  ];
  
  for (const selector of selectors) {
    const element = await page.$(selector);
    if (element) {
      return element;
    }
  }
  
  return null;
}

// 主函数
async function main() {
  const args = process.argv.slice(2);
  
  let csvFilePath = args[0];
  
  if (!csvFilePath) {
    // 默认路径
    csvFilePath = 'C:\\workspace\\caiji\\shop-futvortexstore-com-categories.csv';
  }
  
  console.log(`🚀 AudTools Shopify 批量采集
  CSV 文件: ${csvFilePath}
  设置商品数: 9999
  操作间隔: 2 秒
`);
  
  try {
    const result = await batchCollect(csvFilePath);
    
    if (result.status === 'need-login') {
      console.log('\n⏸️  已暂停，请在打开的浏览器中登录账户，登录完成后告诉我继续...');
      // 不关闭浏览器，保持打开让用户登录
      // 用户继续后会继续执行
    } else {
      console.log('\n✅ 批量处理完成！请在浏览器中导出采集结果。');
      process.exit(0);
    }
    
  } catch (error) {
    console.error('\n💥 处理失败:', error.message);
    process.exit(1);
  }
}

// 如果直接运行
if (require.main === module) {
  main();
}

module.exports = batchCollect;


path: package-lock.json
content: {
  "name": "audtools-shopify-collector",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "audtools-shopify-collector",
      "version": "1.0.0",
      "license": "MIT",
      "dependencies": {
        "csv-parser": "^3.0.0",
        "playwright": "^1.40.0"
      },
      "bin": {
        "audtools-batch-collect": "batch-collect.js"
      }
    },
    "node_modules/csv-parser": {
      "version": "3.2.0",
      "resolved": "https://registry.npmmirror.com/csv-parser/-/csv-parser-3.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "bin": {
        "csv-parser": "bin/csv-parser"
      },
      "engines": {
        "node": ">= 10"
      }
    },
    "node_modules/fsevents": {
      "version": "2.3.2",
      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
      "integrity": [REDACTED_SECRET],
      "hasInstallScript": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
      }
    },
    "node_modules/playwright": {
      "version": "1.58.2",
      "resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.58.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "Apache-2.0",
      "dependencies": {
        "playwright-core": "1.58.2"
      },
      "bin": {
        "playwright": "cli.js"
      },
      "engines": {
        "node": ">=18"
      },
      "optionalDependencies": {
        "fsevents": "2.3.2"
      }
    },
    "node_modules/playwright-core": {
      "version": "1.58.2",
      "resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.58.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "Apache-2.0",
      "bin": {
        "playwright-core": "cli.js"
      },
      "engines": {
        "node": ">=18"
      }
    }
  }
}


path: package.json
content: {
  "name": "audtools-shopify-collector",
  "version": "1.0.0",
  "description": "AudTools Shopify 批量采集器 - 读取 CSV 中的分类链接，批量提交到 audtools 采集商品，自动填充链接设置数量，间隔提交",
  "main": "batch-collect.js",
  "bin": {
    "audtools-batch-collect": "./batch-collect.js"
  },
  "dependencies": {
    "playwright": "^1.40.0",
    "csv-parser": "^3.0.0"
  },
  "keywords": [
    "audtools",
    "shopify",
    "batch",
    "collector",
    "csv",
    "automation"
  ],
  "author": "OpenClaw",
  "license": "MIT"
}


path: setup.json
content: {
  "setupComplete": true,
  "prerequisites": {
    "playwright": {
      "required": true,
      "installed": true,
      "description": "Playwright for browser automation (already installed by web-automation)"
    },
    "csv-parser": {
      "required": true,
      "installed": true,
      "description": "CSV parser for reading input"
    }
  },
  "setupInstructions": [
    "1. Run: npm install",
    "2. Test: node batch-collect.js C:\\workspace\\caiji\\shop-futvortexstore-com-categories.csv"
  ],
  "verifySetup": "Run 'node batch-collect.js' to verify"
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)