Перейти к основному содержимому

HTTP запросы(+работа с куками, прокси, сессии)

Методы базового класса

Чтобы собрать данные с веб-страницы нужно выполнить HTTP запрос. В JavaScript API v2 А-Парсера реализован легкий в использовании метод выполнения HTTP запросов, который в ответ возвращает JSON объект в зависимости от указанных аргументов метода. Далее вы узнаете: как производится HTTP запрос, какие аргументы и опции имеет метод, результаты указанных опций, как указывать условие успешности HTTP запроса, и другое.

Также описаны методы позволяющие легко манипулировать куками, прокси и сессией в создаваемом парсере. После успешного выполнения HTTP запроса, или перед выполнением, вы можете установить/изменить данные прокси/кук/сессии для выполнения HTTP запросов или сохранить для выполнения другим потоком с помощью Менеджера сессий.

Данные методы наследуются от BaseParser и являются основой для создания собственных парсеров

await this.request(method, url[, queryParams][, opts])

await this.request(method, url, queryParams, opts)

Получение HTTP ответа по запросу, в качестве аргументов указывается:

  • method - метода запроса (GET, POST...)
  • url - ссылка для запроса
  • queryParams - хэш с get параметрами или хэш с телом post-запроса
  • opts - хэш с опциями запроса

opts.check_content

check_content: [ условие1, условие2, ...] - массив условий для проверки получаемого контента, если проверка не проходит, то запрос будет повторен с другим прокси.

Возможности:

  • использование в качестве условий строк (поиск по вхождению строки)
  • использование в качестве условий регулярных выражений
  • использование своих функций проверок, в которые передаются данные и хедеры ответа
  • можно задать сразу несколько разных типов условий
  • для логического отрицания поместите условие в массив, т.е. check_content: ['xxxx', [/yyyy/]] означает что запрос будет считаться успешным, если в полученных данных содержится подстрока xxxx и при этом регулярное выражение /yyyy/ не находит совпадений на странице

Для успешного запроса должны пройти все указанные в массиве проверки

Пример (в комментариях указано что нужно для того, чтобы запрос считался успешным):

let response = await this.request('GET', set.query, {}, {
check_content: [
/<\/html>|<\/body>/, //на полученной странице должно сработать это регулярное выражение
['XXXX'], //на полученной странице не должно быть этой подстроки
'</html>', //на полученной странице должна быть такая подстрока
(data, hdr) => {
return hdr.Status == 200 && data.length > 100;
} //эта функция должна вернуть true
]
});

opts.decode

decode: 'auto-html' - автоматическое определение кодировки и преобразование в utf8

Возможные значения:

  • auto-html - на основе заголовков, тегов meta и по содержимому страници (оптимальный рекомендуемый вариант)
  • utf8 - указывает что документ в кодировке utf8
  • <encoding> - любая другая кодировка

opts.headers

headers: { ... } - хэш с заголовками, название заголовка задается в нижнем регистре, можно указать в т.ч. cookie Пример:

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', ...] - позволяет переопределить порядок сортировки заголовков

opts.onlyheaders

onlyheaders: 0 - определяет чтение data, если включено (1), получает только заголовки

opts.recurse

recurse: N - максимальное число переходов по редиректам, по умолчанию 7, используйте 0 для отключения перехода по редиректам

opts.proxyretries

proxyretries: N - число попыток выполнения запроса, по умолчанию берется из настроек парсера

opts.parsecodes

parsecodes: { ... } - перечень кодов HTTP ответов, которые парсер будет считать удачными, по умолчанию берется из настроек парсера. Если указать '*': 1 то все ответы будут считаться удачными. Пример:

parsecodes: {
200: 1,
403: 1,
500: 1
}

opts.timeout

timeout: N - таймаут ответа в секундах, по умолчанию берется из настроек парсера

opts.do_gzip

do_gzip: 1 - определяет использовать ли компрессию (gzip/deflate/br), по умолчанию включено (1), для выключения нужно задать значение 0

opts.max_size

max_size: N - максимальный размер ответа в байтах, по умолчанию берется из настроек парсера

opts.cookie_jar

cookie_jar: { ... } - хэш с куками. Пример хэша:

