- vừa được xem lúc

Dockerize project crawl dữ liệu với Puppeteer

0 0 6

Người đăng: Van Viet DANG

Theo Viblo Asia

Xin chào các bạn, mình là Việt. Sau một thời gian sử dụng Viblo như một công cụ hỗ trợ đọc và tìm hiểu các Tech mới từ các anh chị, các bạn và cũng có cả các em nữa thì hôm nay đây sẽ là bài viết đầu tiên của mình trên diễn đàn này.

Giới thiệu project

Bài viết này sẽ về chủ đề Docker và Dockerize một ứng dụng crawl dữ liệu đơn giản sử dụng thư viện Puppeteer. Project chỉ đơn giản là có 1 endpoint API giúp lấy ld-json schema của 1 trang web dựa vào url được truyền vào làm tham số của API này.

Về ld-json schema thì các bạn có thể đọc ở đây.

Vì mới làm quen với chủ đề này nên việc triển khai code sẽ có nhiều thiếu sót nên nếu có vấn đề gì thì mong được mọi người góp ý để mình có thể cải thiện cho bài viết nha.

Okay, bây giờ chúng ta sẽ xem cấu trúc của project có gì?

Cấu trúc project

  • Đầu tiên chúng ta có file browser.js sẽ là nơi định nghĩa browser puppeteer bao gồm các giá trị config. Ở đây mình set một số giá trị đơn giản như no-sandboxproxy-server nếu có:

    async function startBrowser(proxyInfo) { let browser; let args = ["--no-sandbox"]; if (proxyInfo && proxyInfo.proxyIp && proxyInfo.proxyPort) { args.push(`--proxy-server=${proxyIp}:${proxyPort}`); } try { console.log("Opening the browser......"); browser = await puppeteer.launch({ headless: true, args, 'ignoreHTTPSErrors': true }); } catch (err) { console.log("Could not create a browser instance => : ", err); } return browser;
    }
    
  • Tiếp theo chúng ta có 1 helper hỗ trợ crawl các dữ liệu json-schema

    const scraper = async (browser, { url, proxyAuth }) => { let page = await browser.newPage(); if (proxyAuth && proxyAuth.username && proxyAuth.password) { await page.authenticate({ username: proxyAuth.username, password: proxyAuth.password }) } console.log(`Navigating to ${url}...`); await page.goto(url); let schemas = await page.$$eval('script[type="application/ld+json"]', links => { links = links.map(el => JSON.parse(el.textContent)); return links; }); return schemas;
    }
    

    Giải thích qua một chút về hàm scraper phía trên, nó sẽ thực hiện lấy tất cả các thẻ script có type là application/ld+json ( Các thẻ này chứa toàn bộ thông tin schema của trang đó). Sau đó chuyển về dạng array of json object và trả về chính chúng.

  • Tiếp theo là SchemaService.js. Service này sẽ thực hiện khởi tạo browser và thực hiện crawl trang web thông qua url được truyền vào. Ngoài ra nếu có thông tin về proxy thì cũng sẽ được xử lý ngay phía trên.

    const getSchemas = async ({ url, proxyData }) => { try { let browser; const data = { url } if (proxyData) { const { ip = '', port = '', username = '', password = '' } = await proxyData; browser = await browserObject.startBrowser({ proxyIp: ip, proxyPort: port }); if (username && password) { data['proxyAuth'] = { username, password } } } else { browser = await browserObject.startBrowser(); } const schemas = await scraper(browser, data); await browser.close(); return schemas; } catch (err) { console.log("Error: ", err); }
    }
    
  • Cuối cùng là file app.js. Để xem chúng ta có gì nhé

    require('dotenv').config()
    const express = require('express');
    const schemaService = require('./src/services/schemaService');
    const app = express(); const validateApiKey = (req, res, next) => { const apiKey = req.query.api_key; if (!apiKey) { return res.status(400).json({ error: 'api_key parameter is required' }); } const isValidApiKey = apiKey === process.env.API_KEY; if (isValidApiKey) { next(); } else { return res.status(401).json({ error: 'Invalid API key' }); }
    }; app.get('/extract-schema', validateApiKey, async function (req, res) { const query = req.query const { url, proxy } = query; const schemas = await schemaService.getSchemas({ url, proxyData: proxy }); res.send(schemas);
    }) app.get('/', async function (req, res) { res.send('Hello this is api server that crawl json-ld schema');
    }) const port = process.env.PORT; app.listen(port, () => { console.log("Server Listening on PORT:", port);
    });
    

Trông cũng khá đơn giản phải không nào? Okay vậy chúng ta sẽ tiếp tục đi đến phần tiếp theo là Dockerize ứng dụng này nhé.

