Người ta thường nói: Thương trường là chiến trường, nên có lẽ rằng việc lấy dữ liệu của các đối thủ trong kinh doanh không còn xa lạ ngày nay.
Ở bài viết này mình sẽ chẳng bàn tới việc đúng sai khi "cào" dữ liệu của các website, mà nó đã sinh ra rồi, thì mình sẽ bàn tới việc tối ưu và chạy các scraper sao cho tiện lợi và dễ scale các scraper web nhất.
1.Chọn nền tảng.
Viết scraping là 1 công việc kịch bản và tuần tự (Vào web -> tìm các trang -> lấy dữ liệu) vì vậy option tốt nhất là các ngôn ngữ dạng kịch bản. Mà phổ biến nhất có lẽ là javascrip. Vậy mình sẽ viết các scraper bằng js.
Việc lưu trữ các dữ liệu là rất cần thíết. Mình ví dụ như, bạn cần biết khi nào thì các sản phẩm của đối thủ thay đổi giá chẳng hạn. Việc này cần phải lưu lại giá trước khi thay đổi mới có thể so sánh được. Về công nghệ thì bạn có thể chọn bất kì cơ sở dữ liệu nào nhanh nhất có thể. Thôi thì tiện nhất nhanh nhất và phù hợp nhất với js có lẽ là mongodb.
Việc thông báo cũng rất quan trọng. Có nhiều cách để làm việc này. Bạn có thể báo qua email, hoặc các kênh thông tin trong nhóm làm việc của bạn. Việc này implement rất đơn giản vì các nền tảng đều có api để sử dụng hết rồi :3.
2.Các công nghệ để scraper.
2.1 Công cụ làm việc với DOM Elements
Sau khi có nền tảng rồi thì mình bắt đầu viết code trên đó. Hmm xem js thì có các thư viện scraper nào nhỉ.
-
- Puppeteer
-
- Request-Promise
-
- Cheerio
-
- NightmareJs
Nhiều thật đấy phải không nào! Thế nhưng hãy xem xét 1 bài toán tối ưu nha. Mình sẽ mổ xẻ như sau.
- Tốc độ nhanh.
- cào dữ liệu web thì nhanh nhất là cho chạy bất đồng bộ rồi phải không nào. Giả sử như trang web có 32 trang, mốn nhanh nhất thì chỉ có chạy cùng lúc 32 trang, nếu cào hết trang này sang trang khác sẽ không tối ưu được xử lý cpu.
- Dễ code.
- Vì mình cào frontEnd trên web, nên code càng giống frontEnd càng dễ code.
- Tránh dos server.
- Khi cào web thì vấn đề bắt gặp là quản trị server thấy bạn request quá nhiều có thể cho ip của máy bạn vào danh sách đen, do đó có thể sau này sẽ không thể cào được bằng ip này nữa. Do đó mình sẽ cần hạn chế nhất request lên server để tránh dos server của đối thủ (coder có tâm :3 )
Lòng vòng quanh Hà Nội đủ rồi, mình sẽ chọn cheerio ở thời điểm hiện tại Cheerio Api
Mình sẽ note 1 số điểm mạnh cheerio như sau:
- Cú pháp quen thuộc do dùng jquery core ( quá quen thuộc với frontend phải không nào :> )
- Sử dụng DOM hiệu quả
- Cú pháp linh hoạt
- Chạy bất đồng bộ. Và chúng ta sẽ sử dụng các api của cheerio để lấy dữ liệu.
2.2 công cụ request.
- axios
- fetch
- etc
Vì axios chạy bất đồng bộ và mạnh mẽ nên mọi người nên xem xét option này.
3.Biết người biết ta trăm trận không thua :3.
Ở task scraping này thì bạn biết càng nhiều về website đối thủ thì càng dễ lấy dữ liệu và dễ tối ưu cũng như rút ngắn được code.
Mình sẽ nêu 1 số điểm cần chú ý khi "ghé thăm" website đối thủ như sau.
- Phân trang.
- Biết được phong cách phân trang của website sẽ dễ dàng lấy toàn bộ dữ liệu mà code không bị lặp. Tránh trường hợp mỗi trang phải viết 1 đoạn code khác nhau :).
- Region.
- Hiện nay 1 số trang web có sử dụng định vị để đưa ra giá sản phẩm dựa theo khu vực. Mình giả sử Hà nội thì có giá 120k còn TP.HCM có giá 100k.
- Giảm giá.
- Bạn phải phân loại trong database để tránh việc thông báo giá sản phẩm về không chính xác. Vì sản phẩm giảm giá chỉ có thời gian, không phải lúc nào cũng giảm giá.
- Danh mục sản phẩm.
- Đừng nhẫm lẫn giữa phân trang và danh mục sản phẩm. Mình chắc chắn sẽ còn rất rất nhiều vấn đề. Nhưng có điều rằng mình chưa va vào nên mình chỉ nêu được các điểm quan trọng.
4.Cần lưu những gì nhỉ?
- Ồ tớ vừa nêu trên là ta cần 1 database để lưu các thông tin vậy chính xác là cần lưu những gì?
Mình sẽ liệt kê các trường cần phải có như sau, và nó sẽ thay đổi tùy vào nhu cầu từng người, từng team :3
collection 1
- Công ty, tập đoàn, web mà bạn scrap
Để dễ tra cứu là có nhưng trang nào mà bạn scraping thì cần lưu nhưng thông tin sau:
-
- Tên cty, thương hiệu
-
- 1 mảng các links, đường dẫn của trang web.
collection 2
Chính là thông tin của sản phẩm bao gồm:
- Tên sp
- giá
- Giá sale
- region( sản phẩm này bán ở đâu)
- change price( Khi cửa hàng này thay đổi giá sản phẩm sẽ được lưu vào đây)
Vậy thôi. Thế mà các bạn biết rồi đấy, 1 cửa hàng hay đúng hơn là trang web có thể lên tới 10k sản phẩm thì bạn biết vì sao chọn nosql mà không chọn sql rồi chứ. nosql nhanh, tiện, dễ dàng.
5."Lộ trình" code scraper
Thật sự mình không muốn đưa code vào bài viết 1 chút nào, vì nó khô khan và khó hiểu, mình viết còn chả muốn đọc ấy chứ. Thế nhưng không có code thì bài viết trở thành 1 abstrack, nó trừu tượng cựu kì ấy, xem mà chả biết tới đâu. Vì thế tớ sẽ đưa ra 1 cái quá trình chung mà gọi là lộ trình những công việc cũng được cho dễ hình tượng.
1. Lấy tất các các links mà nó chứa dữ liệu bạn cần (Sản phẩm)
Ban đầu bạn chỉ có 1 đường dẫn là trang chủ. Công việc là làm sao để bóc tách DOM trang này để lấy tất cả các link như các pages, các danh mục được phân trang.
- Công việc này dễ thôi, trước tiên bạn request lên trang web và lấy data để thu về DOM. Sau đó dùng cheerios để bóc tách DOM lấy các trang và danh mục. Mình có đoạn code như sau để tham khảo:
const firstLink = "https://Vi_du_scraper_web_nay"
async function getAllPage(firstLink) {
// request
const response = await axios.get(firstLink)
// get data
const textData = response.data
// get DOM
const $ = cheerio.load(textData)
let linksPages = []
// get all ul > (li) of pageNum
$('.page-numbers')
.find('> li')
.each((_, elm) => {
linksPages.push($(elm))
})
/**
* in dom, after 72 page then elm "next-page",
* so, need get <li> before elm "next-page"
* so **length -2**
*/
// lấy ra tổng số các page có trong links( đối số truyền vào)
const endPage = $(linksPages[linksPages.length - 2], '> a').text()
const endPageRange = _.range(endPage)
return _.map(endPageRange, offset => {
return firstLink + '/page/' + offset'
})
}
Đoạn code trên sẽ lấy được toàn bộ links chứa sản phẩm. Mình ví dụ thôi, đừng quan tâm tới code cho mệt :)), lấy được ý tưởng là được rồi.
2.Lấy data(sản phẩm) dựa vào links
code thì mỗi người 1 khác. Vậy lên mình sẽ nêu ra lưu ý tránh việc code mà không bao hết các trường hợp dẫn đến sai.
- Như lúc đầu, phần này liện quan tới dữ liệu lên bạn phải để ý xem web có phân loại sản phẩm theo danh mục không. nếu có thì phải request kèm theo cookie.
- Mẹo như sau: Bạn vào trình duyệt và chọn khu vực như bình thường sau đó f12 để lấy ra cookie rồi gán vào request. Ví dụ đoạn gán region vào request như sau:
async function getProducts(crawlerLinks, Region) {
const response = await axios.get(crawlerLinks, {
headers: {
Cookie: `web_region=${encodeURIComponent(`${Sản Phẩm Đà Nẵng}`)}`,
},
})
const $ = cheerio.load(response.data)
/* Hàm getProductsByElms là đoạn code mà bạn sẽ cào dữ liệu. Mỗi trang web cấu trúc khác nhau nên code trong hàm này cũng khác nhau
*/
const products = getProductsByElms($, Region)
return products
}
- Khi lưu các đối tượng vào biết như giá sản phẩm, tên sản phẩm thì nên cắt khoảng trắng trước khi lưu.
- Với giá thì nên cắt cả VND đằng sau mục đích để dễ so sánh sau này
3.Viết code sao cho tránh Dos server của Người ta :)).
- À thì ý tưởng mình đã nói bên trên. Dưới này, mình sẽ thực thi nha.
- Rà soát 1 lát thì các bn cũng thấy rằng mình viết bất đồng bộ để cào nhanh nhất có thể, đồng nghĩa với việc request liên tục lên server đối phương. Chính vì thế xảy ra DOS server. Cách giải quyết là xử lý DOM vẫn cho chạy bình thường nhưng xử lý request gửi lên thì sẽ cho đợi 1 khoảng thời gian ngắn như 500ms hoặc hẳn 1000ms để tránh server xử lý quá nhiều request 1 lúc.
- Tiếp theo là phải viết trong hàm async vậy lên mình có 1 đoạn trên stackoverflow như sau để có thể tham khảo:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
Đưa hàm sleep trên mỗi khi chạy xong 1 request để có thể tránh bị block ip nha các bạn:)).
4.Bot tự động báo cáo khi có thay đổi trên web bị scraping.
- Tự động là việc quan trọng trong task này. Mình có option dùng "scheduler for Node" là thư viện xếp lịch trình node-schedule
- mỗi khi task scraper này được chạy phát hiện ra có thay đổi sẽ được thông báo với kênh mà bạn setup( mail, facebook, chat…)
Vậy thôi, thật ra còn rất rất nhiều vấn đề, nhưng trên là những vấn đề chủ chốt trong web scraping bằng phương pháp của mình.
Chúc mọi người vui vẻ.
