Ana içeriğe atla

HTTP istekleri (+çerezlerle çalışma, proxy, oturumlar)

Temel sınıf yöntemleri

Bir web sayfasından veri toplamak için bir HTTP isteği yürütmeniz gerekir. A-Parser'ın JavaScript API v2 sürümünde, belirtilen yöntem argümanlarına bağlı olarak yanıt olarak bir JSON nesnesi döndüren, kullanımı kolay bir HTTP isteği yürütme yöntemi uygulanmıştır. Aşağıda şunları öğreneceksiniz: HTTP isteğinin nasıl yapıldığı, yöntemin hangi argümanlara ve seçeneklere sahip olduğu, belirtilen seçeneklerin sonuçları, bir HTTP isteğinin başarı koşulunun nasıl belirtileceği ve daha fazlası. Bu yöntemler BaseParser sınıfından miras alınır ve özel veri kazıyıcılar oluşturmak için temel teşkil eder. İstek üzerine HTTP yanıtı alma, argüman olarak şunlar belirtilir:

  • method - istek yöntemi (GET, POST...)

  • url - istek için bağlantı

  • queryParams - get parametrelerini içeren bir hash veya post isteği gövdesini içeren bir hash HTTP istekleri veya Oturum Yöneticisi yardımıyla başka bir iş parçacığı tarafından yürütülmek üzere kaydedin.

Bu yöntemler BaseParser sınıfından miras alınır ve özel veri kazıyıcılar oluşturmak için temel teşkil eder

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

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

İsteğe göre HTTP yanıtı alma, argüman olarak şunlar belirtilir:

  • method - istek yöntemi (GET, POST...)
  • url - istek için bağlantı
  • queryParams - get parametrelerini içeren hash veya post isteği gövdesini içeren hash
  • opts - istek seçeneklerini içeren hash

opts.check_content

check_content: [ koşul1, koşul2, ...] - alınan içeriği kontrol etmek için koşullar dizisi, eğer kontrol başarısız olursa, istek başka bir proxy ile tekrarlanacaktır.

Özellikler:

  • koşul olarak dizelerin kullanılması (dize eşleşmesine göre arama)
  • koşul olarak düzenli ifadelerin (regex) kullanılması
  • verilerin ve yanıt başlıklarının iletildiği özel kontrol fonksiyonlarının kullanılması
  • aynı anda birkaç farklı koşul türü tanımlanabilir
  • mantıksal değilleme için koşulu bir dizi içine yerleştirin, yani check_content: ['xxxx', [/yyyy/]] ifadesi, isteğin başarılı sayılması için alınan verilerin xxxx alt dizesini içermesi ve aynı zamanda /yyyy/ düzenli ifadesinin sayfada hiçbir eşleşme bulmaması gerektiği anlamına gelir

İsteğin başarılı olması için dizide belirtilen tüm kontrollerin geçmesi gerekir

Örnek (yorum satırlarında isteğin başarılı sayılması için ne gerektiği belirtilmiştir):

let response = await this.request('GET', set.query, {}, {
check_content: [
/<\/html>|<\/body>/, // alınan sayfada bu düzenli ifade çalışmalıdır
['XXXX'], // alınan sayfada bu alt dize bulunmamalıdır
'</html>', // alınan sayfada bu alt dize bulunmalıdır
(data, hdr) => {
return hdr.Status == 200 && data.length > 100;
} // bu fonksiyon true döndürmelidir
]
});

opts.decode

decode: 'auto-html' - kodlamanın otomatik tespiti ve utf8 formatına dönüştürülmesi

Olası değerler:

  • auto-html - başlıklara, meta etiketlerine ve sayfa içeriğine göre (önerilen en uygun seçenek)
  • utf8 - belgenin utf8 kodlamasında olduğunu belirtir
  • <encoding> - diğer herhangi bir kodlama

opts.headers

headers: { ... } - başlıkları içeren hash, başlık adı küçük harfle belirtilir, cookie dahil edilebilir.

Örnek:

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', ...] - başlıkların sıralama düzenini geçersiz kılmaya olanak tanır

opts.onlyheaders

onlyheaders: 0 - data okumasını belirler; etkinse (1), yalnızca başlıkları alır

opts.recurse

