メインコンテンツへスキップ

HTTPリクエスト(+クッキー、プロキシ、セッションの操作)

基本クラスのメソッド

ウェブページからデータを収集するには、HTTPリクエストを実行する必要があります。A-Parserの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 - リクエスト先の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 を使用します。 proxyretries: N - リクエストの試行回数。デフォルトはスクレイパーの設定から取得されます。

opts.proxyretries

proxyretries: N - ## リクエストメソッド await this.request{#query-methods}

opts.parsecodes

parsecodes: { ... } - リクエストパラメータは、https://a-parser.com/users/?type=staff のようにリクエスト文字列に直接渡すことができます: POST メソッドを使用する場合、リクエストボディは2つの方法で渡すことができます:

  • opts.body に列挙する。例:
parsecodes: {
200: 1,
403: 1,
500: 1
}

opts.timeout

timeout: N - form-data モジュールを使用した POST リクエストによるファイルの送信:

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 - グローバルパラメータの Use proxy を上書きして、JSスクレイパー内の個別のリクエストに対してプロキシの使用を再定義します (1 - 有効、0 - 無効)

opts.noextraquery

noextraquery: 0 - リクエストURLへの Extra query string の追加を無効にします(1 - 有効、0 - 無効)

opts.save_to_file

save_to_file: file - メモリへの書き込みを介さず、ファイルを直接ディスクにダウンロードできます。file の代わりにファイル名と ファイルを保存するパス。このオプションを使用すると、dataに関連するすべて(コンテンツのチェック 実行されず、response.data は空になりますなど)

opts.bypass_cloudflare

bypass_cloudflare: 0 - Chromeブラウザを使用してCloudFlareのJavaScript保護を自動的に回避します(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.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.*

現在のリクエストのクッキー操作

.getAll()

クッキー配列の取得

await this.cookies.getAll();
クッキー配列取得結果の例

.setAll(cookie_jar)

クッキーの設定。引数としてクッキーのハッシュを渡す必要があります

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) - 単一のクッキーを設定します。

クッキーのスコープは指定されたドメインの形式に直接依存するため、host ではホストの前のドットの有無が考慮されます:

  • ドットが指定されている場合(this.cookies.set('.domain.com', ...))、クッキーはすべてのサブドメイン(例: a.domain.com, b.a.domain.com)で使用されます
  • ホストの前にドットがない場合(this.cookies.set('site.com', ...))、クッキーは指定されたホストに対してのみ厳密に使用され(host-only cookie)、サブドメインには送信されません
情報

ドットありとドットなしのクッキーが同時に存在すると、重複やサイトの予期しない動作を引き起こす可能性があるため、この違いは非常に重要です。正確なエミュレーションのために、ターゲットサイトがどのようにクッキーを設定しているか(Domain属性の有無)を常に確認し、適切な形式を使用してください。

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.*

プロキシ操作

.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.*

セッション操作メソッド。各セッションには、使用されたプロキシとクッキーが必ず保存されます。また、任意のデータを追加で保存することも可能です。 JSスクレイパーでセッションを使用するには、まずセッションマネージャーを初期化する必要があります。これは init() 内で await this.sessionManagerinit() メソッドを使用して行います

.init(opts?)

セッションマネージャーの初期化。引数として追加パラメータを持つオブジェクト(opts)を渡すことができます(すべてのパラメータは任意です):

  • name - セッションが属するスクレイパーの名前を再定義できます。デフォルトは初期化が行われるスクレイパーの名前です
  • waitForSession - セッションが現れるまで待機するようにスクレイパーに指示します(これは複数のタスクが動作している場合、例えば一方がセッションを生成し、もう一方がそれを使用する場合に有効です)。つまり、.get().reset() は常にセッションを待ちます
  • domain - このスクレイパーに保存されているすべてのセッションから検索するか(値が指定されていない場合)、特定のドメインのみを検索するか(ドメインの前にドットを付けて指定する必要があります。例: .site.com)を指定します
  • sessionsKey - セッションストレージの名前を手動で設定できます。設定されていない場合、名前は name(または name が設定されていない場合はスクレイパー名)、ドメイン、およびプロキシチェッカーに基づいて自動的に生成されます
  • expire - セッションの有効期間を分単位で設定します。デフォルトは無制限です

