Yêu cầu HTTP (+ làm việc với cookie, proxy, phiên)
Base class methods
To collect data from a web page, you need to make an HTTP request. In JavaScript API v2 of A-Parser, a lightweight method for making HTTP requests is implemented, which returns a JSON object in response depending on the specified method arguments. Next, you will learn: how an HTTP request is made, what arguments and options the method has, the results of the specified options, how to specify the success condition of an HTTP request, and more.
These methods are inherited from BaseParser and are the basis for creating your own parsers
Getting an HTTP response for a request, the following arguments are specified:
method- request method (GET, POST...)url- link for the requestqueryParams- hash with get parameters or hash with the body of a post request HTTP request hoặc lưu để thực thi bởi luồng khác bằng Trình quản lý phiên.
Các phương thức này được kế thừa từ BaseParser và là nền tảng để tạo các công cụ cào dữ liệu tùy chỉnh
await this.request(method, url[, queryParams][, opts])
await this.request(method, url, queryParams, opts)
Nhận phản hồi HTTP theo truy vấn, các tham số được chỉ định như sau:
method- phương thức của truy vấn (GET, POST...)url- liên kết cho truy vấnqueryParams- hash với tham số get hoặc hash với thân của truy vấn postopts- hash với các tùy chọn của truy vấn
opts.check_content
check_content: [ điều kiện1, điều kiện2, ...] - mảng điều kiện để kiểm tra nội dung nhận được, nếu kiểm tra không
đạt, thì truy vấn sẽ được lặp lại với proxy khác.
Tính năng:
- sử dụng chuỗi làm điều kiện (tìm theo sự xuất hiện của chuỗi)
- sử dụng biểu thức chính quy làm điều kiện
- sử dụng các hàm kiểm tra riêng, trong đó dữ liệu và header của phản hồi được truyền vào
- có thể đặt nhiều loại điều kiện khác nhau cùng lúc
- để phủ định logic, hãy đặt điều kiện vào mảng, tức là
check_content: ['xxxx', [/yyyy/]]có nghĩa là truy vấn sẽ được coi là thành công nếu trong dữ liệu nhận được có chuỗi conxxxxvà đồng thời biểu thức/yyyy/không tìm thấy kết quả khớp trên trang
Để truy vấn thành công, tất cả các kiểm tra được chỉ định trong mảng phải đạt
Ví dụ (trong phần chú thích có ghi những gì cần để truy vấn được coi là thành công):
let response = await this.request('GET', set.query, {}, {
check_content: [
/<\/html>|<\/body>/, // trên trang nhận được phải khớp với biểu thức chính quy này
['XXXX'], // trên trang nhận được không được có chuỗi con này
'</html>', // trên trang nhận được phải có chuỗi con này
(data, hdr) => {
return hdr.Status == 200 && data.length > 100;
} // hàm này phải trả về true
]
});
opts.decode
decode: 'auto-html' - tự động xác định mã hóa và chuyển đổi sang utf8
Các giá trị có thể có:
auto-html- dựa trên header, thẻ meta và nội dung của trang (phương án khuyến nghị tối ưu)utf8- chỉ định rằng tài liệu ở mã hóa utf8<encoding>- bất kỳ mã hóa nào khác
opts.headers
headers: { ... } - hash với header, tên header được đặt ở dạng chữ thường, cũng có thể chỉ định cookie.
Ví dụ:
headers: {
accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
'accept-encoding': 'gzip, deflate, br',
cookie: 'a=321; b=test',
'user-agent' 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36'
}
opts.headers_order
headers_order: ['cookie', 'user-agent', ...] - cho phép ghi đè thứ tự sắp xếp của header
opts.onlyheaders
onlyheaders: 0 - xác định việc đọc data, nếu bật (1), chỉ nhận header
opts.recurse
recurse: N - số lần chuyển hướng tối đa, mặc định là 7, dùng 0 để tắt việc đi theo
chuyển hướng
opts.proxyretries
proxyretries: N - số lần thử thực hiện truy vấn, mặc định lấy từ cài đặt của công cụ cào dữ liệu
opts.parsecodes
parsecodes: { ... } - danh sách các mã phản hồi HTTP mà công cụ cào dữ liệu sẽ coi là thành công, mặc định lấy từ
cài đặt của công cụ cào dữ liệu. Nếu chỉ định '*': 1 thì tất cả phản hồi sẽ được coi là thành công.
Ví dụ:
parsecodes: {
200: 1,
403: 1,
500: 1
}
opts.timeout
timeout: N - thời gian chờ phản hồi tính bằng giây, mặc định lấy từ cài đặt của công cụ cào dữ liệu
opts.do_gzip
do_gzip: 1 - xác định có sử dụng nén hay không (gzip/deflate/br), mặc định bật (1), để tắt
cần đặt giá trị 0
opts.max_size
max_size: N - kích thước tối đa của phản hồi tính bằng byte, mặc định lấy từ cài đặt của công cụ cào dữ liệu
opts.cookie_jar
cookie_jar: { ... } - hash với cookie. Ví dụ hash:
"cookie_jar": {
"version": 1,
".google.com": {
"/": {
"login": {
"value": "true"
},
"lang": {
"value": "ru-RU"
}
}
},
".test.google.com": {
"/": {
"id": {
"value": 155643
}
}
}
opts.attempt
attempt: N - chỉ định số lần thử hiện tại, khi sử dụng tham số này trình xử lý thử tích hợp cho
truy vấn này sẽ bị bỏ qua
opts.browser
browser: 1 - mô phỏng tự động header của trình duyệt (1 - bật, 0 - tắt)
opts.use_proxy
use_proxy: 1 - ghi đè việc sử dụng proxy cho một truy vấn riêng bên trong JS parser trên tham số toàn cục
Use proxy (1 - bật, 0 - tắt)
opts.noextraquery
noextraquery: 0 - tắt việc thêm Extra query string vào URL của truy vấn (1 - bật, 0 - tắt)
opts.save_to_file
save_to_file: file - cho phép tải file trực tiếp xuống đĩa, bỏ qua việc ghi vào bộ nhớ. Thay vì file cần chỉ định tên và
đường dẫn để lưu file theo cách nào. Khi dùng tùy chọn này, mọi thứ liên quan đến data sẽ bị bỏ qua (kiểm tra nội dung
trong opts.check_content sẽ không được thực hiện, response.data sẽ trống, v.v.)
opts.bypass_cloudflare
bypass_cloudflare: 0 - tự động vượt qua bảo vệ JavaScript của CloudFlare bằng trình duyệt Chrome (1 - bật, 0 -
tắt)
Việc kiểm soát Chrome Headless trong trường hợp này được thực hiện bởi các cài đặt của công cụ cào dữ liệu bypassCloudFlareChromeMaxPages
và bypassCloudFlareChromeHeadless, cần chỉ định trong static defaultConf và static editableConf:
static defaultConf: typeof BaseParser.defaultConf = {
version: '0.0.1',
results: {
flat: [
['title', 'Title'],
]
},
max_size: 2 * 1024 * 1024,
parsecodes: {
200: 1,
},
results_format: "$title\n",
bypass_cloudflare: 1,
bypassCloudFlareChromeMaxPages: 20,
bypassCloudFlareChromeHeadless: 0
};
static editableConf: typeof BaseParser.editableConf = [
['bypass_cloudflare', ['textfield', 'bypass_cloudflare']],
['bypassCloudFlareChromeMaxPages', ['textfield', 'bypassCloudFlareChromeMaxPages']],
['bypassCloudFlareChromeHeadless', ['textfield', 'bypassCloudFlareChromeHeadless']],
];
async parse(set, results) {
const {success, data, headers} = await this.request('GET', set.query, {}, {
bypass_cloudflare: this.conf.bypass_cloudflare
});
return results;
}
opts.follow_meta_refresh
follow_meta_refresh: 0 - cho phép đi theo các chuyển hướng được khai báo qua thẻ meta HTML:
<meta http-equiv="refresh" content="time; url=..."/>
opts.redirect_filter
redirect_filter: (hdr) => 1 | 0 - cho phép đặt hàm lọc việc đi theo chuyển hướng, nếu hàm
trả về 1, thì công cụ cào dữ liệu sẽ đi theo chuyển hướng (tính đến tham số opts.recurse), khi trả về 0 việc đi theo
chuyển hướng sẽ dừng lại:
redirect_filter: (hdr) => {
if (hdr.location.match(/login/))
return 1;
return 0;
}
opts.follow_common_rediects
opts.follow_common_rediects: 0 - xác định có đi theo các redirect chuẩn hay không (ví dụ http -> https
và/hoặc www.domain.com -> domain.com), nếu đặt 1 thì công cụ cào dữ liệu sẽ đi theo các redirect chuẩn mà không tính
tham số opts.recurse
opts.http2
opts.http2: 0 - xác định có sử dụng giao thức HTTP/2 khi thực hiện truy vấn hay không, mặc định
sử dụng HTTP/1.1
opts.randomize_tls_fingerprint
opts.randomize_tls_fingerprint: 0 - tùy chọn này cho phép vượt qua chặn website theo dấu vân tay TLS (1 - bật, 0 -
tắt)
opts.tlsOpts
tlsOpts: { ... } – cho phép
truyền các cài đặt cho
kết nối https
await this.cookies.*
Làm việc với cookies cho truy vấn hiện tại
.getAll()
Lấy mảng cookies
await this.cookies.getAll();

.setAll(cookie_jar)
Thiết lập cookies, đối số phải được truyền vào là hash với cookie
async parse(set, results) {
this.logger.put("Start scraping query: " + set.query);
await this.cookies.setAll({
"version": 1,
".google.com": {
"/": {
"login": {
"value": "true"
},
"lang": {
"value": "ru-RU"
}
}
},
".test.google.com": {
"/": {
"id": {
"value": 155643
}
}
}
});
let cookies = await this.cookies.getAll();
this.logger.put("Cookies: " + JSON.stringify(cookies));
results.SKIP = 1;
return results;
}

.set(host, path, name, value)
await this.cookies.set(host, path, name, value) - thiết lập một cookie đơn lẻ.
Phạm vi áp dụng của cookie phụ thuộc trực tiếp vào định dạng của domain được chỉ định, vì vậy trong host cần xét đến việc có dấu chấm trước host hay không:
- nếu có chỉ định dấu chấm (
this.cookies.set('.domain.com', ...)), cookie sẽ được dùng cho tất cả các subdomain (ví dụ a.domain.com, b.a.domain.com) - nếu host được chỉ định không có dấu chấm ở trước (
this.cookies.set('site.com', ...)), thì cookie sẽ được sử dụng chỉ cho host đã chỉ định (host-only cookie) và không được gửi tới các subdomain
Sự khác biệt này cực kỳ quan trọng, vì việc đồng thời tồn tại cookie có dấu chấm và không có thể dẫn đến trùng lặp và hoạt động không thể dự đoán của website. Để mô phỏng đúng, hãy luôn kiểm tra website mục tiêu thiết lập cookie như thế nào (có thuộc tính Domain hay không), và sử dụng định dạng tương ứng.
async parse(set, results) {
this.logger.put("Start scraping query: " + set.query);
await this.cookies.set('.a-parser.com', '/', 'Test-cookie-1', 1);
await this.cookies.set('.a-parser.com', '/', 'Test-cookie-2', 'test-value');
let cookies = await this.cookies.getAll();
this.logger.put("Cookies: " + JSON.stringify(cookies));
results.SKIP = 1;
return results;
}

await this.proxy.*
Làm việc với proxy
.next()
Đổi sang proxy tiếp theo, proxy cũ sẽ không còn được dùng cho truy vấn hiện tại
.ban()
Đổi và ban proxy (cần dùng khi dịch vụ chặn hoạt động theo IP), proxy sẽ bị ban trong khoảng thời gian,
được chỉ định trong cài đặt của công cụ cào dữ liệu (proxybannedcleanup)
.get()
Lấy proxy hiện tại (proxy cuối cùng đã được dùng để thực hiện truy vấn)
.set(proxy, noChange?)
await this.proxy.set('http://127.0.0.1:8080', true) - đặt proxy cho yêu cầu tiếp theo. Tham số noChange không bắt buộc, nếu đặt true thì proxy sẽ không thay đổi giữa các lần thử. Mặc định noChange = false
await this.sessionManager.*
Các phương thức để làm việc với phiên. Mỗi phiên luôn lưu proxy đã sử dụng và cookie. Ngoài ra có thể lưu dữ liệu tùy ý bổ sung.
Để sử dụng phiên trong công cụ cào dữ liệu JS trước tiên phải khởi tạo Trình quản lý phiên. Việc này được thực hiện bằng phương thức await this.sessionManagerinit() trong init()
.init(opts?)
Khởi tạo Trình quản lý session. Làm đối số có thể truyền một đối tượng (opts) với các tham số bổ sung (tất cả tham số đều không bắt buộc):
name- cho phép ghi đè tên của công cụ cào dữ liệu mà các session thuộc về, mặc định bằng tên của công cụ cào dữ liệu đang thực hiện khởi tạowaitForSession- chỉ ra cho công cụ cào dữ liệu chờ phiên cho đến khi nó xuất hiện (điều này chỉ có ý nghĩa khi có nhiều job chạy, ví dụ một job tạo phiên, job khác dùng chúng), tức là.get()và.reset()sẽ luôn chờ phiêndomain- chỉ ra tìm phiên trong tất cả các phiên đã lưu của công cụ cào dữ liệu này (nếu không chỉ định giá trị), hoặc chỉ cho một domain cụ thể (cần chỉ định domain có dấu chấm ở đầu, ví dụ.site.com)sessionsKey- cho phép đặt thủ công tên kho lưu trữ phiên, nếu không được đặt thì tên sẽ được tạo tự động dựa trênname(hoặc tên công cụ cào dữ liệu nếunamekhông được đặt), domain và trình kiểm tra proxyexpire- đặt thời gian sống của session tính bằng phút, mặc định không giới hạn
Ví dụ sử dụng:
async init() {
await this.sessionManager.init({
name: 'JS::test',
expire: 15 * 60
});
}
.get(opts?)
Lấy session mới, cần gọi trước khi thực hiện truy vấn (trước lần thử đầu tiên). Trả về một đối tượng với dữ liệu tùy ý đã được lưu trong session. Làm đối số có thể truyền một đối tượng (opts) với các tham số bổ sung (tất cả tham số đều không bắt buộc):
waitTimeout- có thể chỉ định số phút chờ phiên xuất hiện, hoạt động độc lập với tham sốwaitForSessiontrong.init()(bỏ qua nó), khi hết thời gian sẽ dùng phiên rỗngtag- lấy session với tag đã chỉ định, ví dụ có thể dùng tên domain để gán session với các domain từ đó chúng được nhận
Ví dụ sử dụng:
await this.sessionManager.get({
waitTimeout: 10,
tag: 'test session'
})
.reset(opts?)
Xóa cookie và lấy session mới. Cần dùng nếu truy vấn với session hiện tại không thành công. Trả về một đối tượng với dữ liệu tùy ý đã được lưu trong session. Làm đối số có thể truyền một đối tượng (opts) với các tham số bổ sung (tất cả tham số đều không bắt buộc):
waitTimeout- có thể chỉ định số phút chờ phiên xuất hiện, hoạt động độc lập với tham sốwaitForSessiontrong.init()(bỏ qua nó), khi hết thời gian sẽ dùng phiên rỗngtag- lấy session với tag đã chỉ định, ví dụ có thể dùng tên domain để gán session với các domain từ đó chúng được nhận
Ví dụ sử dụng:
await this.sessionManager.reset({
waitTimeout: 5,
tag: 'test session'
})
.save(sessionOpts?, saveOpts?)
Lưu session thành công với khả năng lưu dữ liệu tùy ý trong session. Hỗ trợ 2 đối số không bắt buộc:
sessionOpts- dữ liệu tùy ý để lưu trong session, có thể là số, chuỗi, mảng hoặc đối tượngsaveOpts- đối tượng với các tham số lưu session:multiply- tham số không bắt buộc, cho phép nhân bản session, giá trị cần chỉ định là một sốtag- tham số không bắt buộc, đặt tag cho session được lưu, ví dụ có thể dùng tên domain để gán session với các domain từ đó chúng được nhận
Ví dụ sử dụng:
await this.sessionManager.save('some data here', {
multiply: 3,
tag: 'test session'
})
.count()
Trả về số lượng session cho Trình quản lý session hiện tại
Ví dụ sử dụng:
let sesCount = await this.sessionManager.count();
.removeById(sessionId)
Xóa tất cả session với id đã chỉ định. Trả về số lượng session đã xóa. Id của session hiện tại nằm trong biến this.sessionId
Ví dụ sử dụng:
const removedCount = await this.sessionManager.removeById(this.sessionId);
Ví dụ sử dụng Trình quản lý session tổng hợp
async init() {
await this.sessionManager.init({
expire: 15 * 60
});
}
async parse(set, results) {
let ses = await this.sessionManager.get();
for(let attempt = 1; attempt <= this.conf.proxyretries; attempt++) {
if(ses)
this.logger.put('Data from session:', ses);
const { success, data } = await this.request('GET', set.query, {}, { attempt });
if(success) {
// process data here
results.success = 1;
break;
} else if(attempt < this.conf.proxyretries) {
const removedCount = await this.sessionManager.removeById(this.sessionId);
this.logger.put(`Removed ${removedCount} bad sessions with id #${this.sessionId}`);
ses = await this.sessionManager.reset();
}
}
if(results.success) {
await this.sessionManager.save('Some data', { multiply: 2 });
this.logger.put(`Total we have ${await this.sessionManager.count()} sessions`);
}
return results;
}

Phương thức truy vấn await this.request
Phương thức GET
Có thể truyền tham số truy vấn trực tiếp trong chuỗi truy vấn https://a-parser.com/users/?type=staff:
const { success, data, headers } = await this.request('GET', 'https://a-parser.com/users/?type=staff');
Hoặc dưới dạng object trong queryParams, trong đó key: value tương đương param=value:
const { success, data, headers } = await this.request('GET', 'https://a-parser.com/users/', {
type: 'staff'
});
Phương thức POST
Nếu dùng phương thức POST, thân truy vấn có thể được truyền theo hai cách:
Liệt kê tên biến và giá trị của chúng trong
queryParams, ví dụ:{
"key": set.query,
"id": 1234,
"type": "text"
}Liệt kê chúng trong
opts.body, ví dụ:body: 'key=' + set.query + '&id=1234&type=text'
Nếu thân yêu cầu được truyền dưới dạng object, nó sẽ tự động được chuyển thành dạng form-urlencoded, đồng thời nếu chỉ định body và không
chỉ định header content-type thì sẽ tự động gán content-type: application/x-www-form-urlencoded:
const { success, data, headers } = await this.request('POST', 'https://jsonplaceholder.typicode.com/posts', {
title: 'foo,',
body: 'bar',
userId: 1
});
Nếu thân của truy vấn POST là chuỗi hoặc buffer, thì nó sẽ được truyền nguyên như vậy:
// truy vấn với chuỗi
const string = 'title=foo&body=bar&userId=1';
const { success, data, headers } = await this.request('POST', 'https://jsonplaceholder.typicode.com/posts', {}, {
body: string
});
// truy vấn với buffer
const string = 'title=foo&body=bar&userId=1';
const buf = Buffer.from(string, 'utf8');
const { success, data, headers } = await this.request('POST', 'https://jsonplaceholder.typicode.com/posts', {}, {
body: buf
});
Tải lên tệp
Gửi file bằng truy vấn POST bằng mô-đun form-data:
const file = fs.readFileSync('pathToFile');
const FormData = require('form-data');
const format = new FormData();
format.append('file', file, 'fileName.ext');
const { success, data, headers } = await this.request('POST', 'https://file.io', {}, {
headers: format.getHeaders(),
body: format.getBuffer()
});
Ví dụ gửi file trong truy vấn POST với kiểu nội dung multipart/form-data:
const EOL = '\r\n';
const file = fs.readFileSync('pathToFile');
const boundary = '----WebKitFormBoundary' + String(Math.random()).slice(2);
const requestHeaders = {
'content-type': 'multipart/form-data; boundary=' + boundary
};
const body = '--'
+ boundary
+ EOL
+ 'Content-Disposition: form-data; name="file"; filename="fileName.ext"'
+ EOL
+ 'Content-Type: text/html'
+ EOL
+ EOL
+ file
+ EOL
+ '--'
+ boundary
+ '--';
const { success, data, headers } = await this.request('POST', 'https://file.io', {}, {
headers: requestHeaders,
body
});