Cheerio là gì?

Cheerio là gì?


Đây là thư viện javascript rất mạnh dùng trong việc crawl dữ liệu, thư viện này cung cấp bộ Cheerio Selector gần giống như với jQuery Selector cho nên việc sử dụng
các hàm cũng tương tự như cách sử dụng jQuery Selector. Nói đơn giản bạn có sử dụng các hàm quen thuộc khi sử dụng jQuery ví dụ như : .html() để lấy đoạn mã html, attr( name, value ) để lấy thuộc tính phần tử,…
hệt như khi sử dụng Jquery, thông tin chi tiết về cách hoạt động của thư viện này bạn có thể tìm hiểu thêm tại đây.

Viết ứng dụng demo tự động thông báo khi có chương truyện mới


Luồng thực hiện của chúng ta như sau:

  • Bước 1: Lấy danh sách chương mới nhất của truyện
  • Bước 2: So sánh chương truyện này với chương truyện được lưu trong file json để biết được có chương truyện mới hay chưa, nếu file này chưa có tiến hành tạo mới
  • Bước 3: Nếu có chương truyện mới : lấy chi tiết truyện -> gửi email thông báo

Nào bắt tay vào thực hiện thôi


Thực hiện cài đặt thư viện cheerio và request ( thư viện này dùng gọi tới địa chỉ website muốn crawl dữ liệu)

[] [] [] [link text]
1
2
3
npm install cheerio --save
npm install request --save
npm install nodemailer --save

Thực hiện khai báo các thư viện cần thiết

1
2
3
4
const cheerio = require('cheerio');
const request = require('request');
const fs = require('fs');
const nodemailer = require('nodemailer');

Sau đó viết ra hàm Crawler thực hiện lấy chương mới nhất từ trang truyện -> so sánh với file json

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//   Hàm lấy dữ liệu từ website
function Crawler()
{
//Gửi 1 request tới website
request('http://truyencv.com/pham-nhan-tu-tien-chi-tien-gioi-thien/', function (err, res, body)
{
// Sử dụng cheerio.load để lấy dữ liệu trả về
var $ = cheerio.load(body);
// Lấy chương mới nhất của truyện
var newestChap = $('.list-overview .item .item-value a').text();
var obj = {
'newestChap' : newestChap
}
var json = JSON.stringify(obj);
// Kiểm tra file newchap.json tồn tại không
if (!fs.existsSync('newchap.json')) {
// Nếu chưa tồn tại tạo file mới
fs.writeFile('newchap.json', json, '', (err)=>{
if (err) throw err;
console.log('Tạo file newchap.json thành công!');
});
return;
}
// Đọc file newchap.json nằm trong thư mục dự án
fs.readFile('newchap.json', function readFileCallback(err, data)
{
if (err)
{
console.log('Đọc file newchap.json thất bại!');
return;
}
else
{
// Lấy chương truyện mới nhất từ file json
obj = JSON.parse(data);
var dbChap = obj.newestChap;
// So sánh 2 chương truyện nếu khác nhau -> đã có chương mới
if(newestChap !== dbChap)
{
// Lưu chương mới vào file newchap.json
fs.writeFile('newchap.json', json, '', (err)=>{
if (err) throw err;
console.log('Đã có chương mới!');
console.log('Cập nhật newchap.json thành công!');
});
// Lấy đường dẫn chương truyện mới
var detailUrl = $('.list-overview .item .item-value a').attr('href');
// Tạo yêu cầu mới -> lấy thông tin chương mới
request(detailUrl, (err, res, body)=>{
let cheerioDetail = cheerio.load(body);
let contentDetail = cheerioDetail('.truyencv-read-content .content').text();
// Gửi email thông báo
sendEmail(newestChap,contentDetail);

});
}
else{
console.log('Chưa có chương mới!');
}
}
});
})
}

Viết thêm hàm sendEmail sử dụng thư viện nodemailer để gửi như 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
function sendEmail(subject, content)
{
var transporter = nodemailer.createTransport({
service : 'gmail',
auth: {
user : 'xuanlocle2412@gmail.com',
pass : 'matkhaucuaban'
}
});
var mailOptions= {
from : 'xuanlocle2412@gmail.com',
to: 'xuanlocle2412@gmail.com',
subject : 'Pham nhan tu tien ra chuong moi '+subject,
text : content
}
transporter.sendMail(mailOptions, function(err, info){
if(err)
{
console.log('Lỗi khi gửi mail: ', err);
}
else
{
console.log('Đã gửi email: ', info.response);
}
});
}