使用例:

async init() {
await this.sessionManager.init({
name: 'JS::test',
expire: 15 * 60
});
}

.get(opts?)

新しいセッションを取得します。リクエストを実行する前(最初の試行の前)に呼び出す必要があります。セッションに保存された任意のデータを含むオブジェクトを返します。引数として追加パラメータを持つオブジェクト(opts)を渡すことができます(すべてのパラメータは任意です):

  • waitTimeout - セッションが現れるまで何分待機するかを指定できます。.init()waitForSession パラメータとは独立して動作し(それを無視します)、タイムアウト後は空のセッションが使用されます
  • tag - 指定されたタグを持つセッションを取得します。例えば、セッションを取得元のドメインに関連付けるためにドメイン名を使用できます

使用例:

await this.sessionManager.get({
waitTimeout: 10,
tag: 'test session'
})

.reset(opts?)

クッキーをクリアして新しいセッションを取得します。現在のセッションでのリクエストが成功しなかった場合に使用する必要があります。セッションに保存された任意のデータを含むオブジェクトを返します。引数として追加パラメータを持つオブジェクト(opts)を渡すことができます(すべてのパラメータは任意です):

  • waitTimeout - セッションが現れるまで何分待機するかを指定できます。.init()waitForSession パラメータとは独立して動作し(それを無視します)、タイムアウト後は空のセッションが使用されます
  • tag - 指定されたタグを持つセッションを取得します。例えば、セッションを取得元のドメインに関連付けるためにドメイン名を使用できます

使用例:

await this.sessionManager.reset({
waitTimeout: 5,
tag: 'test session'
})

.save(sessionOpts?, saveOpts?)

成功したセッションを保存し、任意のデータをセッションに保存できます。2つの任意引数をサポートします:

  • sessionOpts - セッションに保存する任意のデータ。数値、文字列、配列、またはオブジェクトを指定できます
  • saveOpts - セッション保存パラメータを持つオブジェクト:
    • multiply - 任意パラメータ。セッションを複製できます。値として数値を指定します
    • tag - 任意パラメータ。保存するセッションのタグを設定します。例えば、セッションを取得元のドメインに関連付けるためにドメイン名を使用できます

使用例:

await this.sessionManager.save('some data here', {
multiply: 3,
tag: 'test session'
})

.count()

現在のセッションマネージャーのセッション数を返します

使用例:

let sesCount = await this.sessionManager.count();

.removeById(sessionId)

指定された id を持つすべてのセッションを削除します。削除されたセッションの数を返します。現在のセッションの id は変数 this.sessionId に含まれています 使用例:

const removedCount = await this.sessionManager.removeById(this.sessionId);

セッションマネージャーの使用に関する包括的な例

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;
}
任意データの保存とその後の取得の例

リクエストメソッド 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');

または、queryParams 内のオブジェクトとして渡すこともできます。ここで key: valueparam=value に相当します:

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

POSTメソッド

POST メソッドを使用する場合、リクエストボディは2つの方法で渡すことができます:

  • queryParams 内に変数名とその値を列挙します。例:

    {
    "key": set.query,
    "id": 1234,
    "type": "text"
    }
  • opts.body 内に列挙します。例:

    body: 'key=' + set.query + '&id=1234&type=text'

リクエストボディ がオブジェクトとして渡される場合、自動的に 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
});

ファイルのアップロード

form-data モジュールを使用した POST リクエストによるファイルの送信:

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

コンテンツタイプ multipart/form-data を使用した POST リクエストでのファイル送信例:

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