Hướng Dẫn Apps Script

Crawl Dữ Liệu Web Về Google Sheets Tự Động Với Apps Script

Tuân HoangTuân Hoang
14 tháng 5, 2026
5 phút đọc
Ảnh minh họa bài viết: Crawl Dữ Liệu Web Về Google Sheets Tự Động Với Apps Script

Apps Script Có Thể Crawl Web Không?

Có — Apps Script cung cấp UrlFetchApp để gọi HTTP request. Bạn có thể lấy dữ liệu từ website, REST API, RSS/XML feed và lưu vào Google Sheets. Phù hợp cho: theo dõi giá sản phẩm, lấy tỷ giá ngân hàng, đọc RSS tin tức, gọi API thời tiết.

Crawl Từ REST API Công Khai

/**
 * Lấy tỷ giá USD/VND từ API và ghi vào Sheet
 */
function fetchExchangeRate() {
  try {
    const url = 'https://api.exchangerate-api.com/v4/latest/USD';
    const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });

    if (response.getResponseCode() !== 200) {
      throw new Error('API error: HTTP ' + response.getResponseCode());
    }

    const data = JSON.parse(response.getContentText());
    const vndRate = data.rates.VND;

    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Tỷ Giá')
      || SpreadsheetApp.getActiveSpreadsheet().insertSheet('Tỷ Giá');

    sheet.appendRow([new Date(), 'USD/VND', vndRate, data.date]);
    console.log('Exchange rate saved: 1 USD = ' + vndRate + ' VND');
  } catch (err) {
    console.error('fetchExchangeRate error: ' + err.toString());
    throw err;
  }
}

Parse HTML Với XmlService

/**
 * Crawl giá sản phẩm từ trang web (ví dụ: lấy giá từ thẻ có class cụ thể)
 * Lưu ý: Chỉ dùng trên website cho phép scraping theo robots.txt
 */
function crawlProductPrice(productUrl) {
  try {
    const response = UrlFetchApp.fetch(productUrl, {
      muteHttpExceptions: true,
      followRedirects: true
    });

    if (response.getResponseCode() !== 200) throw new Error('HTTP ' + response.getResponseCode());

    const html = response.getContentText();

    // Dùng regex để extract giá (thay pattern theo cấu trúc HTML thực tế)
    const priceMatch = html.match(/class="product-price"[^>]*>([^<]+)/);
    const nameMatch = html.match(/<title>([^<]+)</title>/);

    const price = priceMatch ? priceMatch[1].replace(/[^d]/g, '') : 'N/A';
    const name = nameMatch ? nameMatch[1].trim() : productUrl;

    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Giá Sản Phẩm')
      || SpreadsheetApp.getActiveSpreadsheet().insertSheet('Giá Sản Phẩm');

    if (sheet.getLastRow() === 0) {
      sheet.appendRow(['Thời gian', 'Tên sản phẩm', 'Giá (VND)', 'URL']);
    }
    sheet.appendRow([new Date(), name, price, productUrl]);
    console.log('Price crawled: ' + name + ' = ' + price);
    return { name, price };
  } catch (err) {
    console.error('crawlProductPrice error: ' + err.toString());
    return { name: productUrl, price: 'ERROR' };
  }
}

Crawl RSS Feed Tin Tức

function fetchRSSFeed(feedUrl, sheetName) {
  try {
    const response = UrlFetchApp.fetch(feedUrl, { muteHttpExceptions: true });
    if (response.getResponseCode() !== 200) throw new Error('HTTP ' + response.getResponseCode());

    const xml = XmlService.parse(response.getContentText());
    const root = xml.getRootElement();
    const ns = root.getNamespace();
    const items = root.getChild('channel', ns)
      ? root.getChild('channel', ns).getChildren('item', ns)
      : root.getChildren('entry', ns); // Atom feed

    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
    if (sheet.getLastRow() === 0) sheet.appendRow(['Ngày', 'Tiêu đề', 'Link', 'Mô tả']);

    let added = 0;
    items.forEach(item => {
      const title = item.getChildText('title', ns) || '';
      const link = item.getChildText('link', ns) || '';
      const pubDate = item.getChildText('pubDate', ns) || item.getChildText('published', ns) || '';
      const desc = (item.getChildText('description', ns) || '').replace(/<[^>]+>/g, '').substring(0, 200);

      sheet.appendRow([pubDate ? new Date(pubDate) : new Date(), title, link, desc]);
      added++;
    });

    console.log('RSS: added ' + added + ' items from ' + feedUrl);
  } catch (err) {
    console.error('fetchRSSFeed error: ' + err.toString());
    throw err;
  }
}

Xử Lý Rate Limit Và Retry

function fetchWithRetry(url, options, maxRetries) {
  maxRetries = maxRetries || 3;
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const resp = UrlFetchApp.fetch(url, { ...options, muteHttpExceptions: true });
      if (resp.getResponseCode() === 429) {
        Utilities.sleep(attempt * 2000); // Back-off: 2s, 4s, 6s
        continue;
      }
      return resp;
    } catch (err) {
      if (attempt === maxRetries) throw err;
      Utilities.sleep(1000);
    }
  }
}

Lưu Ý Pháp Lý

  • Kiểm tra robots.txt của website trước khi crawl.
  • Không crawl dữ liệu cá nhân hay có bản quyền mà không được phép.
  • Ưu tiên dùng API chính thức nếu website có cung cấp.
  • Thêm Utilities.sleep() giữa các request để tránh bị block IP.

Chia sẻ bài viết:

Tuân Hoang

Tuân Hoang

Đội ngũ SheetStore

Google SheetsGoogle Apps ScriptCRMAutomationPhần mềm quản lý doanh nghiệp

Google Workspace Certified, 5+ years experience

Bạn thấy bài viết hữu ích?

Đăng ký nhận thông báo khi có bài viết mới.

Nhận thông báo khi có bài viết mới. Không spam, hứa luôn! 😊

Bình luận (0)

Vui lòng đăng nhập để tham gia thảo luận