NodeJS adalah lingkungan runtime JavaScript yang dibangun di atas mesin V8 JS yang dikembangkan oleh Google. Yang terpenting, Node.js adalah platform untuk membangun aplikasi web. Seperti JavaScript, ini ideal untuk menyelesaikan tugas web.
Ada beberapa alat pengikis web untuk Node.js: Axios, SuperAgent, Cheerio, dan Puppeteer dengan browser tanpa kepala.
Daftar Isi
Manfaat menggunakan Node.js untuk web scraping
Perusahaan kami menggunakan tumpukan JavaScript + NodeJS + MongoDB di shell Linux untuk web scraping. Tautan penghubungnya adalah NodeJS, yang menawarkan sejumlah keunggulan yang tidak dapat disangkal.
Pertama, NodeJS efisien sebagai lingkungan runtime karena mendukung operasi I/O asinkron. Hal ini mempercepat aplikasi untuk permintaan HTTP dan permintaan basis data di area di mana thread eksekusi utama tidak bergantung pada hasil I/O.
Kedua, NodeJS mendukung transfer data streaming (Stream), yang memudahkan pemrosesan file (atau data) besar bahkan dengan persyaratan sistem minimal.
Ketiga, NodeJS berisi banyak modul bawaan yang mendukung interaksi dengan sistem operasi dan web. Misalnya, FileSystem dan Path untuk prosedur input/output data pada disk sistem, URL untuk mengedit parameter rute dan parameter kueri di URL, Proses dan Proses Anak – untuk mengelola proses sistem operasi yang melayani crawler, serta Utils, debugger, dan sebagainya. .
Keempat, ekosistem NodeJS berisi sejumlah besar paket dari komunitas pengembang yang dapat membantu menyelesaikan hampir semua masalah. Untuk scraping ada perpustakaan seperti Axios, SuperAgent, Cheerio dan Puppeteer. Dan jika Anda ingin mengikis Google menggunakan perpustakaan ini, kami sarankan Anda membaca artikel kami: “Pengikisan web Google dengan Node JS”.
Permintaan HTTP di NodeJS dengan Axios
Axios adalah klien HTTP yang dijanjikan.
Sedikit yang diperlukan dari Axios untuk scraping - sebagian besar permintaan dikirim bersamanya GET
Metode.
Kita gunakan:
const axios = require('axios');
const UserAgent = require('user-agents');
const axios = axios.create({
headers: {
'user-agent': (new UserAgent()).toString(),
'cookie': process.env.COOKIE,
'accept': '*/*',
'accept-encoding': 'gzip, deflate, br',
},
timeout: 10000,
proxy: {
host: process.env.PROXY_HOST,
port: process.env.PROXY_PORT,
auth: {
username: process.env.PROXY_USERNAME,
password: process.env.PROXY_PASSWORD,
}
},
})
Ini satu axios
Instance ini dibuat dengan konfigurasi sampel.
Pustaka agen pengguna menghasilkan nilai aktual untuk header dengan nama yang sama, sehingga menghilangkan kebutuhan untuk memasukkan nilai ini secara manual.
Cookie diterima melalui skrip terpisah, yang tugasnya adalah meluncurkan instance Chromium menggunakan perpustakaan Puppeteer, mengautentikasi situs web target, dan meneruskan nilai cookie yang di-cache oleh browser ke aplikasi.
Dalam contoh ini, properti cookie terikat pada nilai variabel lingkungan process.env.COOKIE
. Ini menyiratkan bahwa cookie sebenarnya ditempatkan di variabel lingkungan, misalnya menggunakan manajer proses pm2.
Meskipun nilai cookie (yang hanya berupa string) dapat diatur langsung pada konfigurasi di atas dengan menyalinnya dari area pengembang browser.
Yang tersisa hanyalah mengirim permintaan http dan mengambil kontennya data
Properti dari Response
Obyek. Ini biasanya HTML, tetapi bisa juga format data lain yang diperlukan.
const yourAsyncFunc = async () => {
const { data } = await axios.get(targetUrl); // data --> <!DOCTYPE html>... and so on
// some code
}
SuperAgent untuk parsing di NodeJS
Sebagai alternatif dari Axios, ada klien HTTP SuperAgent yang ringan. Anda bisa membuat yang sederhana GET
Pertanyaan sebagai berikut:
const superagent = require('superagent');
const yourAsyncFunc = async () => {
const { text } = await superagent.get(targetUrl); // text --> page content
// some code
}
Ia memiliki reputasi yang baik dalam mengembangkan aplikasi web yang menggunakan AJAX. Salah satu fitur SuperAgent adalah metode pipeline untuk mengatur konfigurasi permintaan:
const superagent = require('superagent');
superagent
.get('origin-url/route')
.set('User-Agent': '<some UA>')
.query({ city: 'London' })
.end((err, res) => {
// // Calling the end function will send the request
const { text } = res; // text --> page content
// some other code
});
Pembungkus proksi Superagent memungkinkan Anda menambahkan layanan proksi ke permintaan. SuperAgent juga mendukung async/await
Sintaksis.
Transformasi struktural dengan Cheerio
Cheerio adalah alat penting untuk mengubah konten string HTML menjadi struktur pohon, kemudian melintasi struktur tersebut dan mengekstrak data yang diperlukan.
Scraping terutama menggunakan Load API dan Selectors API.
Sedang memuat:
const cheerio = require('cheerio');
// somewhere inside asynchronous func...
const { data } = await fetchPageContent(url);
const $ = cheerio.load(data); // now we've got cheerio node tree in '$'
Langkah selanjutnya adalah memilih node menggunakan selector dan mengekstrak data yang diperlukan.
Berikut adalah contoh penggunaan cheerio, yang memfilter semua node yang cocok dengan kombinasi penyeleksi ke dalam koleksi dan mengekstrak tautan yang ada di dalamnya:
const urls = $('.catalog li :nth-child(1) a')
.filter((i, el) => $(el).attr('href'))
.map((i, el) => $(el).attr('href'))
.toArray();
// urls --> ('url1', 'url2', ..., 'urlN')
Artinya:
- Temukan elemen dengan
.container
Kelas dalam markup (di tingkat bersarang mana pun); - Pilih semua elemen dengan
li
Tandai (di tingkat bersarang mana pun di dalam.container
); - di masing-masing
li
Elemen, pilih elemen anak pertama; - Di elemen anak pertama, pilih semua elemen dengan
a
tag (di tingkat bersarang mana pun); - Filter hanya ini
a
Elemen yang mengandunghref
Atribut; - ulangi koleksi yang dihasilkan
a
elemen dan ekstrak nilai darihref
Atribut; - Tulis tautan yang diterima ke dalam array JS.
Kode ini hanya berisi 4 baris pendek, namun memiliki kepadatan perintah yang tinggi.
Perlu dicatat bahwa jika Cheerio tidak mengembalikan fragmen konten yang diperlukan, periksa apakah markup HTML yang diterima dari server web benar-benar berisi apa yang diperlukan.
Browser tanpa kepala dengan Dalang
Browser tanpa kepala digunakan untuk mensimulasikan tindakan pengguna secara terprogram pada halaman tanpa mengunduh instance GUI.
Menggunakan browser tanpa kepala menghabiskan sumber daya sistem dengan satu atau lain cara dan meningkatkan waktu proses aplikasi secara keseluruhan. Selain itu, kehati-hatian harus diberikan untuk memastikan bahwa proses dengan instance browser tidak dibiarkan terbuka di sistem, karena pertumbuhannya yang tidak terkendali akan menyebabkan seluruh server mogok.
Dari versi “tanpa kepala”, yang paling umum digunakan adalah Chromium, yang dikelola melalui perpustakaan Puppeteer. Alasan paling umum kemunculannya dalam kode scraper adalah captcha pop-up (atau persyaratan untuk menjalankan beberapa jenis kode JS sebelum memuat). Isi). Browser menerima tugas dari captcha, menunggu solusi dan mengirimkan respons dengan solusi tersebut ke server web. Baru setelah itu kode panggilan menerima HTML untuk diurai.
Terkadang browser tanpa kepala digunakan untuk menerima cookie otorisasi dan, dalam kasus yang sangat jarang, memuat konten dengan mensimulasikan pengguliran mouse.
Menggunakan:
// puppeteer-service.js
const puppeteer = require('puppeteer');
module.exports = async () => {
try {
const browser = await puppeteer.launch({
args: ('--window-size=1920,1080'),
headless: process.env.NODE_ENV === 'PROD', // FALSE means you can see all performance during development
});
const page = await browser.newPage();
await page.setUserAgent('any-user-agent-value');
await page.goto('any-target-url', { waitUntil: ('domcontentloaded') });
await page.waitForSelector('any-selector');
await page.type('input.email-input', username);
await page.type('input.password-input', password);
await page.click('button(value="Log in")');
await page.waitForTimeout(3000);
return page.evaluate(
() => document.querySelector('.pagination').innerHTML,
);
} catch (err) {
// handling error
} finally {
browser && await this.browser.close();
}
}
// somewhere in code
const fetchPagination = require('./puppeteer-service.js');
const $ = cheerio.load(await fetchPagination());
// some useful code next...
Cuplikan kode ini memberikan contoh sederhana dengan metode dasar untuk mengedit halaman Chromium. Pertama, instance browser dan halaman kosong dibuat. Setelah mengatur header 'User-Agent'
Halaman meminta konten di URL yang ditentukan.
Selanjutnya, pastikan konten yang diperlukan telah dimuat (waitForSelector
), masukkan nama pengguna dan kata sandi di bidang izin (type
), klik tombol “Masuk” (click
), atur halaman untuk menunggu selama 3 detik (waitForTimeout
), yang memuat konten pengguna yang berwenang dan akhirnya mengembalikan markup HTML yang dihasilkan dari bagian yang diinginkan dengan penomoran halaman di atas.
Menggunakan Javascripts Async untuk meningkatkan kecepatan
Fitur I/O asinkron yang didukung oleh NodeJS dan JavaScript dapat digunakan untuk mempercepat aplikasi. Ada dua kondisi: inti prosesor bebas di mana seseorang dapat menjalankan proses asinkron secara terpisah dari thread eksekusi utama, dan independensi thread utama dari hasil operasi asinkron.
Tidak mungkin menggunakan permintaan HTTP yang asinkron untuk mempercepat waktu berjalannya proses, karena kelanjutan thread utama secara langsung bergantung pada penerimaan respons terhadap permintaan HTTP.
Hal yang sama berlaku untuk operasi membaca informasi dari database. Sampai data diterima, thread utama scraper tidak ada hubungannya.
Tapi Anda bisa menulis data ke database secara terpisah dari mainstream. Misalkan suatu saat dalam kode suatu objek diterima untuk ditulis ke database. Bergantung pada hasil rekaman, tindakan lebih lanjut perlu dicabangkan dan baru kemudian dilanjutkan ke iterasi berikutnya.
Daripada menunggu hasil pada titik di mana fungsi tulis data dipanggil, Anda dapat membuat proses baru dan menugaskan pekerjaan ini ke dalamnya. Thread utama bisa langsung dilanjutkan.
// in some place of code...
// we've got the object for inserting to database
const data = {
product: 'Jacket',
price: 55,
currency: 'EUR',
};
const { fork } = require('child_process');
// launch the other bind process
const child = fork('path-to-write-data.js');
child.send(JSON.stringify(data));
// do the next code iteration...
Menerapkan kode untuk proses anak dalam file terpisah:
// write-data.js
// inside acync function
process.on('message', (data) => {
const result = await db.writeDataFunc(JSON.parse(data));
if (result) {
// do some impotant things
} else {
// do other impotant things
}
})
Secara umum, fitur metode fork adalah memungkinkan proses induk dan anak untuk bertukar pesan satu sama lain. Namun, untuk tujuan demonstrasi, contoh ini mendelegasikan pekerjaan ke proses anak tanpa memberi tahu proses induk, sehingga memungkinkan proses eksekusi berikutnya berjalan secara paralel dengan proses anak.
Hindari penyumbatan saat mengikis
Sebagian besar situs web target tempat data dikumpulkan secara aktif menolak proses ini. Tentu saja, para pengembang sumber daya ini mengetahui cara kerjanya. Artinya, menyetel header yang benar saja sering kali tidak cukup untuk meng-crawl seluruh situs web.
Server web dapat membatasi distribusi konten setelah sejumlah permintaan dari IP per unit waktu tercapai. Mereka dapat membatasi akses jika mereka menentukan bahwa permintaan tersebut berasal dari proxy pusat data.
Mereka dapat mengirimkan captcha untuk mengatasinya jika lokasi alamat IP pelanggan tampaknya tidak dapat diandalkan. Atau mereka mungkin menawarkan untuk menjalankan kode JS pada halaman sebelum konten utama dimuat.
Tujuannya adalah menggunakan metrik server web untuk memberikan kesan bahwa permintaan tersebut berasal dari browser pengguna dan bukan bot. Jika terlihat cukup realistis, server akan mengirimkan konten sedemikian rupa sehingga tidak membatasi pengguna sebenarnya dan bukan bot secara tidak sengaja.
Jika server web yang pilih-pilih muncul, masalahnya diselesaikan dengan menemukan alamat proxy dengan benar dan secara bertahap meningkatkan kualitasnya hingga alamat pribadi. Kerugian dari keputusan ini adalah meningkatnya biaya pengumpulan data.
Kesimpulan dan temuan
NodeJS dan JavaScript sangat bagus untuk pengumpulan data di semua bagian proses. Jika tumpukan atau eksekutor diperlukan, NodeJS, JavaScript, dan MongoDB adalah salah satu opsi terbaik. Penggunaan NodeJS tidak hanya memungkinkan penyelesaian semua kemungkinan masalah di bidang scraping, tetapi juga memastikan perlindungan dan keandalan ekstraksi data. Dan dengan menggunakan browser tanpa kepala, perilaku pengguna ditiru.