recurse: N - yönlendirmeler üzerinden geçişlerin maksimum sayısıdır, varsayılan olarak 7'dir, geçişi devre dışı bırakmak için 0 kullanın yönlendirmelere

opts.proxyretries

proxyretries: N - istek yürütme deneme sayısı, varsayılan olarak veri kazıyıcı ayarlarından alınır

opts.parsecodes

parsecodes: { ... } - veri kazıyıcının başarılı sayacağı HTTP yanıt kodlarının listesi, varsayılan olarak veri kazıyıcı ayarlarından alınır. Eğer '*': 1 belirtilirse tüm yanıtlar başarılı sayılır.

Örnek:

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

opts.timeout

timeout: N - saniye cinsinden yanıt zaman aşımı, varsayılan olarak veri kazıyıcı ayarlarından alınır

opts.do_gzip

do_gzip: 1 - sıkıştırma (gzip/deflate/br) kullanılıp kullanılmayacağını belirler, varsayılan olarak açıktır (1), kapatmak için 0 değeri atanmalıdır

opts.max_size

max_size: N - bayt cinsinden maksimum yanıt boyutu, varsayılan olarak veri kazıyıcı ayarlarından alınır

opts.cookie_jar

cookie_jar: { ... } - çerezleri içeren hash. Örnek hash:

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

opts.attempt

attempt: N - mevcut deneme numarasını belirtir, bu parametre kullanıldığında bu istek için yerleşik deneme işleyicisi yoksayılır

opts.browser

browser: 1 - tarayıcı başlıklarının otomatik emülasyonu (1 - açık, 0 - kapalı)

opts.use_proxy

use_proxy: 1 - küresel Use proxy parametresinin üzerinde, JS veri kazıyıcı içindeki münferit bir istek için proxy kullanımını geçersiz kılar (1 - açık, 0 - kapalı)

opts.noextraquery

noextraquery: 0 - istek URL'sine Extra query string eklenmesini devre dışı bırakır (1 - açık, 0 - kapalı)

opts.save_to_file

save_to_file: file - dosyayı doğrudan diske indirmenizi sağlar, belleğe yazmadan. file yerine dosyanın kaydedileceği yol. Bu seçenek kullanıldığında, data ile ilgili her şey (içerik kontrolü içerik kontrolü yapılmaz, response.data boş olur vb.)

opts.bypass_cloudflare

bypass_cloudflare: 0 - Chrome tarayıcısını kullanarak CloudFlare JavaScript korumasını otomatik olarak atlama (1 - açık, 0 - kapalı)

Bu durumda Chrome Headless kontrolü, veri kazıyıcının bypassCloudFlareChromeMaxPages ayarlarıyla gerçekleştirilir ve bypassCloudFlareChromeHeadless ayarlarıyla yapılır; bunlar static defaultConf ve static editableConf içine belirtilmelidir:

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 etiketi aracılığıyla bildirilen yönlendirmelerin takip edilmesini sağlar:

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

opts.redirect_filter

redirect_filter: (hdr) => 1 | 0 - yönlendirme takibi için bir filtreleme fonksiyonu tanımlamaya olanak tanır, eğer fonksiyon 1 döndürürse, veri kazıyıcı yönlendirmeyi takip eder (opts.recurse parametresini dikkate alarak), 0 döndürürse yönlendirme takibi durur:

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

opts.follow_common_rediects

opts.follow_common_rediects: 0 - standart yönlendirmelere geçilip geçilmeyeceğini belirler (örneğin http -> https ve/veya www.domain.com -> domain.com), 1 belirtilirse veri kazıyıcı opts.recurse parametresinin

opts.http2

opts.http2: 0 - istekleri gerçekleştirirken HTTP/2 protokolünün kullanılıp kullanılmayacağını belirler, varsayılan olarak HTTP/1.1 kullanılır

opts.randomize_tls_fingerprint

opts.randomize_tls_fingerprint: 0 - bu seçenek, sitelerin TLS parmak izine göre engellemesini aşmaya olanak tanır (1 - açık, 0 - kapalı)

opts.tlsOpts

tlsOpts: { ... } – şunlar için ayarları https bağlantılarına iletmeyi sağlar ​

await this.cookies.*

Mevcut istek için çerezlerle çalışma

.getAll()

Çerez dizisini alma