Dockerize project

  1. Build image với Node18
    • Cùng nhìn qua Dockerfile của mình xem có những gì nào.
    FROM node:18 # Install Google Chrome Stable and fonts
    RUN apt-get update && apt-get install gnupg wget -y && \ wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && \ apt-get update && \ apt-get install google-chrome-stable -y --no-install-recommends && \ rm -rf /var/lib/apt/lists/* WORKDIR /app COPY . . RUN npm install && \  cd ./node_modules/puppeteer && \ npm install CMD ["npm", "start"]
    
    • Ở đây chúng ta sẽ xuất phát từ node:18, sau đó cài đặt các libraries cần thiết để chạy được Puppeteer, và cuối cùng là chạy npm install để cài các dependencies khác nhé
  2. Docker Compose
    • File docker-compose.yml của mình thì khá đơn giản với chỉ 1 service thôi nên cũng không có gì để giải thích cả 😅. Các bạn chú ý ở đây mình đã dùng 1 biến PORT từ file .env nhé. Các bạn có thể tự định nghĩa giá trị này tùy ý.
    version: "3.7" services: app: build: . image: autocrawl/jsonschema ports: - "${PORT}:${PORT}" restart: unless-stopped
    

Okayyyyy, vậy là tạm xong rồi đấy. Chúng ta bắt đầu thử nghiệm web api của mình nào.

Kết quả

  1. Trước khi chạy dự án, các bạn cần định nghĩa 2 giá trị trong file .env là PORT và API_KEY để hệ thống còn biết mà chạy nhé. Ở đây mình sử dụng cổng 8080 và API_KEY random từ trang này nhé.

  2. Khởi động dự án

    docker compose up -d
    
  3. Cùng test thử bằng cách truy cập vào đường dẫn: http://localhost:8080/extract-schema?url=https://us.bebee.com/job/20231023-4984b73f0394f3a333c589ecbf15da25&&api_key=Xo1ROCSbLBGJUf0vLlJuuhxSEbX0pQ9A2ZrS0o2dIskQvL7A3ztn54kUAjjhnqpr. Lưu ý là port và api_key là do các bạn định nghĩa trong file .env nhé.

  4. Và đây là kết quả của mình. result

  • Các bạn có thể tùy ý thay đổi đường link của website cần crawl nhé.

Kết luận

Okay vậy là cuối cùng chúng ta đã hoàn thành 1 ứng dụng crawl đơn giản, cũng khá nhanh phải không nào?

Nếu các bạn có câu hỏi và góp ý gì thì có thể comment trực tiếp dưới bài viết, chúng ta sẽ cùng nhau thảo luận nhé.

Cảm ơn các bạn đã dành thời gian để đọc bài viết của mình. Cheerse 🥂🥂

Bình luận

Bài viết tương tự

- vừa được xem lúc

Crawl website sử dụng Node.js và Puppeteer - phần 2

trong phần 1 mình đã giới thiệu về puppeteer và tạo được 1 project cùng một số file đầu tiên để các bạn có thể crawl dữ liệu từ một trang web bất kỳ. Bài này mình sẽ tiếp nối bài viết trước để hoàn thiện seri này.

0 0 58

- vừa được xem lúc

Puppeteer - Giải pháp trích xuất thông tin Web trong NodeJS [Phần 1]

Chào các bạn! Chắc hẳn trong quá trình phát triển một tính năng phía Backend, bạn sẽ gặp những trường hợp cần phải truy cập một trang web và trích xuất thông tin từ chúng vì một mục đích nào đó. Nó gọ

0 0 58

- vừa được xem lúc

Puppeteer - Giải pháp trích xuất thông tin Web trong NodeJS [Phần 2]

Chào các bạn ! Tại bài viết trước , mình đã chia sẽ những kiến thức ban đầu về Headless Browsers và Puppeteer cùng một ví dụ về việc khởi tạo Puppeteer cũng như thực hiện chức năng chụp ảnh từ một tra

0 0 51

- vừa được xem lúc

Hướng dẫn cào dữ liệu từ nguồn trên web với Puppeteer

Hôm trước có dịp đi xe bus trong lúc chờ tàu điện Nhổn ga Hà Nội, trên xe mình gặp một ông cụ khoảng 70 tuổi tóc bạc trắng đang cúi gằm mặt vào 1 quyển sổ toàn những con số 2 chữ số rồi các ô đánh dấu

0 0 18

- vừa được xem lúc

How To Set Up a Node.js Application for Production with pm2, nginx

Mở đầu. Bài này mình viết với mục đích để tổng hợp lại kiến thức và bổ sung kĩ thuật liên quan đến server cho bản thân.

0 0 73

- vừa được xem lúc

Deploy Serverless với Express và MongoDB

Trong bài viết này, chúng ta sẽ cùng tìm hiểu quá trình xây dựng một ứng dụng web hướng database. Bài viết phù hợp cho developer nào theo hướng full-stack cho các dự án riêng mà không cần phải lo lắng đến việc cài đặt và bảo trì hạ tầng với nhiều service phức tạp.

0 0 30