
Dalam artikel sebelumnya , menggunakan parsing scrapbooking, saya mengumpulkan peringkat film dari situs IMDB dan Kinopoisk dan membandingkannya. Repositori di Github .
Kode melakukan tugasnya dengan baik, tetapi memo sering digunakan untuk "mengikis" bukan beberapa halaman, tetapi beberapa tiga ribu, dan kode dari artikel sebelumnya tidak cocok untuk goresan "besar" seperti itu. Lebih tepatnya, itu tidak akan optimal. Pada prinsipnya, praktis tidak ada yang mencegah Anda menggunakannya untuk merayapi ribuan halaman. Praktis, karena Anda tidak punya banyak waktu

Ketika saya memutuskan untuk menggunakan scraping_imdb.R untuk menjelajah 1000 halaman
Optimasi kode. Satu penggunaan fungsi read_html
Dalam artikel ini, 100 tautan ke halaman-halaman toko buku Labyrinth akan digunakan untuk memeriksa operasi dan kecepatan kode.
Perubahan eksplisit yang dapat mempercepat proses adalah penggunaan fungsi kode "paling lambat" - read_html
. Biarkan saya mengingatkan Anda bahwa dia "membaca" halaman HTML. Dalam versi pertama kode untuk situs film, saya menjalankan read_html
setiap kali saya perlu mendapatkan nilai (nama film, tahun, genre, peringkat). Sekarang jejak "rasa malu" ini telah dihapus dari GitHuba, tetapi ternyata sudah. Tidak ada artinya dalam hal ini, karena variabel yang dibuat menggunakan read_html
berisi informasi tentang seluruh halaman dan untuk mendapatkan data yang berbeda dari itu, cukup untuk html_nodes
variabel ini ke fungsi html_nodes
dan tidak mulai membaca HTML setiap saat. Sehingga Anda dapat menghemat waktu secara proporsional dengan jumlah nilai yang ingin Anda dapatkan. Dari Labyrinth, saya mendapatkan tujuh nilai, masing-masing, kode yang hanya menggunakan satu pembacaan halaman HTML akan bekerja sekitar tujuh kali lebih cepat. Tidak buruk! Tetapi sebelum saya "mempercepat" lagi, saya akan ngelantur dan berbicara tentang hal-hal menarik yang muncul ketika mengikis dari situs web Labyrinth.
Fitur pengikisan halaman di Labirin
Pada bagian ini, saya tidak akan menyentuh prosedur untuk mendapatkan dan menghapus data yang disebutkan dalam artikel sebelumnya. Saya hanya akan menyebutkan momen-momen yang pertama kali saya temui ketika menulis kode untuk scrapbooking toko buku.
Pertama, perlu disebutkan strukturnya. Dia sangat tidak nyaman. Sebaliknya, misalnya, dari situs web Read-Cities, bagian dari genre dengan "filter kosong" hanya memberikan 17 halaman. Tentu saja, semua 8011 buku dalam genre "Prosa Asing Kontemporer" tidak cocok untuk mereka.
Oleh karena itu, saya tidak menemukan sesuatu yang lebih baik daripada berkeliling di https://www.labirint.ru/books/ **** tautan dengan patung sederhana. Terus terang, metode ini bukan yang terbaik (jika hanya karena sebagian besar buku "kuno" tidak memiliki informasi kecuali nama dan karena itu praktis tidak berguna), jadi jika ada yang menawarkan solusi yang lebih elegan, saya akan senang. Tetapi saya menemukan bahwa di bawah angka pertama yang bangga di situs web Labyrinth adalah sebuah buku berjudul "Cara Membuat Moonshine" . Sayangnya, sudah tidak mungkin untuk membeli gudang pengetahuan ini.
Semua alamat selama pencacahan dapat dibagi menjadi dua jenis:
- Halaman yang ada
- Halaman yang tidak ada
Halaman yang ada, pada gilirannya, dapat dibagi menjadi dua bagian:
- Halaman yang berisi semua informasi yang diperlukan
- Halaman yang tidak mengandung semua informasi yang diperlukan
Saya berakhir dengan tabel data dengan tujuh kolom:
- ISBN - Nomor Buku ISBN
- PRICE - harga buku
- NAME - judul buku
- PENULIS - penulis buku
- PUBLISHER - penerbit
- TAHUN - tahun publikasi
- HALAMAN - jumlah halaman
Semuanya jelas dengan halaman dengan informasi lengkap, mereka tidak memerlukan perubahan apa pun dibandingkan dengan kode untuk situs film.
Adapun halaman di mana beberapa data tidak tersedia, tidak begitu sederhana dengan mereka. Pencarian di halaman hanya akan mengembalikan nilai-nilai yang ditemukan dan panjang output akan berkurang dengan jumlah elemen yang tidak akan ditemukan. Ini akan menghancurkan seluruh struktur. Untuk menghindari ini, konstruksi if ... else ditambahkan ke setiap argumen, yang memperkirakan panjang vektor yang diperoleh setelah menggunakan fungsi html_nodes
dan jika itu nol, ia mengembalikan NA
untuk menghindari nilai bias.
PUBLISHER <- unlist(lapply(list_html, function(n){ publishing <- if(n != "NA") { publishing_html <- html_nodes(n, ".publisher a") publishing <- if(length(publishing_html) == 0){ NA } else { publishing <- html_text(publishing_html) } } else { NA } }))
Tapi seperti yang Anda bisa perhatikan di sini sebanyak dua jika dan sebanyak dua lainnya. Hanya "internal" if..esle yang relevan dengan solusi untuk masalah yang dijelaskan di atas. Eksterior menyelesaikan masalah dengan halaman yang tidak ada.
Halaman yang tidak memiliki masalah besar. Jika nilai digeser pada halaman dengan data yang hilang, maka ketika input read_html
halaman yang tidak ada, fungsi akan read_html
kesalahan dan kode akan berhenti dieksekusi. Karena entah bagaimana tidak mungkin mendeteksi halaman seperti itu di muka, perlu untuk memastikan bahwa kesalahan tidak menghentikan seluruh proses.
Fungsi possibly
dari paket yang possibly
akan membantu kami dengan ini. Arti fungsi possibly
(selain possibly
quietly
dan safely
) adalah untuk mengganti hasil cetak efek samping (misalnya, kesalahan) dengan nilai yang sesuai dengan kita. possibly
memiliki struktur yang possibly(.f, otherwise)
dan jika kesalahan terjadi dalam kode, alih-alih menghentikan eksekusi, ia menggunakan nilai default (jika tidak). Dalam kasus kami, tampilannya seperti ini:
book_html <- possibly(read_html, "NA")(n)
n adalah daftar alamat halaman situs yang kami memo. Pada output, kita mendapatkan daftar panjang n, di mana elemen dari halaman yang ada akan berada dalam bentuk "normal" untuk melakukan fungsi read_html
, dan elemen dari halaman yang tidak ada akan terdiri dari vektor karakter "NA". Harap perhatikan bahwa nilai default harus berupa vektor karakter, karena di masa mendatang kami akan merujuknya. Jika kita hanya menulis NA
, seperti pada bagian kode PUBLISHER, ini tidak akan mungkin. Untuk menghindari kebingungan, Anda dapat mengubah nilai sebaliknya dari NA ke yang lain.
Dan sekarang kembali ke kode untuk mendapatkan nama penerbit. Eksternal jika ... diperlukan untuk tujuan yang sama seperti internal, tetapi sehubungan dengan halaman yang tidak ada. Jika variabel book_html
sama dengan "NA", maka masing-masing nilai "scraped" juga sama dengan NA
(di sini Anda sudah bisa menggunakan NA
"asli", daripada penipu simbolis). Jadi pada akhirnya, kita mendapatkan tabel dari formulir berikut:
Sekarang kembali dengan percepatan proses pengikisan.
Komputasi paralel dalam perbandingan R. Kecepatan dan jebakan saat menggunakan fungsi read_html
Secara default, semua perhitungan dalam R dilakukan pada inti prosesor yang sama. Dan sementara inti yang malang ini berkeringat, "mengorek" data dari ribuan halaman untuk kami, kawan-kawan kami yang lain "mendinginkan" dengan melakukan beberapa tugas lain. Menggunakan komputasi paralel membantu menarik semua inti prosesor untuk memproses / menerima data, yang mempercepat proses.
Saya tidak akan masuk jauh ke dalam desain komputasi paralel pada R, Anda dapat membaca lebih lanjut tentang mereka, misalnya di sini . Cara saya memahami paralelisme pada R adalah membuat salinan R dalam kelompok terpisah sesuai dengan jumlah kernel yang ditunjukkan yang berinteraksi satu sama lain melalui soket .
Saya akan memberi tahu Anda tentang kesalahan yang saya buat saat menggunakan komputasi paralel. Awalnya, rencana saya adalah ini: menggunakan komputasi paralel, saya mendapatkan daftar 100 halaman "baca" read_html
, dan kemudian dalam mode normal saya hanya mendapatkan data yang saya butuhkan. Pada awalnya semuanya berjalan dengan baik: Saya mendapat daftar, menghabiskan waktu jauh lebih sedikit daripada dalam mode normal R. Tapi hanya ketika saya mencoba berinteraksi dengan daftar ini, saya menerima kesalahan:
Error: external pointer is not valid
Akibatnya, saya menyadari apa masalahnya, melihat contoh-contoh di Internet, dan setelah itu, menurut hukum kekejaman, saya menemukan penjelasan Henrik Bengtsson dalam sketsa untuk paket mendatang . Faktanya adalah bahwa fungsi XML dari paket xml2
adalah objek yang tidak dapat diekspor.
) Objek-objek ini "terikat" ke sesi R ini dan tidak dapat ditransfer ke proses lain, yang saya coba lakukan. Oleh karena itu, fungsi yang diluncurkan dalam komputasi paralel harus berisi "siklus penuh" operasi: membaca halaman HTML, menerima dan membersihkan data yang diperlukan.
Membuat komputasi paralel itu sendiri tidak membutuhkan banyak waktu dan garis kode. Hal pertama yang Anda butuhkan adalah mengunduh perpustakaan. Repositori Github menunjukkan paket mana yang diperlukan untuk metode mana. Di sini saya akan menunjukkan komputasi paralel menggunakan fungsi parLapply
dari paket parallel
. Untuk melakukan ini, jalankan saja doParallel
( parallel
akan mulai secara otomatis dalam kasus ini). Jika Anda tiba-tiba tidak tahu atau lupa jumlah inti prosesor Anda, deteksi berapa banyak dari mereka yang akan membantu mendeteksi detectCores
# detectCores - , number_cl <- detectCores()
Selanjutnya, buat salinan paralel R:
# makePSOCKcluster - R, cluster <- makePSOCKcluster(number_cl) registerDoParallel(cluster)
Sekarang kita sedang menulis fungsi yang akan melakukan semua prosedur yang kita butuhkan. Saya perhatikan sejak itu sesi baru dibuat. Paket R yang fungsinya digunakan dalam fungsi kita sendiri harus dituliskan di badan fungsi. Di spider_parallel.R, ini menyebabkan paket stringr
berjalan dua kali: pertama untuk mendapatkan alamat halaman, dan kemudian menghapus data.
Dan kemudian prosedurnya hampir tidak berbeda dengan menggunakan fungsi lapply
biasa. Di parLapply
kami memberikan daftar alamat, fungsi kami sendiri, dan, satu-satunya tambahan, variabel dengan kluster yang kami buat.
# parLapply - lapply big_list <- parLapply(cluster, list_url, scraping_parellel_func) # stopCluster(cluster)
Itu saja, sekarang tinggal membandingkan waktu yang dihabiskan.
Perbandingan kecepatan komputasi serial dan paralel
Ini akan menjadi titik terpendek. Komputasi paralel 5 kali lebih cepat dari biasanya:
Menggores kecepatan tanpa menggunakan komputasi paralel
Kecepatan Scraping Menggunakan Komputasi Paralel
Apa yang harus saya katakan? Komputasi paralel dapat menghemat banyak waktu Anda tanpa membuat kesulitan dalam membuat kode. Dengan peningkatan jumlah inti, kecepatan akan meningkat secara proporsional dengan jumlah mereka. Jadi, dengan beberapa perubahan, kami mempercepat kode 7 kali pertama (berhenti menghitung read_html
di setiap langkah), dan kemudian 5 lagi, menggunakan perhitungan paralel. Skrip laba-laba tanpa komputasi paralel, menggunakan paket parallel
dan foreach
, ada di repositori di Github.
Gambaran kecil dari paket Rcrawler
. Perbandingan kecepatan.
Ada beberapa cara lain untuk memo halaman HTML di R, tapi saya akan fokus pada paket Rcrawler . Fitur yang membedakannya dari alat lain dalam bahasa R adalah kemampuan untuk menjelajah situs. Anda dapat mengatur fungsi Rcrawler
dengan nama yang sama ke alamat Rcrawler
dan secara metodis, halaman per halaman, melewati seluruh situs. Rcrawler
memiliki banyak argumen untuk mengatur pencarian (misalnya, Anda dapat mencari berdasarkan kata kunci, sektor situs (berguna ketika situs terdiri dari sejumlah besar halaman), kedalaman pencarian, mengabaikan parameter URL yang membuat halaman duplikat, dan banyak lagi. Juga dalam hal ini fungsi-fungsi telah diletakkan perhitungan paralel, yang ditentukan oleh argumen no_cores
(jumlah core prosesor yang terlibat) dan no_conn
(jumlah permintaan paralel).
Untuk kasus kami, memotong dari alamat yang ditentukan, ada fungsi ContentScraper
. Ini tidak menggunakan komputasi paralel secara default, jadi Anda harus mengulang semua manipulasi yang saya jelaskan di atas. Saya menyukai fungsi itu sendiri - ia menyediakan banyak opsi untuk mengatur goresan dan dipahami dengan baik pada tingkat intuitif. Juga di sini Anda tidak dapat menggunakan if..else untuk halaman yang hilang atau nilai yang hilang, seperti eksekusi fungsi tidak berhenti.
# ContentScraper: # CssPatterns - CSS . # ExcludeCSSPat - CSS , . # , CSS CSS , . # ManyPerPattern - FALSE, , # . TRUE, , . # PatternsName - . # c , t_func <- function(n){ library(Rcrawler) t <- ContentScraper(n, CssPatterns = c("#product-title", ".authors", ".buying-price-val-number", ".buying-pricenew-val-number", ".publisher", ".isbn", ".pages2"), ExcludeCSSPat = c(".prodtitle-availibility", ".js-open-block-page_count"), ManyPerPattern = FALSE, PatternsName = c("title", "author", "price1", "price2", "publisher", "isbn", "page")) return(t) }
Tetapi dengan semua kualitas positif, fungsi ContentScraper
memiliki satu minus sangat serius - kecepatan kerja.
Konten Rcrawler Rcrawler
ContentScraper
Tanpa Komputasi Paralel
Konten Rcrawler ContentScraper ContentScraper
Rcrawler
Menggunakan Parallel Computing
Jadi Rcrawler harus digunakan jika Anda perlu memotong situs tanpa terlebih dahulu menentukan alamat url, serta dengan sejumlah kecil halaman. Dalam kasus lain, kecepatan lambat akan melebihi semua kemungkinan keuntungan menggunakan paket ini.
Saya akan berterima kasih atas komentar, saran, keluhan
Tautan repositori Github
Profil Lingkaran Saya