【RPA】前端实现分国家自动化采集Google商品数据写入excel

采集背景

  • 公司的一些商品通过Google Shopping进行广告推广,约占Google渠道流量60%。
  • Google在广告界面展示商品的评分情况,影响客户决策,需定期对该评分情况进行监测,业务会评估监测结果,由编辑部门编撰、补充高分评论,维护重点商品评分;
  • Google Shopping评论信息来源-独立站广告系统上传给Google,Google经过评估过滤校验后,形成最终评分;
  • 爬取频次要求:1个月/次

由于涉及到公司的隐私信息,下面我用Amazon平台举例子给大家展示了。

采集步骤及要求

需要采集的国家如下:

国家 站点
United States US
Australia AU
Canada CA
Germany DE
Spain ES
Italy IT
United Kingdom UK
France FR
Ireland EUR
Netherlands NL
Poland PL
Mexico MX

STEP1 : Google设置到对应的国家,如下图所示:

STEP2:指定seller,并排列组合来切换价格、评分信息以命中更多商品;

价格区间(示例为按USD币种呈现的价格区间,其他国家需进行换算): 10-30、30.01-60、60.01-100、100-200、200-300、300-500、500-1000、1000+

技术实现

由于整体逻辑是在 egg 服务里实现的,这里就不牵扯更多的东西了,把核心的技术要点展示出来。

STEP1:我们先把要用到的数据常量准备好,

  • currencyRatio是美元换算成其他国家币种的比例,做价格区间时切换成对应国家币种的价格区间;
  • site字段在勾选指定卖家时候会用到;
  • _site是需要传给后端的
js 复制代码
const countries = [
  { name: "美国", site: "Amazon.com", _site: "US", currencyRatio: 1 },
  { name: "法国", site: "Amazon.fr - Seller", _site: "FR", currencyRatio: 0.92 },
  { name: "澳大利亚", site: "Amazon AU", _site: "AU", currencyRatio: 1.52 },
  { name: "加拿大", site: "Amazon.ca", _site: "CA", currencyRatio: 1.32 },
  { name: "德国", site: "Amazon.de", _site: "DE", currencyRatio: 0.92 },
  { name: "西班牙", site: "Amazon.es", _site: "ES", currencyRatio: 0.92 },
  { name: "意大利", site: "Amazon.it", _site: "IT", currencyRatio: 0.92 },
  { name: "英国", site: "uk.Amazon.com", _site: "UK", currencyRatio: 0.79 },
  { name: "爱尔兰", site: "Amazon.eur", _site: "EUR", currencyRatio: 0.92 },
  { name: "荷兰", site: "Amazon.nl", _site: "NL", currencyRatio: 1.79 },
  { name: "波兰", site: "Amazon.pl", _site: "PL", currencyRatio: 4 },
  { name: "墨西哥", site: "Amazon.mx", _site: "MX", currencyRatio: 17.53 },
];

// 美国的价格区间分段
const usPriceRanges = [
  { min: 10, max: 30 },
  { min: 30.01, max: 60 },
  { min: 60.01, max: 100 },
  { min: 100.01, max: 200 },
  { min: 200.01, max: 300 },
  { min: 300.01, max: 500 },
  { min: 500.01, max: 1000 },
  { min: 1000.01, max: "" },
];

// 生成excel的表头字段
const excelHeader = {
  skuName: { text: "商品标题", width: 200 },
  shopName: { text: "店铺名", width: 15 },
  mark: { text: "评分", width: 15 },
  commentCount: { text: "评论数量", width: 15 },
  link: { text: "商品链接", width: 250 },
  page: { text: "当前页", width: 15 },
  min: { text: "最小价格", width: 15 },
  max: { text: "最大价格", width: 15 },
  rating: { text: "星级", width: 15 },
  totalPage: { text: "总页数", width: 15 },
};

STEP2 :使用 Puppeteer 启动一个浏览器实例

js 复制代码
const puppeteer = require("puppeteer");

const createBrowserInstance = async () => {
  let browser = await puppeteer.launch({
    headless: false,
    defaultViewport: {
      width: 1920,
      height: 1080,
    },
  });
  const page = await browser.newPage();
  
  return page;
};

STEP3:切换国家逻辑,这里在实际项目里会对文本元素的查找支持中英文的兼容逻辑,这里先使用中文模式进行查找,如:搜索结果区域和一些国家切换的特殊逻辑处理就会用到

js 复制代码
const switchLanguagePage =
  "https://www.google.com/preferences?hl=&prev=&lang=1&prev=https://www.google.com/preferences?hl%3D%26prev%3D";

async function clickTargetElement({
  page,
  selector,
  callback,
  searchText,
  fullEqual = false,
}) {
  const elements = await page.$$(selector);

  for (const element of elements) {
    const elementText = await page.evaluate(callback, element);

    if (
      fullEqual ? elementText === searchText : elementText.includes(searchText)
    ) {
      await element.click();
      break;
    }
  }
  return elements.length;
}