"cookie_jar": {
"version": 1,
".google.com": {
"/": {
"login": {
"value": "true"
},
"lang": {
"value": "ru-RU"
}
}
},
".test.google.com": {
"/": {
"id": {
"value": 155643
}
}
}

opts.attempt

attempt: N - указывает на номер текущей попытки, при использовании этого параметра встроенный обработчик попыток для данного запроса игнорируется

opts.browser

browser: 1 - автоматическая эмуляция заголовков браузера (1 - включено, 0 - выключено)

opts.use_proxy

use_proxy: 1 - переопределяет использование прокси для отдельного запроса внутри JS парсера поверх глобального параметра Use proxy (1 - включено, 0 - выключено)

opts.noextraquery

noextraquery: 0 - отключает добавление Extra query string к урлу запроса (1 - включено, 0 - отключено)

opts.save_to_file

save_to_file: file - позволяет скачать файл напрямую на диск, минуя запись в память. Вместо file указывается имя и путь под каким сохранить файл. При использовании этой опции игнорируется все, что связано с data (проверка контента в check_content, response.data будет пустой и т.д.).

opts.bypass_cloudflare

bypass_cloudflare: 0 - автоматический обход JavaScript защиты CloudFlare используя браузер Chrome (1 - включено, 0 - выключено)

Контроль Chrome Headless в этом случае осуществляется настройками парсера bypassCloudFlareChromeMaxPages и bypassCloudFlareChromeHeadless, которые нужно указать в static defaultConf и 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 - позволяет переходить по редиректам, объявленным через HTML мета тег:

<meta http-equiv="refresh" content="time; url=..."/>

opts.redirect_filter

redirect_filter: (hdr) => 1 | 0 - позволяет задать функцию фильтрации перехода по редиректам, если функция возвращает 1, то парсер перейдет по редиректу(учитывая параметр opts.recurse), при возврате 0 переход по редиректам прекратиться:

redirect_filter: (hdr) => {
if (hdr.location.match(/login/))
return 1;
return 0;
}

opts.follow_common_rediects

opts.follow_common_rediects: 0 - определяет переходить ли по стандартным редиректам(например http -> https и/или www.domain.com -> domain.com), если указать 1 то парсер будет переходить по стандартным редиректам без учета параметра opts.recurse

opts.http2

opts.http2: 0 - определяет переходить использовать ли протокол HTTP/2 при выполнении запросов, по умолчанию используется HTTP/1.1

opts.randomize_tls_fingerprint

opts.randomize_tls_fingerprint: 0 - данная опция позволяет обходить бан сайтов по TLS отпечатку (1 - включено, 0 - выключено)

opts.tlsOpts

tlsOpts: { ... } – позволяет передавать настройки для https соединений ​

await this.cookies.*

Работа с cookies для текущего запроса

.getAll()

Получение массива cookies

await this.cookies.getAll();

пример результата получения массива cookies

.setAll(cookie_jar)

Установка cookies, в качестве аргумента должен быть передан хэш с куками

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;
}

пример результата установки массива cookies

.set(host, path, name, value)

await this.cookies.set(host, path, name, value) - установка одиночного cookie

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;
}

пример результата установки одиночного cookie

await this.proxy.*

Работа с прокси

.next()

Сменить прокси на следующий, старый прокси больше не будет использован для текущего запроса

.ban()

Сменить и забанить прокси (необходимо использовать когда сервис блокирует работу по IP), прокси будет забанен на время, указанное в настройках парсера(proxybannedcleanup)

.get()

Получить текущий прокси (последний прокси с которым был сделан запрос)

.set(proxy[, noChange])

await this.proxy.set('http://127.0.0.1:8080', true) - установить прокси для следующего запроса, параметр noChange необязательный, если задан true то прокси не будет меняться между попытками. По умолчанию noChange = false

await this.sessionManager.*

.init(opts)

Для использования сессий в JS парсере сначала нужно инициализировать Менеджер сессий. Делается это с помощью функции init()

async init() {
await this.sessionManager.init({
//здесь можно задать дополнительные параметры
});
}

В this.sessionManager.init() можно использовать следующие параметры:

  • name - необязательный параметр, позволяет переопределить имя парсера, которому принадлежат сессии, по-умолчанию равно имени парсера, в котором происходит инициализация
  • canChangeProxy - необязательный параметр, возможность менять прокси, по-умолчанию равно 1
  • domain - необязательный параметр, указывает искать сессии среди всех сохраненных для этого парсера (если значение не задано), или же только для конкретного домена (необходимо указывать домен с точкой спереди, например .site.com)

Для работы с сессиями существует несколько функций:

.get()

await this.sessionManager.get() - получает новую сессию, необходимо вызывать перед осуществлением запроса

.reset()

await this.sessionManager.reset() - очистка куков и получение новой сессии. Необходимо вызывать, если с текущей сессией запрос не был удачным.

.save()

await this.sessionManager.save() - сохранение удачной сессии либо сохранение произвольных данных в сессии

Пример сохранения произвольных данных и дальнейшего их получения:

async init() {
await this.sessionManager.init({
//здесь можно задать дополнительные параметры
});
}

async parse(set, results) {
this.logger.put("Start scraping query: " + set.query);

let ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)';
let referer = set.query;
let data = 'Some data';

await this.sessionManager.save({ua, referer, data});
let session = await this.sessionManager.get();

this.logger.put("Session: " + JSON.stringify(session));

results.SKIP = 1;
return results;
}

пример сохранения произвольных данных и дальнейшего их получения

Методы запросов (await this.request)

Метод GET

Передать параметры запроса можно напрямую в строке запроса https://a-parser.com/users/?type=staff:

const {success, data, headers} = await this.request('GET', 'https://a-parser.com/users/?type=staff');

Или как объект, где key: value равно param=value:

const {success, data, headers} = await this.request('GET', 'https://a-parser.com/users/', {
type: 'staff'
});

Метод POST

Если используется метод POST, тело запроса можно передать двумя способами:

  • просто перечислив названия переменных и их значения в queryParams. Например:
{
"key": set.query,
"id": 1234,
"type": "text"
}
  • через переменную body в opts. Например:
body: 'key=' + set.query + '&id=1234&type=text'

Если body передается как объект он автоматически преобразуется в форму form-urlencoded, так же если указан body и не указан content-type заголовок, то автоматически присвоится 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
});

Если тело POST запроса строка или буффер, то оно так и передается:

//Запрос со строкой
const string = 'title=foo&body=bar&userId=1';
const {success, data, headers} = await this.request('POST', 'https://jsonplaceholder.typicode.com/posts', {}, {
body: string
});

//Запрос с буффером
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
});

Загрузка файлов

Отправка файла POST запросом с использованием модуля 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()
});

Пример отправки файла в POST запросе с типом контента 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
});