await this.cookies.getAll();
Çerez dizisi almanın sonuç örneği

.setAll(cookie_jar)

Çerezleri ayarlama, argüman olarak çerezleri içeren bir hash iletilmelidir

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;
}
Çerez dizisi ayarlamanın sonuç örneği

.set(host, path, name, value)

await this.cookies.set(host, path, name, value) - tek bir çerez ayarlama.

Çerezin görünürlük alanı doğrudan belirtilen alan adı formatına bağlıdır, bu nedenle host kısmında ana bilgisayarın önündeki nokta dikkate alınır:

  • eğer nokta belirtilmişse (this.cookies.set('.domain.com', ...)), çerez tüm alt alan adları için kullanılacaktır (örneğin a.domain.com, b.a.domain.com)
  • eğer ana bilgisayar önünde nokta olmadan belirtilmişse (this.cookies.set('site.com', ...)), çerez kesinlikle belirtilen ana bilgisayar için kullanılacaktır (host-only cookie) ve alt alan adlarına iletilmez
bilgi

Bu ayrım kritik öneme sahiptir, çünkü noktalı ve noktasız çerezlerin aynı anda var olması bunların yinelenmesine ve sitenin öngörülemez çalışmasına neden olabilir. Doğru emülasyon için hedef sitenin çerezleri tam olarak nasıl ayarladığını (Domain özniteliği ile mi yoksa onsuz mu) her zaman kontrol edin ve uygun formatı kullanın.

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;
}
Tekil bir çerez ayarlamanın sonuç örneği

await this.proxy.*

Proxy ile çalışma

.next()

Proxy'yi bir sonrakine değiştir, eski proxy mevcut istek için artık kullanılmayacaktır

.ban()

Proxy'yi değiştir ve yasakla (servis IP üzerinden çalışmayı engellediğinde kullanılmalıdır), proxy veri kazıyıcı ayarlarında belirtilen süre boyunca (proxybannedcleanup) yasaklanacaktır

.get()

Mevcut proxy'yi al (isteğin yapıldığı son proxy)

.set(proxy, noChange?)

await this.proxy.set('http://127.0.0.1:8080', true) - sonraki istek için proxy ayarla. noChange parametresi isteğe bağlıdır, true olarak verilirse proxy denemeler arasında değişmez. Varsayılan olarak noChange = false

await this.sessionManager.*

Oturumlarla çalışma yöntemleri. Her oturum mutlaka kullanılan proxy'yi ve çerezleri saklar. Ayrıca isteğe bağlı olarak rastgele veriler de kaydedilebilir. JS veri kazıyıcıda oturumları kullanmak için önce mutlaka Oturum Yöneticisi'ni başlatmanız gerekir. Bu, init() içinde await this.sessionManagerinit() yöntemiyle yapılır

.init(opts?)

Oturum Yöneticisi'nin başlatılması. Argüman olarak ek parametreler içeren bir nesne (opts) iletilebilir (tüm parametreler isteğe bağlıdır):

  • name - oturumların ait olduğu veri kazıyıcı adını geçersiz kılmaya olanak tanır, varsayılan olarak başlatmanın yapıldığı veri kazıyıcı adına eşittir
  • waitForSession - veri kazıyıcıya oturum görünene kadar beklemesini söyler (bu, yalnızca birden fazla görev çalışırken geçerlidir; örneğin biri oturum üretir, diğeri onları kullanır), yani .get() ve .reset() her zaman oturumu bekler
  • domain - bu veri kazıyıcı için kaydedilmiş tüm oturumlar arasından arama yapılmasını belirtir (değer belirtilmemişse) veya yalnızca belirli bir alan adı için arama yapılmasını belirtir (başında nokta olan alan adıyla yazılmalıdır, örneğin .site.com)
  • sessionsKey - oturum deposunun adını elle belirlemeyi sağlar; belirtilmezse ad, name (veya name belirtilmemişse veri kazıyıcının adı), alan adı ve proxy denetleyicisi temel alınarak otomatik oluşturulur
  • expire - oturumun ömür süresini dakika cinsinden belirler, varsayılan olarak sınırsızdır

Kullanım örneği:

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

.get(opts?)

