You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:编写Google Sheet AppScript批量获取URL的标题、描述等元数据

解决Google Sheet批量提取URL元标签的问题

问题根源

IMPORTXML成功率低是因为不同网站的元标签结构没有统一标准:

  • Title可能在<title>标签,也可能在og:title元标签
  • Description可能是name="description"或property="og:description"
  • Logo的位置更分散:og:image、rel="apple-touch-icon"、rel="icon"都有可能
    单一XPath根本覆盖不了所有情况,得用更灵活的脚本处理。

用Google Apps Script实现批量提取

下面的脚本会自动处理多种标签结构,把结果写入相邻列,新手也能直接用:

function extractMetaTags() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const urlColumn = 1; // URL在A列(从1开始计数)
  const startRow = 2; // 第1行是表头,从第2行开始处理
  const lastRow = sheet.getLastRow();
  
  // 批量获取所有URL
  const urls = sheet.getRange(startRow, urlColumn, lastRow - startRow + 1).getValues();
  
  // 遍历每个URL,提取信息
  urls.forEach((row, index) => {
    const url = row[0];
    if (!url) return; // 跳过空行
    
    let title = "";
    let description = "";
    let keywords = "";
    let logo = "";
    
    try {
      // 发送请求获取HTML,设置模拟浏览器的User-Agent避免被拦截
      const options = {
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
        }
      };
      const response = UrlFetchApp.fetch(url, options);
      const html = response.getContentText();
      const doc = XmlService.parse(html);
      const root = doc.getRootElement();
      
      // 提取Title:先取<title>标签,再取og:title
      const titleElements = root.getDescendants().filter(elem => elem.getName() === 'title');
      if (titleElements.length > 0) {
        title = titleElements[0].getText().trim();
      } else {
        const ogTitle = root.getDescendants().filter(elem => 
          elem.getAttribute('property')?.getValue() === 'og:title'
        );
        if (ogTitle.length > 0) title = ogTitle[0].getAttribute('content')?.getValue().trim() || "";
      }
      
      // 提取Description:先取name="description",再取og:description
      const descMeta = root.getDescendants().filter(elem => 
        elem.getAttribute('name')?.getValue() === 'description'
      );
      if (descMeta.length > 0) {
        description = descMeta[0].getAttribute('content')?.getValue().trim() || "";
      } else {
        const ogDesc = root.getDescendants().filter(elem => 
          elem.getAttribute('property')?.getValue() === 'og:description'
        );
        if (ogDesc.length > 0) description = ogDesc[0].getAttribute('content')?.getValue().trim() || "";
      }
      
      // 提取Keywords:name="keywords"
      const keywordsMeta = root.getDescendants().filter(elem => 
        elem.getAttribute('name')?.getValue() === 'keywords'
      );
      if (keywordsMeta.length > 0) {
        keywords = keywordsMeta[0].getAttribute('content')?.getValue().trim() || "";
      }
      
      // 提取Logo:优先级og:image > apple-touch-icon > favicon
      const ogImage = root.getDescendants().filter(elem => 
        elem.getAttribute('property')?.getValue() === 'og:image'
      );
      if (ogImage.length > 0) {
        logo = ogImage[0].getAttribute('content')?.getValue().trim() || "";
      } else {
        const appleIcon = root.getDescendants().filter(elem => 
          elem.getAttribute('rel')?.getValue() === 'apple-touch-icon'
        );
        if (appleIcon.length > 0) {
          logo = appleIcon[0].getAttribute('href')?.getValue().trim() || "";
        } else {
          const favicon = root.getDescendants().filter(elem => 
            elem.getAttribute('rel')?.getValue() === 'icon'
          );
          if (favicon.length > 0) logo = favicon[0].getAttribute('href')?.getValue().trim() || "";
        }
      }
      
      // 处理相对路径的Logo URL
      if (logo && !logo.startsWith('http')) {
        const urlObj = new URL(url);
        logo = `${urlObj.protocol}//${urlObj.host}${logo.startsWith('/') ? '' : '/'}${logo}`;
      }
      
    } catch (e) {
      // 处理请求失败的情况,比如网站无法访问
      title = "请求失败";
      description = e.toString();
    }
    
    // 把结果写入相邻列:B=Title, C=Description, D=Keywords, E=Logo
    const resultRow = startRow + index;
    sheet.getRange(resultRow, 2).setValue(title);
    sheet.getRange(resultRow, 3).setValue(description);
    sheet.getRange(resultRow, 4).setValue(keywords);
    sheet.getRange(resultRow, 5).setValue(logo);
    
    // 每处理10个URL暂停1秒,避免触发请求限制
    if ((index + 1) % 10 === 0) {
      Utilities.sleep(1000);
    }
  });
  
  SpreadsheetApp.getUi().alert("提取完成!");
}

使用步骤

  1. 打开你的Google Sheet,点击顶部菜单栏的「扩展」→「Apps Script」
  2. 删除默认的myFunction,粘贴上面的代码
  3. 根据你的表格修改参数:
    • 如果URL在B列,把urlColumn改成2
    • 如果第1行不是表头,把startRow改成1
  4. 点击工具栏的运行按钮,第一次运行会要求授权,按照提示完成授权(Google会提示脚本未验证,点击「高级」→「继续访问」即可)
  5. 等待脚本运行完成,结果会自动写入B-E列

注意事项

  • Google Apps Script有每日请求配额,几百个URL建议分2-3次运行,或者调整脚本里的暂停时间
  • 部分动态渲染的网站(比如用React/Vue构建的单页应用),静态解析HTML拿不到元标签,这种情况需要用Puppeteer,但配置起来更复杂,新手可以先处理静态网站
  • 如果遇到某个网站一直请求失败,可能是对方有反爬机制,可以尝试修改User-Agent的值

内容的提问来源于stack exchange,提问作者AyS 0908

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:05:03