Cuối cùng viết thêm 1 hàm lặp thao tác Crawler này 3 giây một lần

1
2
3
var timer = setInterval(function() {
return Crawler();
}, 5000);

Sau đó ta chạy lệnh node server.js trong cửa sổ command và chờ mail được gửi thôi!
Như vậy mình đã hướng dẫn cho các bạn làm 1 ví dụ demo đơn giản crawl dữ liệu với cheerio và nodejs. Cảm ơn và hẹn gặp lại các bạn trong những bài sau

Blog đầu tiên nên đặt tựa là gì?

Lời mở đầu


Chào mọi người, thật ngại quá. Đây là blog - có thể được gọi là đầu tiên của mình hiện tại. Trước đây mình từng viết blog về tình yêu, về những cảm nhận sâu sắc các thứ nhưng sau một thời gian thấy không hợp nên mình xóa hết rồi. Hehe, từ đây mình sẽ làm quen với những điều mới mẻ, lặng lẽ mà viết một blog của mình thôi.

Vậy… mình là ai?

Hiện tại mình đang là dev tại một công ty đầy tiềm năng trong lĩnh vực xây dựng. Công việc của cũng không xoay quanh gì ngoài những dòng code huyền bí, những UI hợp lý và những anh chị đầy nhiệt huyết luôn hết mình bất thình lình trong công việc. Không những thế, mình còn có hàng tá những ứng dụng muốn làm. Cái mình thiếu chỉ là thời gian mà thôi, hị hị.

Mục phụ

Cái gì cũng có sự bắt đầu, để đến mục đích phải có mục phụ, ai viết blog cũng sẽ trải qua những bài viết đầu tiên. Và, “mục phụ” của bài viết này là mình khoe mình đã có blog mới. Nhẹ hơn, lược giản hơn ít tính năng hơn. Nhưng mình thích là được.

Mục đích chính


Mục đích chính của mình vẫn là khoe blog của mình. Nhưng khoan, blog của mình hiện tại đang sử dụng github page - nếu bạn muốn xây dựng một website mà chỉ bao gồm HTML, CSS và Js. Bạn có thể xây dựng 1 static web hoàn toàn miễn phí phục vụ cho việc học tập hoặc thậm chí cho mục đích kinh doanh với nó https://pages.github.com.

  • Lợi ích của Github Page là dễ dàng cài đặt, dễ dàng commit các nội dung mới. Và hơn hết, nó nhẹ nữa.
  • Điểm khó khăn của Github Page là khó khăn với newbie, bạn sẽ tốn kha khá thời gian để vật lộn với github, với repository, với hexo, cách trỏ DNS hay thậm chí là cách viết bài với Markdown nữa. Nhưng đừng lo, vô số đàn anh đàn chị đi trước đã để lại một kho hướng dẫn đồ sộ trên google. Nên cũng không quá khó khăn nếu bạn là người mới đâu nhé!
  • Nếu bạn cảm thấy muốn phát triển một blog dễ dàng hơn, mình khuyến cáo bạn nên dùng wordpress.com. Giao diện trực quan, tạo xong tài khoản, up avatar, banner, đặt một tiêu đề thật chất, viết vài dòng nội dung là bạn có thể trở thành blogger rồi. Thú vị phải không nào.

Yêu

Thế giới hơn 8 tỷ người, hàng ngàn hàng triệu trang blog nhan nhản trên mang từng sứ mệnh riêng. Mình thừa nhận chúng ta có duyên số, nên nếu có thể, bạn hãy cho mình xin một số bình luận cho vui nhà vui blog. Giờ là 1 giờ sáng và mình đã đọc đi đọc lại 3 lần blog này, chứng tỏ mình trân trọng bạn đọc biết bao. Hị hị. Nếu bạn tò mò tại sao mình không giải thích cái tiêu đề thì bạn đoán đi.

Hướng dẫn sử dụng listview trong Flutter

Trước khi bắt đầu, mình muốn chắc chắn rằng bạn đã có cài đặt flutter. Nếu chưa cài, mình sẽ có bài hướng dẫn cài flutter trong tương lai.Giờ thì bạn có thể search cách cài đặt flutter trên google nhé.

