Web Scraping chắc hẳn với một số anh em develop cũng không còn lạ lẫm gì. Bài viết hôm nay, tôi viết hường dẫn xây dựng một ứng dụng Web Scraping với Nuxtjs bằng cách sử dụng thư viện Puppeteer của Google.
Nó được xây dựng với mục đích mô tả cách thiết lập và cáu hình Puppeteer để nó hoạt động với Nuxtjs và sử dụng nó như thế nào để Scrape dữ liệu từ một website và hiển thị nó trên website của bạn.
Vì Puppeteer là một thư viện Node dạng Server-side nên rất khó để thiết lập nó hoạt động với thư viện là một Client-side như Vue.js và không có nhiều tài liệu hướng dẫn về nó. Vậy nên bài viết này chúng ta có thể giải quyết vấn đề này trong dự án bằng Nuxt.js và Puppeteer.
Kiến thức cơ bản
Web Scraping
Web Scraping nghe có vẻ lạ lẫm nhưng thực tế nó lại là một thuật ngữ rất đơn giản và dễ hiểu. Nó là một công nghệ mô tả lại quá trình trích xuất dữ liệu từ các website và lưu chúng lại dưới định dạng bất kì để phục vụ cho nhiều mục đích khác nhau.
Web Scraping sẽ tự động hoá quy trình trích xuất thông tin từ các trang web và lưu trữ thông tin này dưới dạng text, json, v.v. để xử lý thêm.
Một vài lợi ích của việc sử dụng Web Scraping:
- Web Scraping có thể sử dụng để trích xuất chi tiết sản phẩm của các trang web thương mại điện tử như giá cả, tên sản phẩm, hình ảnh, v.v.
- Việc tìm kiếm trên web rất hữu ích trong nghiên cứu vì nó có thể giúp thu thập dữ liệu có cấu trúc từ nhiều trang web.
- Việc thu thập dữ liệu từ các nguồn khác nhau để phân tích có thể được tự động hóa với Web Scraping một cách dễ dàng.
- Nó có thể được sử dụng để thu thập dữ liệu cho việc testing hoặc đào tạo cho tập dữ liệu machine learning.
Puppeteer
Puppeteer là một thư viện Node được sử dụng để cào dữ liệu từ các trang web, tự động hoá việc điền các biểu mẫu, v.v.
Nó là một thư viện Node chính thực được phát hành bới Google để kiểm soát các phiên bản trên Google Chrome, có thể được cấu hình ở chế độ chạy nền.
Puppeteer được sử dụng trong một số trường hợp:
- Trang web cào dữ liệu.
- Theo dõi hiệu suất load trang.
- Tự động hoá việc điền biểu mẫu.
- Tạo ảnh chụp màn hình, PDF.
- Rất hữu ích cho việc Automated Testing.
- Thực hiện tự động hoá nhiều thao tác trên trình duyệt.
Xây dựng một ứng JobScrapper
Tạo project Nuxtjs
Trước khi bắt đầu phát triển ứng dụng Web Scraping, chúng ta cần cài đặt và thiết lập Nuxtjs. Có thể tham khảo tại https://nuxtjs.org/docs/2.x/get-started/installation.
Install Nuxtjs
1 2 | yarn create nuxt-app job-scrapper |
Sau khii cài đặt, chúng ta bắt đầu tạo các components, stores khác nhau và các trang chúng ta sẽ cần scrape trong dự án này.
Tiếp theo chúng ta cần tạo components Jobs để hiển thị danh sách các công việc được tạo, một job store để quản lý các job state, một trang điều hướng như sau:
Cài đặt tất cả các dependencies cần thiết để cào trang với nuxtjs và puppeteer
1 2 | npm i puppeteer net tls |
Configuring Puppeteer
Đây là phần khó khăn, chúng ta có thể gặp một vài vấn đề khác nhau khi cấu hình Puppeteer hoạt động với nuxtjs vì nuxtjs là song song giữa Client và Server Side framework.
Rất khó để biết được vị trí đặt Puppeteer hoặc như thế nào để gọi nó từ server-side vì Puppeteer là một thự viện server node và chỉ hoạt động trên server-side của nuxt.js. Chúng ta sẽ cùng thiết lập để nó hoạt động được trên dự án của mình nhé.
Trước tiên, tạo một script.js
trong thư mục gốc với đoạn code sau:
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> saveFile <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>writeFileSync <span class="token keyword">const</span> pkgJsonPath <span class="token operator">=</span> require<span class="token punctuation">.</span>main<span class="token punctuation">.</span>paths<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">'/puppeteer'</span> <span class="token operator">+</span> <span class="token string">'/package.json'</span> <span class="token keyword">const</span> json <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span>pkgJsonPath<span class="token punctuation">)</span> <span class="token comment">// eslint-disable-next-line no-prototype-builtins </span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>json<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'browser'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> json<span class="token punctuation">.</span>browser <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">delete</span> json<span class="token punctuation">.</span>browser<span class="token punctuation">.</span>ws <span class="token function">saveFile</span><span class="token punctuation">(</span>pkgJsonPath<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>json<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span> |
Nhìn vào đây chúng ta có thể hiểu được nó làm gì. Nó sẽ thực hiện kiểm tra trong package.json
đã tồn tại đối tượng browser không và thực hiện xoá thuộc tính ws
của browser
.
Nó sẽ cần thực hiện mỗi khí chạy npm install
.
Vậy nên chúng ta cần add script này vào package.json và nó sẽ được thực thi như một postinstall
script.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token string">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"dev"</span><span class="token operator">:</span> <span class="token string">"nuxt"</span><span class="token punctuation">,</span> <span class="token string">"build"</span><span class="token operator">:</span> <span class="token string">"nuxt build"</span><span class="token punctuation">,</span> <span class="token string">"start"</span><span class="token operator">:</span> <span class="token string">"nuxt start"</span><span class="token punctuation">,</span> <span class="token string">"export"</span><span class="token operator">:</span> <span class="token string">"nuxt export"</span><span class="token punctuation">,</span> <span class="token string">"serve"</span><span class="token operator">:</span> <span class="token string">"nuxt serve"</span><span class="token punctuation">,</span> <span class="token string">"lint:js"</span><span class="token operator">:</span> <span class="token string">"eslint --ext .js,.vue --ignore-path .gitignore ."</span><span class="token punctuation">,</span> <span class="token string">"lint"</span><span class="token operator">:</span> <span class="token string">"yarn lint:js"</span><span class="token punctuation">,</span> <span class="token string">"test"</span><span class="token operator">:</span> <span class="token string">"jest"</span><span class="token punctuation">,</span> <span class="token string">"postinstall"</span><span class="token operator">:</span> <span class="token string">"node script"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> |
chúng ta cần thêm đoạn code sau vào package.json:
1 2 3 4 5 6 7 | <span class="token string">"browser"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"fs"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token string">"path"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token string">"os"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token string">"tls"</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> |
Chúng ta cần thiết lập fs
, path
, os
và tls
thành false vì cần thiết trên server-side.
Tiếp theo, mở nuxt.config.js
và thêm vào đoạn code sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | build<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">extend</span><span class="token punctuation">(</span><span class="token parameter">config<span class="token punctuation">,</span> <span class="token punctuation">{</span> isServer<span class="token punctuation">,</span> isClient <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> config<span class="token punctuation">.</span>externals <span class="token operator">=</span> config<span class="token punctuation">.</span>externals <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isServer<span class="token punctuation">)</span> <span class="token punctuation">{</span> config<span class="token punctuation">.</span>node <span class="token operator">=</span> <span class="token punctuation">{</span> fs<span class="token operator">:</span> <span class="token string">'empty'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>externals<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> config<span class="token punctuation">.</span>externals<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> puppeteer<span class="token operator">:</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> config<span class="token punctuation">.</span>externals<span class="token punctuation">.</span>puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> config<span class="token punctuation">.</span>output<span class="token punctuation">.</span>globalObject <span class="token operator">=</span> <span class="token string">'this'</span> <span class="token keyword">return</span> config <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> |
Cấu hình này chỉ bắt buộc puppeteer và thêm nó mảng khi Nuxt.js đặt tại Client-side và thiết lập fs
rỗng.
Web Scrapping
Tạo một file JobScrapper.js
với đoạn code sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span> <span class="token keyword">const</span> jobUrl <span class="token operator">=</span> <span class="token comment">// SITE URL HERE let page let browser </span> <span class="token keyword">let</span> cardArr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token keyword">class</span> <span class="token class-name">Jobs</span> <span class="token punctuation">{</span> <span class="token comment">// We will add 3 methods here </span> <span class="token comment">// Initializes and create puppeteer instance </span> <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// Visits the page, retrieves the job </span> <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">resolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// Converts the job to array </span> <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">getJobs</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> Jobs |
Tạo Init method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token comment">// headless: false, </span> args<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'--no-sandbox'</span><span class="token punctuation">,</span> <span class="token string">'--disable-setuid-sandbox'</span><span class="token punctuation">,</span> <span class="token string">'--disable-dev-shm-usage'</span><span class="token punctuation">,</span> <span class="token string">'--disable-accelerated-2d-canvas'</span><span class="token punctuation">,</span> <span class="token string">'--no-first-run'</span><span class="token punctuation">,</span> <span class="token string">'--no-zygote'</span><span class="token punctuation">,</span> <span class="token string">'--single-process'</span><span class="token punctuation">,</span> <span class="token comment">// <- this one doesn't works in Window </span> <span class="token string">'--disable-gpu'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">await</span> Promise<span class="token punctuation">.</span><span class="token function">race</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span>jobUrl<span class="token punctuation">,</span> <span class="token punctuation">{</span> waitUntil<span class="token operator">:</span> <span class="token string">'networkidle2'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">waitForSelector</span><span class="token punctuation">(</span><span class="token string">'.search-card'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Function init
khởi tạo puppeteer với một số cấu hình, tạo một trang mới với browser.newPage()
và ghé URL với await page.goto
, và chờ cho trang load thành công với await page.waitForSelector
.
Tạo một Resolver method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <span class="token comment">// Visits the page, retrieves the job</span> <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">resolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> jobURLs <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">evaluate</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> cards <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.search-card'</span><span class="token punctuation">)</span> cardArr <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>cards<span class="token punctuation">)</span> <span class="token keyword">const</span> cardLinks <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> cardArr<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">card</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> cardTitle <span class="token operator">=</span> card<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.card-title-link'</span><span class="token punctuation">)</span> <span class="token keyword">const</span> cardDesc <span class="token operator">=</span> card<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.card-description'</span><span class="token punctuation">)</span> <span class="token keyword">const</span> cardCompany <span class="token operator">=</span> card<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'a[data-cy="search-result-company-name"]'</span><span class="token punctuation">)</span> <span class="token keyword">const</span> cardDate <span class="token operator">=</span> card<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.posted-date'</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> text <span class="token punctuation">}</span> <span class="token operator">=</span> cardTitle <span class="token keyword">const</span> <span class="token punctuation">{</span> host <span class="token punctuation">}</span> <span class="token operator">=</span> cardTitle <span class="token keyword">const</span> <span class="token punctuation">{</span> protocol <span class="token punctuation">}</span> <span class="token operator">=</span> cardTitle <span class="token keyword">const</span> pathName <span class="token operator">=</span> cardTitle<span class="token punctuation">.</span>pathname <span class="token keyword">const</span> query <span class="token operator">=</span> cardTitle<span class="token punctuation">.</span>search <span class="token keyword">const</span> titleURL <span class="token operator">=</span> protocol <span class="token operator">+</span> <span class="token string">'//'</span> <span class="token operator">+</span> host <span class="token operator">+</span> pathName <span class="token operator">+</span> query <span class="token keyword">const</span> company <span class="token operator">=</span> cardCompany<span class="token punctuation">.</span>textContent cardLinks<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> titleText<span class="token operator">:</span> text<span class="token punctuation">,</span> titleURLHost<span class="token operator">:</span> host<span class="token punctuation">,</span> titleURLPathname<span class="token operator">:</span> pathName<span class="token punctuation">,</span> titleURLSearchQuery<span class="token operator">:</span> query<span class="token punctuation">,</span> titleURL<span class="token operator">:</span> titleURL<span class="token punctuation">,</span> titleDesc<span class="token operator">:</span> cardDesc<span class="token punctuation">.</span>innerHTML<span class="token punctuation">,</span> titleCompany<span class="token operator">:</span> company<span class="token punctuation">,</span> titleDate<span class="token operator">:</span> cardDate<span class="token punctuation">.</span>textContent<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">return</span> cardLinks <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">return</span> jobURLs <span class="token punctuation">}</span> |
Đầu tiên, nó chọn tất cả Jobs đã được liệt kê và chuyển đổi nó thành mảng và lặp lại từng công việc trong khi lấy dữ liệu cần thiết.
Tạo getJobs method
1 2 3 4 5 6 7 8 9 | <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">getJobs</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> jobs <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> data<span class="token punctuation">.</span>jobs <span class="token operator">=</span> jobs data<span class="token punctuation">.</span>total_jobs <span class="token operator">=</span> jobs<span class="token punctuation">.</span>length <span class="token keyword">return</span> data <span class="token punctuation">}</span> |
Phương thức này chỉ trả veer mảng Jobs từ phương thức resolver
và đóng trình duyệt.
Tạo Vuex action
Tiếp theo, chúng ta sẽ thiết lập Vuex store để truy xuất dữ liệu jobs mỗi khi chúng ta gửi hành động getJobs
và lưu trữ chúng ở state.
Mở store/job.js
và thêm đoạn code sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">import</span> JobScrapper <span class="token keyword">from</span> <span class="token string">'~/JobScrapper'</span> <span class="token comment">// Action </span> <span class="token keyword">async</span> <span class="token function">getJobs</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> commit <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> JobScrapper<span class="token punctuation">.</span><span class="token function">getJobs</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>total_jobs<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'STORE_JOBS'</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span> <span class="token keyword">return</span> data<span class="token punctuation">.</span>jobs <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Mutation </span> <span class="token constant">STORE_JOBS</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span> payload</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> state<span class="token punctuation">.</span>jobs <span class="token operator">=</span> payload<span class="token punctuation">.</span>jobs state<span class="token punctuation">.</span>total_jobs <span class="token operator">=</span> payload<span class="token punctuation">.</span>total_jobs <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">// Getter </span> <span class="token keyword">export</span> <span class="token keyword">const</span> getters <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function-variable function">getJobs</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>jobs <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token comment">// State </span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">state</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> jobs<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> total_jobs<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Hiển thị Jobs
Mở pages/job.vue
và thêm vào:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row mt-5"</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"card-group"</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row"</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-md-8"</span><span class="token operator">></span> <span class="token operator"><</span>Job v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"(job, i) in jobs"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"i"</span> <span class="token operator">:</span>job<span class="token operator">=</span><span class="token string">"job"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token keyword">async</span> <span class="token function">asyncData</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> store <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> getJobs <span class="token operator">=</span> store<span class="token punctuation">.</span>getters<span class="token punctuation">[</span><span class="token string">'job/getJobs'</span><span class="token punctuation">]</span> <span class="token keyword">let</span> jobs <span class="token operator">=</span> <span class="token function">getJobs</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>jobs<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span> jobs <span class="token operator">=</span> <span class="token keyword">await</span> store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'job/getJobs'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> jobs <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
Đâu là một cách bạn có thể gửi các actions trong mỗi trang bạn muốn, nhưng nó sẽ phải nằm trong asyncData()
hook vì nó được gọi từ Server-side.
Một cách khác hoặc cách tốt nhất là gửi action bên trong nuxtServerInit
vì nó sẽ được thực thi mỗi khi tải trang mới.
Tạo index.js
bên trong thư mục store với đoạn code sau:
1 2 3 4 5 6 | <span class="token keyword">async</span> <span class="token function">nuxtServerInit</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> dispatch <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'job/getJobs'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> |
Tạo tác này sẽ quét jobs và lưu trữ chúng vào state, chúng ta có thể sử dụng ...mapState
hoặc ...mapGetters
để truy xuất dữ liệu và hiển thị nó trên components của mình.
Dự án này, chúng ta sẽ sử dụng nuxtServerInit
và ...mapState
trong bất kì components vào chúng ta muốn hiển thị dữ liệu job.
Jobs Component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>section<span class="token operator">></span> <span class="token operator">...</span><span class="token operator">...</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row mb-1 mt-5"</span> v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"jobs.length !== 0"</span><span class="token operator">></span> <span class="token operator"><</span>div v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"job in jobs"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"job.id"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-md-6 col-sm-12 mb-4"</span> <span class="token operator">></span> <span class="token comment">// My JOB component to display a specific job </span> <span class="token operator"><</span>Job <span class="token operator">:</span>job<span class="token operator">=</span><span class="token string">"job"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span>div v<span class="token operator">-</span><span class="token keyword">else</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row mb-1 mt-5"</span><span class="token operator">></span>No Jobs at <span class="token keyword">this</span> time<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator">...</span><span class="token operator">...</span><span class="token operator">...</span><span class="token punctuation">.</span> <span class="token operator"><</span><span class="token operator">/</span>section<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">{</span> mapState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> computed<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span><span class="token function">mapState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token function-variable function">jobs</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token operator">...</span>state<span class="token punctuation">.</span>job<span class="token punctuation">.</span>jobs<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> ' <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> <span class="token operator"><</span>style<span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>style<span class="token operator">></span> |
Cuối cùng chúng ta có thể chạy ứng dụng Web Scrapper để xem thành quả hoạt động ra sao nhé.
1 2 3 | $ yarn build $ yarn start |
Kết luận
Hy vọng sau bài viết này chúng ta có thể làm được những điều xa hơn nữa để hiểu sâu hơn về cách xử lý Web Scrapping bằng Puppeteer trong Nuxtjs. Chúng ta nên xây dựng một JobScrapper hoàn chỉnh với những áp dụng thực tế và hữu ích như: cào dữ liệu ngân hàng để phân tích và cập nhật thông tin tài chính, hay lấy dữ liệu tiền tệ trên các website hiện nay. v.v.
Phương thức Web Scraping với Nuxtjs sử dụng Puppeteer có rất nhiều cách giải quyết và có thể hơi khó với một số người mới bắt đầu. Có thể sẽ có nhiều hướng giải quyết khác nhau hy vọng sẽ có nhiều đóng góp cho những ý tưởng tốt hơn.
Chúc các bạn thành công!