Webstorm chỉ hỗ trợ bạn tạo 2 loại project NodeJs: Node.js Express App và Meteor Js App. Nếu bạn muốn tạo 1 project NodeJs tùy ý, không dùng ExpressJs lẫn Meteor thì làm thế nào? Project tạo ra đảm bảo phải debug được Webstorm.
Alexa Viet Nam 500
7.153 viewsAlexa Viet Nam 500 là ứng dụng nho nhỏ xem, so sánh top 500 website tại Việt Nam theo đánh giá của Alexa. Ứng dụng này xây dựng trên Node.js, Firebase, Bootstrap 3.
- Demo: https://alexavn500.firebaseapp.com/
- Source code: http://github.com/vietjs/alexavn500
Chức năng
Danh sách top 500 websites ở Việt Nam theo Alexa.com bao gồm thông tin:
- Tên website
- Địa chỉ website
- Mô tả
- Favicon
- Thời gian tải trang web
- Thứ tự Alexa
- Google Page Rank
- Số thống kê: tỷ lệ bounce rate, thời gian truy cập, số trang trong một lần truy cập, …
Hướng dẫn
- Cài đặt
npm install
- Cấu hình Trong file
config.js
, thayfirebase.siteRef
bằng đường dẫn tới Firebase của bạn. - Cập nhật Alexa top 500 Việt Nam
node alexa.js
- Cập nhật Google Page Range
node pagerange.js
- Cập nhật thống kê chi tiết Alexa
node alexa-detail.js
- Host website trên firebase https://www.firebase.com/docs/hosting/quickstart.html
cd public && firebase deploy
Dùng Node.js phân tích kết quả chương trình Dzựt cô hồn của Tiki.vn
18.617 viewsBài viết này được viết trong tâm trạng nặng nề và ê chề của một tên “cô hồn” chưa đủ trình độ, và dựa trên trí tưởng bở đầy ngông cuồng, Tui không phán xét cũng như đánh giá ai, Tui hoan nghênh mọi đóng góp, chê bai.
Từ 2 năm nay, năm nào cũng vào tháng 7 – tháng “Cô Hồn”, Tiki.vn lại tổ chức sự kiện Dzựt Cô Hồn Online, sự kiện được đông đảo “cô hồn mạng” từ Bắc chí Nam hưởng ứng nhiệt tình, Tui – tác giả bài viết này cũng nằm một trong số đó. Nhưng do chưa đủ độ “cô hồn”, chưa năm nào mà Tui giật được bất cứ thứ gì. Uất ức, căm phẫn, Tui nghĩ ra cái kịch bản là có những điều mờ ám ở đây và quyết tâm tìm ra những “cô hồn” nào là người đã ôm hết giải thưởng của Tui!
Mấy câu Tui thắc mắc như sau?
- Ai là người trúng thưởng?
- Trúng thưởng mấy lần?
- Trúng thưởng đợt nào?
- Trúng thưởng món gì?
- Giá trị trúng thưởng là bao nhiêu?
- Dzựt bao nhiêu lần thì trúng?
- Ai là “cô hồn” nhất? (chiêu trò, kỹ thuật tà đạo)
- Có mối liên quan nào giữa người trúng thưởng và nội bộ Tiki hay không?
Khi trả lời được các câu hỏi trên, chúng ta dễ dàng biết được ai là “cô hồn ăn gian”, ai là “cô hồn nội gián”. Để trả lời được các câu hỏi trên, chúng ta cần thu thập đủ dữ liệu. May thay, Tiki cho chúng ta xem trang kết quả: http://dzut-co-hon.tiki.vn/result#main
I. Thu thập dữ liệu
1. Đồ nghề
- Module request: HTTP client
- Module cheerio: cho phép truy vấn HTML giống như jQuery dành cho Node.js, ưu điểm là nhanh, nhẹ, sử dụng i như jQuery ở client
- Module lodash: là bản port của Underscore.js áp dụng functional programming cho Node.js
- Module async: quản lý mớ callback hỗn loạn cho Node.js
- Node.js
2. Chiến thuật
- Mở trang kết quả Dzựt Cô Hồn Online http://dzut-co-hon.tiki.vn/result#main
- Lấy danh sách kết quả và lưu lại
- Mở trang tiếp theo
- Lặp lại 2 và 3 cho tới khi hết
3. Thực hiện
var request = require('request'); var fs = require('fs'); var _ = require('lodash'); var cheerio = require('cheerio'); var async = require('async'); // đường dẫn để crawl var url = 'http://dzut-co-hon.tiki.vn/result?page='; // hàm thực hiện truy vấn HTML và bóc tách dữ liệu function crawl(currentPage, callback) { console.log('Going to crawl page: ' + currentPage); request(url + currentPage, function (r, e, b) { var $ = cheerio.load(b); $('table tr:first-child').remove(); var $rows = $('table tr'); if ($rows.length > 0) { var rows = []; $rows.each(function () { rows.push({ email: $(this).find('td').eq(0).text(), product: $(this).find('td').eq(1).text(), batch: $(this).find('td').eq(2).text(), time: $(this).find('td').eq(3).text(), result: $(this).find('td').eq(4).text(), }); }); // ghi kết quả ra file: results/[page].json fs.writeFileSync('./results/' + currentPage + '.json', JSON.stringify(rows, undefined, 2)); callback(); } else { console.log('ended'); } }); } // quản lý hàng đợi, thực hiện 10 requests / thời điểm, tránh thực hiện DDOS server tiki var queues = async.queue(function (page, done) { crawl(page, done); }, 10); // thực hiện xong queues.drain = function () { console.log('ALL DONE!'); } // 16000 là số trang kết quả của tiki for(var _i=continuePage; _i<16000; _i++) { queues.push(_i); }
4. Kết quả
[ { "email": "tuyet*****@bidv.com.vn", "product": "Yêu Người Yêu Người Ta", "batch": "N/A", "time": "2014-08-14 16:27:55.294", "result": "Xém dzụt được" }, { "email": "tuyet*****@bidv.com.vn", "product": "Tơ Đồng Rỏ Máu", "batch": "N/A", "time": "2014-08-14 16:24:07.573", "result": "Xém dzụt được" } ]
II. Bổ sung dữ liệu
Khi nhìn vào kết quả này, chúng ta vẫn chưa kết luận được gì, mà nếu có thì cũng không chính xác! Các dữ liệu còn thiếu:
- Sản phẩm, giá trị sản phẩm
- Email đầy đủ
Từ giá trị sản phẩm có thể nói lên được nhiều điều về “cô hồn gian lận”:
- sản phẩm có giá trị cao sẽ hấp dẫn hơn
- sản phẩm ít khi lặp lại (có A rồi, thôi lấy B)
Email đầy đủ có thể nói lên nhiều điều:
- Loại trừ trường hợp mail bị che là giống nhau làm cho kết quả không còn chính xác.
- Từ email có thể suy ra thêm nhiều thứ như: Facebook (có Facebook -> quan hệ), search Google để bổ sung thông tin
1. Thông tin sản phẩm
Thực hiện
var request = require('request'); var fs = require('fs'); var cheerio = require('cheerio'); request.get('http://dzut-co-hon.tiki.vn/', function (err, res, body) { if (err) { console.error('Lỗi', err); return; } var $ = cheerio.load(body); var products = []; $('.product-box .product-box-item').each(function () { var product = { name: $(this).find('span.title').attr('title').trim(), base_price: parseInt($(this).find('.price-regular').text().trim().replace(/[^0-9]/g, '')), sale_price: parseInt($(this).find('.price-sale').text().trim().replace(/[^0-9]/g, '')), image: $(this).find('img').attr('src'), discount: parseInt($(this).find('.sale-tag').text().trim().replace(/[^0-9]/g, '')), } products.push(product); }); fs.writeFileSync('./results/products.json', JSON.stringify(products, undefined, 2)); console.log('Done!'); });
Kết quả
[ { "name": "Asus ZenFone 5 A501CG - 5 inch/ 2 nhân 1.6GHz/ 2 SIM/ 8GB/ 8MP/ 2110mAh", "base_price": 3990000, "sale_price": 377000, "image": "http://gs.tikicdn.com/assets/dch2014v2/media/white.png", "discount": 91 }, { "name": "Asus FonePad 7 FE170CG - 7 inch/ 8GB/ Wifi + 3G/ 3950mAh/ 2 SIM/ Hỗ Trợ Nghe Gọi", "base_price": 2990000, "sale_price": 277000, "image": "http://gs.tikicdn.com/assets/dch2014v2/media/white.png", "discount": 91 } ]
2. Email đầy đủ (báo cho Tiki.vn fix)
Trang kết quả http://dzut-co-hon.tiki.vn/result có cái box search email bự bành ky khói lửa trên đầu, khi bạn gõ email của bạn nó sẽ hiện ra các lần dzựt, kết quả, sản phẩm dzựt và email đầy đủ.
Thử chức năng search email với cái email giả định của tui là [email protected] đã bị che là abcx*****@gmail.com:
- abcx*****@gmail.com: không ra kết quả
- abcx: không ra kết quả
- gmail.com: không ra kết quả
- abcxyz123: ra kết quả
- [email protected]: ra kết quả
WTF? E hèm, Tui dừng lại, làm điếu thuốc cho thông não … 5, 10, 15 phút … Bingo!
- Tiki.vn dùng Magento (mở Tiki trong Chrome, mở console, gõ `Mage`, wah lah!)
- Tiki.vn’s Magento: Nginx + PHP + MySQL
- => dzut-co-hon.tiki.vn *: Nginx + PHP + MySQL
- Search trong MySQL?: full text search, LIKE
- Full text search: no way, overkill!
- LIKE: rất phổ biến
Tui suy ra thuật toán tìm kiếm của trang Dzựt Cô Hồn Online như sau:
- Người dùng gõ vô từ khóa
- Hệ thống dùng LIKE để tìm: %[từ-khóa]%
- Nếu kết quả là nhiều hơn 1 (hoặc 2?), không trả về
- Nếu kết quả là 1: trả về kết quả bao gồm đầy đủ email
=> muốn lấy được email đầy đủ, phải tạo ra từ khóa tìm kiếm trả về 1 kết quả.
Đào sâu vô toán tử LIKE của MySQL http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html, tui phát hiện ra 1 thứ dùng được
SQL pattern matching enables you to use “_” to match any single character and “%” to match an arbitrary number of characters (including zero characters).
Ký tự “_” chỉ khớp với 1 ký tự bất kỳ. Thử gõ lại từ khóa vô box search email xem nào:
- [email protected]: woh hoh! email đầy đủ của tui kia rồi.
- Thử với email bất kỳ, kết quả như mong đợi.
Đoạn này tui chỉ viết mã giả:
// đọc file kết quả đã lấy được từ bước trước // lấy ra email đã bị che abcx*****@gmail.com // định dạng email đã bị che thành từ khóa: [email protected] // dùng từ khóa này lên dzut-co-hom.tiki.vn tìm kiếm // lấy email đầy đủ // cập nhật vô kết quả trước đó var getMails = async.queue(function (task, done) { var keyword = task.keyword; var file = task.file; var hashed = task.hashed; request.post('http://dzut-co-hon.tiki.vn/result', { form: { email: keyword } }, function (e, r, b) { if (!e) { var $ = cheerio.load(b); $('table tr').eq(0).remove(); var rows = []; $('table tr').each(function () { var row = { hashed: hashed, email: $(this).find('td').eq(0).text(), product: $(this).find('td').eq(1).text(), date: $(this).find('td').eq(2).text(), status: $(this).find('td').eq(3).text(), file: file }; rows.push(row); }) fs.writeFile('./results/' + file, JSON.stringify(rows, undefined, 2), done); } else { done() } }) }, 10)
Như vậy là chúng ta có đầy đủ dữ liệu cần thiết, bắt tay vào phân thích.
III. Phân tích kết quả
Khi đã có đầy đủ dữ liệu, ta có thể import vào cơ sở dữ liệu: MySQL, MongoDB và truy vấn để dễ dàng trả lời các câu hỏi ở phần đầu:
- Ai là người trúng thưởng?
- Trúng thưởng mấy lần?
- Trúng thưởng đợt nào?
- Trúng thưởng món gì?
- Giá trị trúng thưởng là bao nhiêu?
- Dzựt bao nhiêu lần thì trúng?
=> lọc ra danh sách nghi vấn:
- Giá trị trúng thưởng cao.
- Tỷ lệ dzựt / trúng rất cao (theo Tui thấy có trường hợp 100%, dzựt 1 nhát ăn liền giải to).
- Dzựt trúng nhiều và giải thưởng rải đều từ cao -> thấp.
- Đợt nào cũng trúng.
Mở rộng ra thêm tí xíu từ email có thể trả lời thêm
- Có mối liên quan nào giữa người trúng thưởng và nội bộ Tiki hay không?
- Có mối liên quan nào giữa những người trúng thưởng giải cao hay không?
Nghi vấn chiêu trò:
- Hệ thống tự thêm vào 1 email dzựt thành công định trước cho mỗi đợt.
- Chỉ hiện sản phẩm có thể dzựt cho 1 số email và sắp xếp theo giá trị từ cao tới thấp.
- Có câu trả lời ẩn, đúng cho tất cả câu hỏi khi giật.
- Bên trong Tiki sẽ có những câu trả lời chính xác hơn khi có ghi nhận địa chỉ IP tham gia Dzựt Cô Hồn Online
Viết tới đây thì Tui cũng thấy đói bụng rồi, xin kết bài viết ở khía cạnh kỹ thuật và kết quả thì Tui không có, mà có thì Tui cũng không công bố vì nó có liên quan tới Tiki cũng như quyền riêng tư cá nhân.
IV. Kết
1. Node.js cùng với hệ thống module thiệt là mạnh mẽ, nhà nhà, người người hãy học và dùng Node.js :-p
2. Trong quá trình mõ mẫm mới thấy sự chuẩn bị của Tiki cho Dzựt Cô Hồn Online năm nay thiệt là nghiêm túc
- Năm nay chương trình Dzựt Cô Hồn Online được tách ra hệ thống máy chủ riêng để tránh ảnh hưởng Tiki.vn (năm ngoái là viết extension cho Magento, chạy chung hệ thống với trang Tiki.vn nên cả 2 cùng chết)
- Việc tách ra dẫn đến cách chơi và phát quà khác: đăng ký ở trang Dzựt Cô Hồn Online, chơi thắng thì được phiếu giảm giá, phiếu giảm giá được tạo bên Tiki.vn.
- Sử dụng Amazon Web Services (AWS), khu vực Singapore (giảm độ trễ khi truy cập)
- Chỉ sử dụng chung hệ thống server CDN với Tiki.vn (nguyên nhân làm cho Tiki.vn chậm?)
- Sử dụng Nginx + PHP-FPM
=> Tui nghĩ giải pháp của Tiki năm nay là đúng hướng và hợp lý rồi, một vài gợi ý cho Tiki cho năm sau làm tốt hơn:
- Cho người chơi đăng ký tham gia trước, tới giờ chỉ chơi thôi -> dự đoán được số lượng người tham gia cùng lúc, tính toán máy chủ hợp lý hơn.
- Cấu hình load balancing trước cho Nginx, khi thấy quá tải chỉ việc thêm server, reload Nginx config.
- Tách máy chủ MySQL ra server riêng (không biết là năm nay có làm chưa?), cấu hình MySQL 1 master / 1 slave từ trước, đụng trận thì gia giảm server cho hợp lý.
3. Bài viết là sự đồng cảm của Tui đến mấy bạn giống Tui, làm kỹ thuật mà chưa đủ độ “cô hồn”.
4. Hẹn gặp lại các bạn “cô hồn” vào năm sau và cảm ơn Tiki đã đến sân chơi này.
Tổng hợp tin tức sử dụng node.js
9.081 viewsHãy tưởng tượng rằng bạn đang chuẩn bị bắt tay vào thực hiện dự án N.GN (Next Google News) hay N.BM (Next Báo mới), bạn đang cầm một danh sách dài các chức năng, yêu cầu:
- thu thập bài viết từ nhiều nguồn: vnexpress.net, news.zing.vn, soha.vn, …
- tự động sắp xếp bài viết
- phát hiện bài viết trùng lặp
- tóm tắt nội dung bài viết
- phân tích hình đại diện cho bài viết
- xây dựng website
- …
Việc xây dựng dự án trên cần có các công nghệ thích hợp: Python để thu thập bài viết, Java dùng cho việc phân tích, PHP cho website, MongoDB để lưu trữ dữ liệu … (thêm công nghệ khác của bạn vô …) trước khi quyết định chọn công nghê để bắt tay vào làm, mình đề xuất các bạn nên sử dụng node.js, lý do mình sẽ trình bày trong loạt bài viết sau đây:
- Thu thập bài viết dùng thư viện request – node.js (TODO)
- Trích xuất nội dung, hình đại diện bài viết sử dụng readability – node.js (TODO)
- Phát hiện bài viết trùng lặp dùng với node.js (TODO)
- Tóm tắt nội dung bài viết với node-summary – node.js (TODO)
Quét cổng mạng siêu nhanh với evilscan – node.js
8.462 viewsQuét cổng (port scanning) là công việc thường xuyên của người quản trị mạng, web developer và hacker dù là mũ trắng hay đen. node.js cùng với cơ chế vòng lặp sự kiện là môi trường cũng như công cụ hoàn hảo để lập trình ứng dụng mạng nhanh, mạnh.
Hôm nay VietJS giới thiệu với các bạn bộ thư viện evilscan quét cổng cực nhanh và mạnh cho node.js.
evilscan hỗ trợ sử dụng dưới dạng module cho node.js hoặc chạy độc lập, các chức năng bao gồm
- quét một IP hoặc dãy IP. Ví dụ: 172.16.0.1, 172.16.0.1-172.16.0.255, 172.16.0.0/16
- quét một cổng, nhiều cổng, dãy cổng. Ví dụ: 22, 20-22
- đọc biểu ngữ của cổng. Ví dụ: khi scan port 22 của Ubuntu server sẽ trả về biểu ngữ OpenSSH_5.9p1 Debian-5ubuntu1.1
- IAC negotiation (?)
- truy ngược domain từ một IP (reverse dns lookup)
- truy ngược thông tin định vị của IP (geolocation)
Sử dụng evilscan như ứng dụng độc lập
# cài đặt evilscan toàn cục (-g) npm install -g evilscan # xem hướng dẫn sử dụng evilscan --help # scan toàn bộ port trong mạng LAN với dãy IP 192.168.0.0/24 và in ra biểu ngữ evilscan 172.16.0.0/24 --port=0-65535 --banner
Sử dụng evilscan dưới dạng module
Để hiểu cách sử dụng evilscan dưới dạng module, cách tốt nhất chúng ta sẽ viết ứng dụng API scan port đơn giản
Mã nguồn: http://github.com/vietjs/port-scanner
Demo: http://port-scanner.herokuapp.com/port/scan?ip=74.125.225.167&port=80
Thực hiện
- Tạo thư mục chứa mã nguồn dự án
mkdir port-scanner
- Khởi tạo node.js project tại thư mục vừa tạo
cd port-scanner npm init
- Download module express, evilscan và ghi thông tin phụ thuộc vô file package.json (–save), sử dụng express để đơn giản hóa việc xử lý truy vấn HTTP
npm install express evilscan --save
- Tạo file index.js và viết code cho ứng dụng
index.js
// khai báo require module evilscan var evilscan = require('evilscan') // khai báo require module express , express = require('express'); // khởi tạo ứng dụng express var app = express(); // ứng dụng port-scanning tự giới thiệu về mình app.get('/', function(req, res){ res.send('<h1>Tui tên là port-scanning.<br>'+ 'Tui chạy rất nhanh và rất nguy hiểm.<br>'+ '<a href="http://vietjs.com/?p=9">http://vietjs.com/2014/06/01/quet-cong-mang-sieu-nhanh-su-dung-node-js/</a></h1>'); }); // ứng dụng port-scanning làm việc app.get('/port/scan', function (req, res) { var ips = req.query.ip , ports = req.query.port , options = {} , results = []; // kiểm tra lỗi và trả về thông báo khi thiếu thông tin if (!ips || !ports) { res.send(400, 'Missing ip or port params. Correctly URL is /port/scan?ip=192.168.0.1&port=22'); return; } // khai báo options cho evilscan // xem thông tin đầy đủ tại https://github.com/eviltik/evilscan options = { target: ips, // địa chỉ IP, dãy IP port: ports, // cổng status:'TROU', // ? banner: true, // hiển thị biểu ngữ của cổng kết nối concurrency: 255, // số lượng kết nối đồng thời timeout: 2000, // thời gian chờ kết nối (ms) geo: true, // xác định vị trí địa lý của địa chỉ IP reverse: true // hiển thị thông tin reverse dns };; // khởi tạo evilscan scanner var scanner = new evilscan(options, function () { // khởi tạo xong scanner }); // khi quét có kết quả, lưu kết quả vào biến results scanner.on('result', function (data) { results.push(data); }); // khi có lỗi, trả về thông báo lỗi scanner.on('error', function (err) { res.send(500, 'evilscan error: ' + err); }); // khi quét xong, kết thúc xử lý cho truy vấn này và trả về kết quả scanner.on('done', function () { res.send(results); }); // chạy evilscan scanner scanner.run(); }); var HTTP_PORT = process.env.PORT || 3000; app.listen(HTTP_PORT); console.log('port-scanning application listening at 0.0.0.0:' + HTTP_PORT);
- Chạy ứng dụng
node index.js
- Sử dụng truy cập http://localhost:3000/port/scan?ip=127.0.0.1&port=3000
Phản hồi gần đây