Trong bài viết này, mình sẽ hướng dẫn các bạn:

  • Sử dụng listview để hiện ra danh sách các từ tiếng anh random.
  • Đổi màu cho các item trong listview
  • Thêm icon check/uncheck cho item listview

Import các thư viện cần thiết

Vì chúng ta sẽ hiện random các từ tiếng anh nên mình sẽ sử dụng thư viện english_word, một thư viện phổ biến. Bạn có thể tham khảo tất cả các thư viện hiện có tại pub.dartlang.org.
Mở file “pubspec.yaml”, file này có tác dụng như build-gradle (app) của android thuần, dùng để khai báo các thư viện sẽ sử dụng. Bạn copy dòng sau vào dependency tuong tự như sau:

[]
1
2
3
4
5
6
7
8
9
10
dependencies:
flutter:
sdk: flutter

# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
english_words: ^3.1.5

*ký tự ‘^’ gợi cho chương trình biết rằng sẽ chỉ lấy thư viện version 3.1.5 trở lên.

Tiếp theo bạn, bật terminal ở dưới góc trái của IDE Android studio, nhập dòng lệnh sau:
flutter packages get

Dòng lệnh này sẽ download các thư viện bạn đã khai báo. Một bước nữa, vào “main.dart”, bạn thêm dòng lệnh import mà thư viện english word đã cho:

import ‘package:english_words/english_words.dart’;

Bắt đầu tạo listview trong flutter

Sau khi đã thêm thư viện vào project rồi, bạn mở “main.dart”, đảm bảo hàm main của bạn đang được trỏ vào đúng lớp.

[]
1
2
3
4
5
6
7
8
9
10
11
main() => runApp(new MyApp()); 	//One-line function Khai báo chương trình chính

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {

return new MaterialApp(
title: "Title",
home: new MyRandomWord());
}
}

Để hàm MyApp hoạt động, bạn phải có lớp MyRandomWord như sau:
[]
1
2
3
4
5
6
7
8
class MyRandomWord extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyRandomWordState(); //
}
}


Class kế thừa từ StatefulWidget nên bạn cần phải tạo một lớp kế thừa từ State để khai báo cho nó:

[]
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
36
37
38
39
40
41
class MyRandomWordState extends State<MyRandomWord> {

final _words = <WordPair>[]; // Danh sách các từ tiếng anh sẽ được tạo tự động
final _checkedWord = new Set<WordPair>(); //Danh sách lưu các từ được nhấn để hiện check hay uncheck
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("List item"),
),
body: new ListView.builder(itemBuilder: (context,index) { //context là con trỏ hiện tại, index là item hiện tại đang xem
if(index>=_words.length){ //Đặt điều kiện, khi lưới xuống cuối cùng của list view thì sẽ tạo thêm 10 từ random nữa
_words.addAll(generateWordPairs().take(10));
}
return _buildRow(_words[index],index);
}) ,
);
}
Widget _buildRow(WordPair wordPair,int index) {
final color = index%2 ==0 ?Colors.red:Colors.blue; //Làm xen kẽ màu cho đẹp
final isChecked = _checkedWord.contains(wordPair);
return new ListTile(
leading: new Icon(isChecked?Icons.check_box:Icons.check_box_outline_blank,
color: color,),
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 18.0,color: color),
),
onTap: (){
setState(() {
if(isChecked){
_checkedWord.remove(wordPair);
}else
_checkedWord.add(wordPair);
});
},
);
}
}

Bạn nên nhớ, để một ListView.builder được tạo đúng thì bạn phải trả về hàm ListTile, ListTile này sẽ tạo từng dòng cho ListView, nó có chức năng tương tự như listviewitem vậy. Đa số chức năng mình đã comment trong code nên mình sẽ không nhắc lại.
Đến đây, bạn có thể đọc lại một lần nữa hoặc triển khai code để chạy thử, nhờ đó bạn có thể biết được từng thành phần trong ListView sẽ hoạt động như thế nào.
Nếu làm đúng theo những bước trên, bạn sẽ nhận được kết quả như sau.

Một lần nữa, cảm ơn bạn đã theo dõi blog của mình. Hi vọng sẽ gặp bạn trong bài viết tiếp theo, “Chuyển màn hình với navigator và route”. Xin gửi lời cảm ơn chân thành đến thầy Nguyễn Đức Hoàng với những kiến thức rất ngắn gọn và hữu ích.