Yeni bir oturum alma, istek gerçekleştirilmeden önce (ilk denemeden önce) çağrılmalıdır. Oturumda saklanan rastgele verileri içeren bir nesne döndürür. Argüman olarak ek parametreler içeren bir nesne (opts) iletilebilir (tüm parametreler isteğe bağlıdır):

  • waitTimeout - oturumun görünmesi için kaç dakika bekleneceğini belirtme imkânı sağlar, .init() içindeki waitForSession parametresinden bağımsız çalışır (onu yoksayar), süre dolduğunda boş bir oturum kullanılır
  • tag - belirtilen etikete sahip oturumu alma, örneğin oturumları alındıkları alan adlarına bağlamak için alan adı kullanılabilir

Kullanım örneği:

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

.reset(opts?)

Çerezleri temizleme ve yeni bir oturum alma. Mevcut oturumla istek başarılı olmadıysa kullanılmalıdır. Oturumda saklanan rastgele verileri içeren bir nesne döndürür. Argüman olarak ek parametreler içeren bir nesne (opts) iletilebilir (tüm parametreler isteğe bağlıdır):

  • waitTimeout - oturumun görünmesi için kaç dakika bekleneceğini belirtme imkânı sağlar, .init() içindeki waitForSession parametresinden bağımsız çalışır (onu yoksayar), süre dolduğunda boş bir oturum kullanılır
  • tag - belirtilen etikete sahip oturumu alma, örneğin oturumları alındıkları alan adlarına bağlamak için alan adı kullanılabilir

Kullanım örneği:

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

.save(sessionOpts?, saveOpts?)

Oturumda rastgele verileri saklama imkanıyla başarılı bir oturumu kaydetme. 2 isteğe bağlı argümanı destekler:

  • sessionOpts - oturumda saklanacak rastgele veriler; sayı, dize, dizi veya nesne olabilir
  • saveOpts - oturum kaydetme parametrelerini içeren nesne:
    • multiply - isteğe bağlı parametre, oturumu çoğaltmaya olanak tanır, değer olarak bir sayı belirtilmelidir
    • tag - isteğe bağlı parametre, kaydedilen oturum için bir etiket belirler, örneğin oturumları alındıkları alan adlarına bağlamak için alan adı kullanılabilir

Kullanım örneği:

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

.count()

Mevcut Oturum Yöneticisi için oturum sayısını döndürür

Kullanım örneği:

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

.removeById(sessionId)

Belirtilen id'ye sahip tüm oturumları siler. Silinen oturum sayısını döndürür. Mevcut oturumun id'si this.sessionId değişkeninde bulunur Kullanım örneği:

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

Oturum Yöneticisi kullanımına dair kapsamlı örnek

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;
}
Rastgele verileri kaydetme ve daha sonra bunları alma örneği

İstek yöntemleri await this.request

GET metodu

İstek parametreleri doğrudan istek dizesinde iletilebilir https://a-parser.com/users/?type=staff:

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

Ya da queryParams içinde nesne olarak, burada key: value param=value'e eşittir:

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

POST metodu

Eğer POST yöntemi kullanılıyorsa, istek gövdesi iki şekilde iletilebilir:

  • Değişken adlarını ve değerlerini queryParams içinde listeleyin, örneğin:

    {
    "key": set.query,
    "id": 1234,
    "type": "text"
    }
  • Bunları opts.body içinde listeleyin, örneğin:

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

Eğer istek gövdesi nesne olarak aktarılırsa otomatik olarak form-urlencoded biçimine dönüştürülür; ayrıca body belirtilmiş ve content-type başlığı belirtilmemişse, otomatik olarak content-type: application/x-www-form-urlencoded atanır:

const { success, data, headers } = await this.request('POST', 'https://jsonplaceholder.typicode.com/posts', {
title: 'foo,',
body: 'bar',
userId: 1
});

Eğer POST isteğinin gövdesi bir dize veya tampon (buffer) ise, olduğu gibi iletilir:

// dize ile istek
const string = 'title=foo&body=bar&userId=1';
const { success, data, headers } = await this.request('POST', 'https://jsonplaceholder.typicode.com/posts', {}, {
body: string
});

// tampon ile istek
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
});

Dosya yükleme

form-data modülünü kullanarak POST isteği ile dosya gönderme:

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 içerik türü ile bir POST isteğinde dosya gönderme örneği:

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