async function switchCountryBySearchText(page, country) {
  await page.goto(switchLanguagePage); // 先切换语言

  // 打开切换国家的弹窗
  await clickTargetElement({
    page,
    selector: "span.sjVJQd.RqT6ge",
    callback: (element) => element.textContent,
    searchText: "搜索结果区域",
  });

  await page.waitForTimeout(1000 * 3);

  await page.type("input[type=text]", country.name, { delay: 100 });

  await page.waitForTimeout(1000 * 3);

  // 点击搜索的国家 对某些国家需要特殊处理
  await clickTargetElement({
    page,
    selector: "g-menu-item",
    callback: (element) => element.textContent,
    searchText: country.name,
    fullEqual: ["西班牙", "意大利", "爱尔兰", "荷兰", "波兰"].includes(
      country.name
    ),
  });

  await page.waitForTimeout(1000 * 3);

  // 点击确认
  const confirmEl =
    "div.qk7LXc.TUOsUe.Fb1AKc.PnNX7d.ivkdbf > div:last-child > span:last-child";
  await page.waitForSelector(confirmEl, { visible: true });
  await page.click(confirmEl);
}

STEP4:搜索 amazon关键词,并点击到对应的购物tab,这里会有多种dom形态,所以做了特殊处理,然后,卖家栏设置到对应的关键词上,也就是上面的site字段;同理,这里也只是做了中文模式的逻辑

js 复制代码
async function openAmazonShopPage(page) {
  await page.goto("https://www.google.com");

  await page.waitForTimeout(3000);

  await page.type("textarea[name=q]", "amazon");

  await page.keyboard.press("Enter");
}
async function clickShoppingTab(page, sellerKeyword) {
  // 点击购物tab
  const hasEl = await clickTargetElement({
    page,
    selector: "span.FMKtTb",
    callback: (element) => element.textContent,
    searchText: "购物",
  });

  // 如果没有购物tab,那就是其它类型的dom结构
  if (!hasEl) {
    await clickTargetElement({
      page,
      selector: "div.hdtb-mitem",
      callback: (element) => element.textContent,
      searchText: "购物",
    });
  }

  await page.waitForTimeout(1000 * 10);

  // 卖家栏勾选带amazon关键字的
  await clickTargetElement({
    page,
    selector: "span.lg3aE > span",
    callback: (element) => element.textContent,
    searchText: sellerKeyword,
  });
}

STEP5:生成评分和价格区间的排列组合,结构如下图所示,后面我们循环这个数组来完成单个国家的数据采集

js 复制代码
// 获取价格和评分的组合
function getRatingsAndPrices(ratio, usPriceRanges) {
  const priceRanges = usPriceRanges.map((item) => ({
    min: (item.min * ratio).toFixed(2),
    max: item.max ? (item.max * ratio).toFixed(2) + "" : "",
  }));

  return Array.from({ length: 4 }, (_, index) => index + 1).map(
    (ratingRange) => ({
      priceRanges,
      rating: `${ratingRange} 颗星及以上`,
    })
  );
}
// 循环评分
for (const item of ratingsAndPrices) {
  // 步骤6,见下面
}

STEP6 :先处理集 1 颗星及以上,在单个评分里面再循环内部的 priceRanges,去采集每个价格区间期的数据

js 复制代码
// 切换评分
async function switchRating(page, rating) {
  // 商品评分 勾选星级
  await page.evaluate((rating) => {
    const allMarkEl = document.querySelectorAll(".lg3aE.aIuqNd .cNaB2e");
    for (const el of allMarkEl) {
      if (el.textContent.includes(rating)) {
        el.click();
        break;
      }
    }
  }, rating);
}

async processRating(page, item, country, app, filePath) {
  // 点击评分
  await switchRating(page, item.rating);
  await page.waitForTimeout(1000 * 20);

  // 循环价格区间
  for (const range of item.priceRanges) {
    // 步骤7,见下面
  }
}

STEP7:切换价格区间,然后判断有多少页,依次循环每一页对数据做采集,并将当页的数据写入到excel文件中,单个国家采集完的部分数据如下图所示

js 复制代码
// 切换价格区间
async function switchPriceRange(page, range) {
  await setInputValue(page, "input[name=lower]", range.min);
  if (range.max) {
    await setInputValue(page, "input[name=upper]", range.max);
  }
  await page.waitForTimeout(1000 * 10);
  await page.waitForSelector("button.sh-dr__prs", { visible: true });
  await page.click("button.sh-dr__prs");
}

async processPriceRange(page, app, { range, item, country, filePath }) {
  await switchPriceRange(page, range);
  await page.waitForTimeout(1000 * 20);

  let currentPage = 1;
  let totalPage = await getPageCount(page);

  while (currentPage <= totalPage) {
    const currentParams = {
      totalPage,
      page: currentPage,
      rating: item.rating,
      ...range,
    };
   
    const result = await getItemDetails(page, currentParams);

    await writeToExcel(result, filePath, excelHeader);

    if (currentPage > 1) await clickTargetPage(page, currentPage);
    await page.waitForTimeout(1000 * 20);

    currentPage++;
  }
}

STEP8:将文件通过接口形式传给后端同学写入数据库,经过BI的后续数据加工处理形成最终报表给到业务同学。

失败的原因或成功的结果都会通过钉钉机器人的方式发送到告警群,便于快速的得知任务状态或方便进行排错处理。

跑完上述完整流程的一个关键操作:操作完每一步后设置足够长的等待时间来模拟人工操作流程,不然会很容易触发人机验证,如下图:

相关推荐
IMPYLH3 小时前
HTML 的 <small> 元素
前端·网络·html
IT_陈寒3 小时前
Vue的响应式让我加班到凌晨,问题竟出在这个不起眼的地方
前端·人工智能·后端
AlienZHOU12 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc15 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师17 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen17 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒17 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
65岁退休Coder18 小时前
PI Agent 开发一个生产级 Harness
后端·node.js·agent
前端snow18 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林81818 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器