Crawlee: Framework cào dữ liệu và Audit website mạnh mẽ cho NodeJS

    Tác giả
    12/10/2026 14 phút đọc 19 lượt xem
    Chế độ đọc
    📦 Trải nghiệm mã nguồn mở:

    Tải mã nguồn Crawlee trên GitHub

    Trong kỷ nguyên số, dữ liệu cấu trúc và thông tin kỹ thuật của website đóng vai trò cốt lõi trong việc đánh giá, xếp hạng và tối ưu hóa thứ hạng trên công cụ tìm kiếm. Đối với các kỹ sư phần mềm và chuyên gia Technical SEO, việc sở hữu một công cụ cào dữ liệu (web crawler) tự chủ, hiệu năng cao và có khả năng vượt qua các lớp bảo vệ chống bot hiện đại là yếu tố sống còn. Crawlee – thư viện cào dữ liệu và trích xuất thông tin mã nguồn mở hàng đầu dành cho Node.js do đội ngũ Apify phát triển – chính là giải pháp toàn diện thay thế hoàn hảo cho các crawler truyền thống cồng kềnh.

    Bài viết này phân tích chuyên sâu về kiến trúc nội tại của Crawlee, hướng dẫn cài đặt từng bước, xây dựng pipeline xử lý dữ liệu và hiện thực hóa một công cụ audit Technical SEO tự động hoàn chỉnh từ mã nguồn thực tế.

    Nguyên lý hoạt động của Crawlee trong Web Scraping và SEO Audit

    Crawlee không đơn thuần là một wrapper bao bọc các trình duyệt không đầu (Headless Browsers), mà là một hệ sinh thái scraping toàn diện được thiết kế để giải quyết những bài toán phức tạp nhất trong quá trình thu thập thông tin quy mô lớn (large-scale web scraping).

    Kiến trúc Module hóa và Cơ chế Autoscaling

    Một trong những điểm yếu lớn nhất của các crawler tự viết bằng Puppeteer thuần hoặc Playwright là việc quản lý tài nguyên hệ thống. Nếu mở quá nhiều tab cùng lúc, hệ thống sẽ rơi vào tình trạng quá tải CPU và tràn RAM dẫn đến crash tiến trình. Crawlee giải quyết triệt để vấn đề này nhờ cơ chế AutoscaledPool tích hợp sẵn. Hệ thống liên tục đo lường mức độ chiếm dụng bộ nhớ (Memory Heap) và tải xử lý của CPU. Khi tài nguyên tiệm cận ngưỡng cảnh báo, Crawlee tự động giảm tần suất khởi tạo tiến trình duyệt web và ngược lại sẽ tăng tốc song song (concurrency) khi hệ thống dồi dào tài nguyên.

    Tích hợp đa tầng: Cheerio, Puppeteer và Playwright

    Crawlee phân tách rõ ràng các chiến lược trích xuất dữ liệu tùy thuộc vào mục tiêu crawl:

    • CheerioCrawler: Xử lý thông qua HTTP request thuần túy và phân tích cú pháp HTML tĩnh. Tốc độ cực nhanh, tiêu tốn rất ít RAM và CPU, hoàn hảo cho các trang tin tức tĩnh, trang thương mại điện tử render sẵn (SSR) hoặc kiểm tra mã phản hồi HTTP (status code).
    • PlaywrightCrawler & PuppeteerCrawler: Sử dụng headless browsers thực thụ (Chromium, Firefox, WebKit) để thực thi toàn bộ JavaScript, đợi DOM render hoàn chỉnh, vượt qua hydration của các framework Single Page Application (SPA) như React, Vue, Angular hay Next.js.

    Cơ chế SessionPool và Quản lý Fingerprint chống chặn Bot

    Các hệ thống WAF hiện đại (Cloudflare, Akamai, DataDome) sử dụng thuật toán phân tích hành vi và nhận diện dấu vân tay trình duyệt (browser fingerprinting) bao gồm WebGL, Canvas, AudioContext, HTTP headers, TCP/IP stack để phát hiện bot tự động. Crawlee tích hợp sẵn SessionPool cùng gói tiện ích chống phát hiện tinh vi:

    • Tự động luân chuyển User-Agent hợp lệ theo từng phiên truy cập.
    • Mô phỏng kích thước viewport, timezone, ngôn ngữ hệ thống và font chữ thực tế.
    • Tự động lưu trữ và chia sẻ cookies, local storage giữa các yêu cầu thuộc cùng một phiên làm việc, hỗ trợ duy trì trạng thái đăng nhập hoặc vượt qua vòng kiểm tra phiên của máy chủ mục tiêu.
    • Khả năng tự động loại bỏ các session bị gắn cờ (blocked/captcha) và chuyển hướng yêu cầu sang session mới với proxy sạch.

    Yêu cầu hệ thống và môi trường triển khai

    Để vận hành Crawlee ổn định, đặc biệt khi triển khai cào dữ liệu sâu cho hàng chục ngàn URL phục vụ báo cáo SEO quy mô doanh nghiệp, bạn cần chuẩn bị hạ tầng phần cứng và phần mềm đáp ứng các tiêu chuẩn kỹ thuật sau:

    Cấu hình phần cứng và Runtime Node.js

    • Node.js: Phiên bản 18.0.0 LTS trở lên (khuyến nghị Node.js 20.x hoặc 22.x LTS để tối ưu hóa hiệu năng động cơ V8 và hỗ trợ Fetch API nguyên bản).
    • RAM: Tối thiểu 2GB đối với CheerioCrawler; từ 4GB – 8GB RAM đối với PlaywrightCrawler chạy đa tiến trình Chromium.
    • CPU: Tối thiểu 2 Core, khuyến nghị 4 Core trở lên để phân phối tải trình duyệt không đầu.
    • Hệ điều hành: Tương thích hoàn toàn trên Ubuntu 20.04/22.04 LTS, Debian, macOS và Windows 10/11 (khuyến khích Linux trong môi trường production).

    Triển khai Container hóa với Docker

    Khi chạy các headless browser như Chromium trên môi trường Linux server hoặc CI/CD, việc thiếu hụt các thư viện đồ họa hệ thống (như libnss3, libatk-bridge2.0-0) là lỗi phổ biến nhất. Đội ngũ phát triển Crawlee đã cung cấp sẵn image Docker chính thức đã được cài sẵn toàn bộ dependencies hệ điều hành:

    # Base image chính thức từ Apify cho Crawlee và Playwright
    FROM apify/actor-node-playwright:20
    
    WORKDIR /usr/src/app
    
    COPY package*.json ./
    RUN npm ci --only=production
    
    COPY . ./
    
    CMD ["node", "dist/main.js"]

    Hướng dẫn cài đặt thủ công Crawlee cho dự án NodeJS

    Bạn có thể tích hợp Crawlee trực tiếp vào dự án hiện có mà không cần phụ thuộc vào nền tảng đám mây của Apify. Dưới đây là các bước khởi tạo dự án NodeJS sử dụng TypeScript/ESM chuẩn.

    Khởi tạo dự án và cài đặt dependencies

    Mở terminal và thực thi các câu lệnh sau để khởi tạo thư mục và cài đặt các package cần thiết:

    # Khởi tạo thư mục dự án
    mkdir crawlee-seo-auditor
    cd crawlee-seo-auditor
    
    # Khởi tạo package.json
    npm init -y
    
    # Cài đặt crawlee và playwright
    npm install crawlee playwright
    
    # Cài đặt TypeScript và typings (nếu sử dụng TypeScript)
    npm install -D typescript @types/node tsx

    Cấu hình package.json chuẩn bị cho ESM

    Để sử dụng cú pháp import/export hiện đại, hãy thêm "type": "module" vào file package.json của bạn:

    {
      "name": "crawlee-seo-auditor",
      "version": "1.0.0",
      "type": "module",
      "scripts": {
        "start": "tsx src/index.ts"
      },
      "dependencies": {
        "crawlee": "^3.12.0",
        "playwright": "^1.49.0"
      },
      "devDependencies": {
        "@types/node": "^20.10.0",
        "tsx": "^4.19.0",
        "typescript": "^5.3.0"
      }
    }

    Nếu bạn đang tự học xây dựng các hệ thống web scraping để phục vụ cho các chiến dịch tối ưu hóa On-page thực chiến, tham gia khóa học SEO miễn phí sẽ giúp bạn hiểu rõ các tiêu chuẩn xếp hạng của Google và cách áp dụng dữ liệu cào được vào bài toán tăng trưởng lưu lượng tìm kiếm hữu cơ.

    Cấu hình Crawler và Kỹ thuật Xử lý dữ liệu

    Crawlee xây dựng kiến trúc dựa trên mô hình định tuyến (Router) và hàng đợi yêu cầu (RequestQueue), giúp việc bóc tách logic xử lý trở nên rõ ràng và dễ bảo trì.

    Phân biệt CheerioCrawler vs PlaywrightCrawler

    Việc lựa chọn đúng lớp Crawler quyết định 80% tốc độ và chi phí tài nguyên của hệ thống:

    • CheerioCrawler: Khởi tạo nhẹ, phân tích cú pháp HTML bằng cú pháp tương tự jQuery. Thích hợp cho audit nhanh các thẻ meta tĩnh, tiêu đề, mã trạng thái HTTP, liên kết gãy (broken links).
    • PlaywrightCrawler: Tải toàn bộ tài nguyên (CSS, JS, hình ảnh, font chữ). Thích hợp để kiểm tra kích thước thực tế của phần tử trên giao diện, tính toán Core Web Vitals, kiểm tra nội dung render bằng client-side JavaScript.

    Quản lý hàng đợi RequestQueue và Router

    RequestQueue đảm bảo cơ chế enqueue URL đệ quy mà không gặp hiện tượng trùng lặp (deduplication) thông qua khóa duy nhất uniqueKey. Dưới đây là ví dụ cấu hình Router phân tách xử lý trang danh mục và trang chi tiết sản phẩm:

    import { createPlaywrightRouter, PlaywrightCrawler } from 'crawlee';
    
    export const router = createPlaywrightRouter();
    
    // Định tuyến mặc định: Thu thập tất cả internal link
    router.addDefaultHandler(async ({ enqueueLinks, log }) => {
        log.info('Đang enqueue các liên kết nội bộ...');
        await enqueueLinks({
            globs: ['https://webnova.com.vn/**'],
            strategy: 'same-domain',
        });
    });
    
    // Xử lý cụ thể cho bài viết blog
    router.addHandler('BLOG_POST', async ({ page, parseWithCheerio, log }) => {
        const title = await page.title();
        log.info(`Đang audit trang blog: ${title}`);
    });

    Cấu hình Proxy và Quản lý Session

    Khi crawl quy mô lớn, việc gửi hàng nghìn request từ một địa chỉ IP tĩnh duy nhất sẽ nhanh chóng kích hoạt cơ chế rate-limiting của máy chủ web. Crawlee hỗ trợ ProxyConfiguration mạnh mẽ cho phép xoay vòng HTTP/SOCKS5 proxy:

    import { ProxyConfiguration } from 'crawlee';
    
    const proxyConfiguration = new ProxyConfiguration({
        proxyUrls: [
            'http://user:password@proxy1.example.com:8000',
            'http://user:password@proxy2.example.com:8000',
        ],
    });

    Kịch bản thực tế: Xây dựng tool audit Technical SEO tự động

    Dưới đây là một kịch bản kỹ thuật hoàn chỉnh: Xây dựng một công cụ quét toàn diện On-page Technical SEO cho một tên miền bất kỳ. Công cụ sẽ bóc tách trạng thái HTTP, thời gian phản hồi (response time), Canonical link, Title, Meta Description, thẻ Robots, số lượng thẻ H1/H2, cũng như toàn bộ liên kết nội bộ (Internal Links) và liên kết ngoại (Outbound Links).

    Thiết lập cấu trúc chương trình SEO Audit

    Tạo file src/seo_auditor.ts và hiện thực logic quét toàn bộ website với PlaywrightCrawler:

    import { PlaywrightCrawler, Dataset, LogLevel, log } from 'crawlee';
    
    // Cấu hình log mức độ INFO
    log.setLevel(LogLevel.INFO);
    
    // Định nghĩa cấu trúc dữ liệu bản ghi audit
    interface SeoAuditRecord {
        url: string;
        statusCode: number | null;
        responseTimeMs: number;
        title: string;
        titleLength: number;
        metaDescription: string;
        metaDescriptionLength: number;
        canonicalUrl: string | null;
        robotsMeta: string | null;
        h1Count: number;
        h1Content: string[];
        h2Count: number;
        internalLinksCount: number;
        externalLinksCount: number;
        timestamp: string;
    }
    
    const targetDomain = 'https://webnova.com.vn';
    
    const crawler = new PlaywrightCrawler({
        maxRequestsPerCrawl: 100, // Giới hạn số trang quét thử nghiệm
        maxConcurrency: 5,        // Giới hạn 5 tab song song để kiểm soát RAM
        navigationTimeoutSecs: 30,
    
        async requestHandler({ request, page, response, enqueueLinks }) {
            const startTime = Date.now();
            log.info(`Đang phân tích: ${request.url}`);
    
            const statusCode = response ? response.status() : null;
    
            // Trích xuất dữ liệu Technical SEO trực tiếp từ DOM sau khi render
            const auditData = await page.evaluate(() => {
                const titleEl = document.querySelector('title');
                const title = titleEl ? titleEl.innerText.trim() : '';
    
                const descEl = document.querySelector('meta[name="description"]');
                const metaDescription = descEl ? (descEl.getAttribute('content') || '').trim() : '';
    
                const canonicalEl = document.querySelector('link[rel="canonical"]');
                const canonicalUrl = canonicalEl ? canonicalEl.getAttribute('href') : null;
    
                const robotsEl = document.querySelector('meta[name="robots"]');
                const robotsMeta = robotsEl ? robotsEl.getAttribute('content') : null;
    
                const h1List = Array.from(document.querySelectorAll('h1')).map(el => el.innerText.trim());
                const h2List = Array.from(document.querySelectorAll('h2'));
    
                // Phân loại link nội bộ và link ngoại sàn
                const allAnchors = Array.from(document.querySelectorAll('a[href]'));
                let internalLinks = 0;
                let externalLinks = 0;
                const currentHost = window.location.hostname;
    
                allAnchors.forEach(a => {
                    const href = a.getAttribute('href');
                    if (!href || href.startsWith('#') || href.startsWith('javascript:')) return;
                    
                    try {
                        const parsed = new URL(href, window.location.origin);
                        if (parsed.hostname === currentHost) {
                            internalLinks++;
                        } else {
                            externalLinks++;
                        }
                    } catch {
                        // Bỏ qua các URL không hợp lệ
                    }
                });
    
                return {
                    title,
                    metaDescription,
                    canonicalUrl,
                    robotsMeta,
                    h1List,
                    h2Count: h2List.length,
                    internalLinks,
                    externalLinks
                };
            });
    
            const responseTimeMs = Date.now() - startTime;
    
            const record: SeoAuditRecord = {
                url: request.url,
                statusCode,
                responseTimeMs,
                title: auditData.title,
                titleLength: auditData.title.length,
                metaDescription: auditData.metaDescription,
                metaDescriptionLength: auditData.metaDescription.length,
                canonicalUrl: auditData.canonicalUrl,
                robotsMeta: auditData.robotsMeta,
                h1Count: auditData.h1List.length,
                h1Content: auditData.h1List,
                h2Count: auditData.h2Count,
                internalLinksCount: auditData.internalLinks,
                externalLinksCount: auditData.externalLinks,
                timestamp: new Date().toISOString()
            };
    
            // Lưu bản ghi vào Dataset của Crawlee
            await Dataset.pushData(record);
    
            // Tiếp tục enqueue các liên kết cùng domain
            await enqueueLinks({
                strategy: 'same-domain',
                transformRequestFunction(req) {
                    // Loại trừ các tệp tĩnh không cần thiết để tối ưu băng thông
                    if (req.url.match(/\.(png|jpg|jpeg|gif|svg|pdf|zip|css|js|woff|woff2)$/i)) {
                        return false;
                    }
                    return req;
                }
            });
        },
    
        failedRequestHandler({ request, log, error }) {
            log.error(`Yêu cầu thất bại đối với ${request.url}: ${error.message}`);
        },
    });
    
    // Chạy crawler
    await crawler.run([targetDomain]);
    
    log.info('Hoàn tất audit. Dữ liệu đã được lưu trữ trong thư mục storage/datasets/default!');

    Xuất dữ liệu báo cáo dạng CSV và JSON

    Một ưu điểm vượt trội của Crawlee là cơ chế lưu trữ tự động trong storage/datasets/default/. Bạn có thể dễ dàng chuyển đổi toàn bộ dataset thành file CSV hoặc JSON chỉ với một dòng lệnh bằng API của Dataset:

    import { Dataset } from 'crawlee';
    
    // Xuất toàn bộ dữ liệu ra tệp CSV phục vụ Google Sheets / Excel
    await Dataset.exportToCSV('seo_audit_report');
    
    // Hoặc xuất ra tệp JSON chuẩn hóa
    await Dataset.exportToJSON('seo_audit_report');

    Đối với các bạn làm nội dung và tối ưu SEO đang muốn nâng cao năng lực tự động hóa phân tích website, việc tham gia khóa học SEO miễn phí sẽ là bước đệm lý tưởng giúp kết hợp nhuần nhuyễn giữa tư duy Technical SEO chuyên sâu và các công cụ lập trình hiện đại.

    Khắc phục lỗi và Tối ưu hóa hiệu suất cào dữ liệu

    Khi triển khai cào dữ liệu cho các website có hàng trăm ngàn URL, hệ thống của bạn sẽ đối mặt với các vấn đề kỹ thuật hóc búa. Dưới đây là phương pháp chuẩn hóa để khắc phục triệt để từng vấn đề.

    Xử lý Rate Limiting và Tránh Bị Block IP

    Khi máy chủ web phản hồi mã lỗi 429 Too Many Requests hoặc 403 Forbidden, crawler cần tự động điều chỉnh tốc độ thay vì tiếp tục gửi dồn dập request:

    • Adaptive Rate Limiting: Thiết lập maxRequestsPerMinute trong crawler option để giới hạn số lượng request tối đa trên mỗi phút.
    • Backoff Exponential: Khi bắt gặp mã lỗi 429, Crawlee tự động tăng thời gian chờ trước khi retry request đó vào cuối hàng đợi.
    • Luân chuyển Session: Sử dụng sessionPoolOptions: { maxPoolSize: 50 } để phân tán các request qua nhiều phiên duyệt riêng biệt.

    Quản lý Bộ nhớ, CPU và Khắc phục Hiện tượng Memory Leak

    Headless browsers là ứng dụng tiêu tốn bộ nhớ khủng khiếp. Nếu một instance Chromium chạy liên tục qua hàng nghìn trang, hiện tượng memory leak bên trong trình duyệt là không thể tránh khỏi. Để giải quyết triệt để:

    • Cấu hình maxRequestsPerCrawl hợp lý hoặc cấu hình tự động restart browser instance sau mỗi 50-100 trang bằng tùy chọn maxRequestsPerCrawl: 100 trong pool.
    • Chặn các tài nguyên không phục vụ audit SEO như hình ảnh, web fonts và stylesheet nặng thông qua route interceptor của Playwright:
      await page.route('**/*', (route) => {
          const resourceType = route.request().resourceType();
          if (['image', 'media', 'font', 'stylesheet'].includes(resourceType)) {
              return route.abort();
          }
          return route.continue();
      });

      Việc chặn hình ảnh và font chữ có thể giúp tăng tốc độ crawl lên từ 300% đến 500% đồng thời giảm 70% mức tiêu thụ RAM.

    Vượt qua thách thức JavaScript Hydration và SPA

    Các website hiện đại sử dụng Next.js hoặc Gatsby thường áp dụng kỹ thuật Hydration: Mã HTML ban đầu trả về có thể chứa thông tin khác hoặc chưa tải hết so với trạng thái sau khi file JavaScript client-side chạy xong. Để đảm bảo dữ liệu audit chính xác 100%:

    • Sử dụng page.waitForLoadState('networkidle') hoặc chờ đợi một selector cụ thể xuất hiện trên DOM trước khi tiến hành bóc tách:
      await page.waitForSelector('main', { timeout: 10000 });
    • Đối với các trang cuộn vô tận (Infinite Scroll), tích hợp hàm giả lập cuộn trang để kích hoạt nạp toàn bộ lazy-loaded links trước khi phân tích DOM.

    Tổng kết

    Crawlee đã định hình lại tiêu chuẩn cho việc thu thập dữ liệu web trên nền tảng Node.js. Nhờ sự kết hợp hài hòa giữa cơ chế quản lý tài nguyên tự động, khả năng tích hợp linh hoạt cả Cheerio lẫn Playwright, cùng hệ thống phòng vệ chống bot tinh vi, Crawlee là vũ khí mạnh mẽ dành cho bất kỳ kỹ sư phần mềm hay chuyên gia Technical SEO nào muốn làm chủ dữ liệu website ở quy mô lớn.

    Thẻ: Web Nova
    Chia sẻ: in
    Về tác giả

    Đội ngũ Web Nova chia sẻ kiến thức về website, công nghệ, AI và giải pháp tăng trưởng số cho doanh nghiệp.

    Để lại một bình luận

    Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *