
1. Pendahuluan
Web scraping adalah teknik pengambilan data dari situs web dan
mengubahnya menjadi format yang dapat dianalisis lebih lanjut. Dalam R,
kita dapat menggunakan paket rvest untuk mengotomatisasi
proses ini tanpa perlu memahami HTML secara mendalam.
Laporan ini akan membahas bagaimana menggunakan rvest
untuk mengambil data dari tabel yang tersedia di situs web serta
bagaimana mengekstrak informasi dari elemen HTML tertentu.
2. Instalasi dan Persiapan
Sebelum melakukan web scraping, pastikan R dan paket yang diperlukan
sudah terinstal.
Instalasi Paket:
install.packages("rvest") library(rvest)
Selain itu, kita juga dapat menggunakan paket tambahan seperti
tidyverse untuk manipulasi data:
install.packages("tidyverse") library(tidyverse)
3. Contoh Web Scraping
3.1 Mangambil Data dari Tabel Web
Jika sebuah situs memiliki tabel yang ingin kita ekstrak, kita dapat
menggunakan fungsi html_table(). Sebagai contoh, kita akan
mengambil data medali Olimpiade dari sebuah situs web.
Langkah-langkah:
- Menentukan URL situs web.
- Menggunakan
read_html() untuk membaca halaman web.
- Menemukan elemen tabel dan mengubahnya menjadi data frame.
# Load library yang dibutuhkan
library(rvest)
## Warning: package 'rvest' was built under R version 4.4.3
library(httr)
# URL yang akan di-scrape
url <- "https://example.com/olympic-medals"
# Gunakan GET dengan User-Agent agar tidak diblokir
response <- GET(url, user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"))
# Cek apakah halaman dapat diakses
if (status_code(response) == 200) {
# Baca HTML dari konten response
page <- read_html(content(response, as = "text", encoding = "UTF-8"))
# Ambil data dari tabel pertama
data_medali <- page %>% html_node("table") %>% html_table(fill = TRUE)
# Tampilkan 6 baris pertama
head(data_medali)
} else {
print(paste("Gagal mengakses halaman. Status code:", status_code(response)))
}
## [1] "Gagal mengakses halaman. Status code: 404"
3.2 Mengambil Data dari Elemen HTML Spesifik
Terkadang, data yang kita butuhkan tidak dalam bentuk tabel tetapi
tersembunyi dalam elemen HTML tertentu. Kita bisa menggunakan
html_nodes() untuk mengambil data dari elemen tersebut.
Sebagai contoh, kita akan mengambil daftar nama paket R dari situs
web CRAN.
# Load library yang dibutuhkan
library(rvest)
library(httr)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
# URL halaman CRAN yang akan di-scrape
url <- "https://cran.r-project.org/web/packages/available_packages_by_name.html"
# Menggunakan GET dengan User-Agent agar tidak diblokir
response <- GET(url, user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"))
# Cek apakah halaman dapat diakses
if (status_code(response) == 200) {
# Baca HTML dari konten response
page <- read_html(content(response, as = "text", encoding = "UTF-8"))
# Mengambil tabel pertama dari halaman
nama_paket <- page %>% html_nodes("table") %>% .[[1]] %>% html_table(fill = TRUE)
# Memastikan hanya mengambil kolom pertama dan menghapus header yang salah
nama_paket <- nama_paket[, 1, drop = FALSE]
# Menghapus baris kosong atau tidak valid
nama_paket <- nama_paket %>%
filter(nchar(X1) > 0) %>% # Hapus string kosong
filter(X1 != "X1") # Hapus header duplikat (jika ada)
# Menyimpan hasil tanpa mencetak langsung
nama_paket <- head(nama_paket, -1) # Menghapus baris terakhir dari output
} else {
print(paste("Gagal mengakses halaman. Status code:", status_code(response)))
}
3.3 Web Scraping dengan Loop
Untuk mengambil data dari beberapa halaman web, kita bisa menggunakan
loop. Sebagai contoh, kita akan mengambil data pemain dari beberapa tim
sepak bola.
# Load library yang dibutuhkan
library(rvest)
library(httr)
library(dplyr)
# Daftar URL yang akan di-scrape
urls <- c("https://example.com/team1",
"https://example.com/team2",
"https://example.com/team3")
# Inisialisasi data frame kosong untuk menyimpan hasil
data_pemain <- data.frame()
# Loop melalui setiap URL
for (url in urls) {
# Menggunakan GET dengan User-Agent agar tidak diblokir
response <- GET(url, user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"))
# Periksa apakah halaman dapat diakses
if (status_code(response) == 200) {
# Baca HTML dari konten response
page <- read_html(content(response, as = "text", encoding = "UTF-8"))
# Mengambil tabel pertama dari halaman
tabel <- page %>% html_node("table") %>% html_table(fill = TRUE)
# Menambahkan kolom URL untuk identifikasi
tabel$Source <- url
# Menggabungkan data dengan data frame utama
data_pemain <- bind_rows(data_pemain, tabel)
} else {
message(paste("Gagal mengakses:", url, "- Status:", status_code(response)))
}
}
## Gagal mengakses: https://example.com/team1 - Status: 404
## Gagal mengakses: https://example.com/team2 - Status: 404
## Gagal mengakses: https://example.com/team3 - Status: 404
# Menampilkan beberapa baris pertama hasil scraping
head(data_pemain)
4. Tantangan dalam Web Scraping
- Beberapa situs memiliki perlindungan anti-scraping yang dapat
memblokir akses otomatis.
- Struktur HTML dapat berubah dari waktu ke waktu, sehingga skrip
harus diperbarui secara berkala.
- Untuk scraping data dalam jumlah besar, sebaiknya batasi jumlah
permintaan untuk menghindari pemblokiran.
5. Kesimpulan
Web scraping menggunakan rvest di R memungkinkan
pengambilan data dari berbagai sumber web dengan mudah. Dengan memahami
dasar-dasar rvest, kita dapat mengekstrak tabel, teks, dan
elemen HTML lainnya untuk dianalisis lebih lanjut.
LS0tDQp0aXRsZTogIlBlbmdhbnRhciBXZWIgU2NyYXBpbmcgZGVuZ2FuIFIgZGFuIFBha2V0IHJ2ZXN0Ig0KDQphdXRob3I6IA0KICAgIC0gIk5hYmlsYSBBbmdnaXRhIFB1dHJpIg0KICAgIA0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIg0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIGdhbGxlcnk6IHRydWUNCiAgICBsaWJfZGlyOiBsaWJzDQogICAgZGZfcHJpbnQ6ICJwYWdlZCINCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNzczogInN0eWxlL3N0eWxlLmNzcyINCi0tLQ0KPGltZyBzcmM9ImltZy9wcm9maWxlLmpwZyIgYWx0PSJQcm9maWxlIiBpZD0ibG9nby11dGFtYSIgc3R5bGU9IndpZHRoOjMwMHB4OyBkaXNwbGF5OiBibG9jazsgbWFyZ2luOiBhdXRvOyIvPg0KDQoNCiMgKioxLiBQZW5kYWh1bHVhbioqDQoNCldlYiBzY3JhcGluZyBhZGFsYWggdGVrbmlrIHBlbmdhbWJpbGFuIGRhdGEgZGFyaSBzaXR1cyB3ZWIgZGFuIG1lbmd1YmFobnlhIG1lbmphZGkgZm9ybWF0IHlhbmcgZGFwYXQgZGlhbmFsaXNpcyBsZWJpaCBsYW5qdXQuIERhbGFtIFIsIGtpdGEgZGFwYXQgbWVuZ2d1bmFrYW4gcGFrZXQgYGBydmVzdGBgIHVudHVrIG1lbmdvdG9tYXRpc2FzaSBwcm9zZXMgaW5pIHRhbnBhIHBlcmx1IG1lbWFoYW1pIEhUTUwgc2VjYXJhIG1lbmRhbGFtLg0KDQpMYXBvcmFuIGluaSBha2FuIG1lbWJhaGFzIGJhZ2FpbWFuYSBtZW5nZ3VuYWthbiBgYHJ2ZXN0YGAgdW50dWsgbWVuZ2FtYmlsIGRhdGEgZGFyaSB0YWJlbCB5YW5nIHRlcnNlZGlhIGRpIHNpdHVzIHdlYiBzZXJ0YSBiYWdhaW1hbmEgbWVuZ2Vrc3RyYWsgaW5mb3JtYXNpIGRhcmkgZWxlbWVuIEhUTUwgdGVydGVudHUuDQoNCiMgKioyLiBJbnN0YWxhc2kgZGFuIFBlcnNpYXBhbioqDQoNClNlYmVsdW0gbWVsYWt1a2FuIHdlYiBzY3JhcGluZywgcGFzdGlrYW4gUiBkYW4gcGFrZXQgeWFuZyBkaXBlcmx1a2FuIHN1ZGFoIHRlcmluc3RhbC4NCg0KKipJbnN0YWxhc2kgUGFrZXQqKjoNCg0KYGBpbnN0YWxsLnBhY2thZ2VzKCJydmVzdCIpDQpsaWJyYXJ5KHJ2ZXN0KWBgDQoNClNlbGFpbiBpdHUsIGtpdGEganVnYSBkYXBhdCBtZW5nZ3VuYWthbiBwYWtldCB0YW1iYWhhbiBzZXBlcnRpIHRpZHl2ZXJzZSB1bnR1ayBtYW5pcHVsYXNpIGRhdGE6DQoNCmBgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmxpYnJhcnkodGlkeXZlcnNlKWBgDQoNCiMgKiozLiBDb250b2ggV2ViIFNjcmFwaW5nKioNCg0KIyMgKiozLjEgTWFuZ2FtYmlsIERhdGEgZGFyaSBUYWJlbCBXZWIqKg0KDQpKaWthIHNlYnVhaCBzaXR1cyBtZW1pbGlraSB0YWJlbCB5YW5nIGluZ2luIGtpdGEgZWtzdHJhaywga2l0YSBkYXBhdCBtZW5nZ3VuYWthbiBmdW5nc2kgYGBodG1sX3RhYmxlKClgYC4gU2ViYWdhaSBjb250b2gsIGtpdGEgYWthbiBtZW5nYW1iaWwgZGF0YSBtZWRhbGkgT2xpbXBpYWRlIGRhcmkgc2VidWFoIHNpdHVzIHdlYi4NCg0KKipMYW5na2FoLWxhbmdrYWgqKjoNCg0KMS4gTWVuZW50dWthbiBVUkwgc2l0dXMgd2ViLg0KMi4gTWVuZ2d1bmFrYW4gYGByZWFkX2h0bWwoKWBgIHVudHVrIG1lbWJhY2EgaGFsYW1hbiB3ZWIuDQozLiBNZW5lbXVrYW4gZWxlbWVuIHRhYmVsIGRhbiBtZW5ndWJhaG55YSBtZW5qYWRpIGRhdGEgZnJhbWUuDQpgYGB7ciBlY2hvPVRSVUV9DQojIExvYWQgbGlicmFyeSB5YW5nIGRpYnV0dWhrYW4NCmxpYnJhcnkocnZlc3QpDQpsaWJyYXJ5KGh0dHIpDQoNCiMgVVJMIHlhbmcgYWthbiBkaS1zY3JhcGUNCnVybCA8LSAiaHR0cHM6Ly9leGFtcGxlLmNvbS9vbHltcGljLW1lZGFscyINCg0KIyBHdW5ha2FuIEdFVCBkZW5nYW4gVXNlci1BZ2VudCBhZ2FyIHRpZGFrIGRpYmxva2lyDQpyZXNwb25zZSA8LSBHRVQodXJsLCB1c2VyX2FnZW50KCJNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXaW42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEyNCBTYWZhcmkvNTM3LjM2IikpDQoNCiMgQ2VrIGFwYWthaCBoYWxhbWFuIGRhcGF0IGRpYWtzZXMNCmlmIChzdGF0dXNfY29kZShyZXNwb25zZSkgPT0gMjAwKSB7DQogICMgQmFjYSBIVE1MIGRhcmkga29udGVuIHJlc3BvbnNlDQogIHBhZ2UgPC0gcmVhZF9odG1sKGNvbnRlbnQocmVzcG9uc2UsIGFzID0gInRleHQiLCBlbmNvZGluZyA9ICJVVEYtOCIpKQ0KICANCiAgIyBBbWJpbCBkYXRhIGRhcmkgdGFiZWwgcGVydGFtYQ0KICBkYXRhX21lZGFsaSA8LSBwYWdlICU+JSBodG1sX25vZGUoInRhYmxlIikgJT4lIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpDQogIA0KICAjIFRhbXBpbGthbiA2IGJhcmlzIHBlcnRhbWENCiAgaGVhZChkYXRhX21lZGFsaSkNCn0gZWxzZSB7DQogIHByaW50KHBhc3RlKCJHYWdhbCBtZW5nYWtzZXMgaGFsYW1hbi4gU3RhdHVzIGNvZGU6Iiwgc3RhdHVzX2NvZGUocmVzcG9uc2UpKSkNCn0NCmBgYA0KDQojIyAqKjMuMiBNZW5nYW1iaWwgRGF0YSBkYXJpIEVsZW1lbiBIVE1MIFNwZXNpZmlrKioNCg0KVGVya2FkYW5nLCBkYXRhIHlhbmcga2l0YSBidXR1aGthbiB0aWRhayBkYWxhbSBiZW50dWsgdGFiZWwgdGV0YXBpIHRlcnNlbWJ1bnlpIGRhbGFtIGVsZW1lbiBIVE1MIHRlcnRlbnR1LiBLaXRhIGJpc2EgbWVuZ2d1bmFrYW4gYGBodG1sX25vZGVzKClgYCB1bnR1ayBtZW5nYW1iaWwgZGF0YSBkYXJpIGVsZW1lbiB0ZXJzZWJ1dC4NCg0KU2ViYWdhaSBjb250b2gsIGtpdGEgYWthbiBtZW5nYW1iaWwgZGFmdGFyIG5hbWEgcGFrZXQgUiBkYXJpIHNpdHVzIHdlYiBDUkFOLg0KYGBge3IgZWNobz1UUlVFfQ0KIyBMb2FkIGxpYnJhcnkgeWFuZyBkaWJ1dHVoa2FuDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeShodHRyKQ0KbGlicmFyeShkcGx5cikgIA0KDQojIFVSTCBoYWxhbWFuIENSQU4geWFuZyBha2FuIGRpLXNjcmFwZQ0KdXJsIDwtICJodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvYXZhaWxhYmxlX3BhY2thZ2VzX2J5X25hbWUuaHRtbCINCg0KIyBNZW5nZ3VuYWthbiBHRVQgZGVuZ2FuIFVzZXItQWdlbnQgYWdhciB0aWRhayBkaWJsb2tpcg0KcmVzcG9uc2UgPC0gR0VUKHVybCwgdXNlcl9hZ2VudCgiTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzkxLjAuNDQ3Mi4xMjQgU2FmYXJpLzUzNy4zNiIpKQ0KDQojIENlayBhcGFrYWggaGFsYW1hbiBkYXBhdCBkaWFrc2VzDQppZiAoc3RhdHVzX2NvZGUocmVzcG9uc2UpID09IDIwMCkgew0KICAjIEJhY2EgSFRNTCBkYXJpIGtvbnRlbiByZXNwb25zZQ0KICBwYWdlIDwtIHJlYWRfaHRtbChjb250ZW50KHJlc3BvbnNlLCBhcyA9ICJ0ZXh0IiwgZW5jb2RpbmcgPSAiVVRGLTgiKSkNCiAgDQogICMgTWVuZ2FtYmlsIHRhYmVsIHBlcnRhbWEgZGFyaSBoYWxhbWFuDQogIG5hbWFfcGFrZXQgPC0gcGFnZSAlPiUgaHRtbF9ub2RlcygidGFibGUiKSAlPiUgLltbMV1dICU+JSBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQ0KICANCiAgIyBNZW1hc3Rpa2FuIGhhbnlhIG1lbmdhbWJpbCBrb2xvbSBwZXJ0YW1hIGRhbiBtZW5naGFwdXMgaGVhZGVyIHlhbmcgc2FsYWgNCiAgbmFtYV9wYWtldCA8LSBuYW1hX3Bha2V0WywgMSwgZHJvcCA9IEZBTFNFXQ0KICANCiAgIyBNZW5naGFwdXMgYmFyaXMga29zb25nIGF0YXUgdGlkYWsgdmFsaWQNCiAgbmFtYV9wYWtldCA8LSBuYW1hX3Bha2V0ICU+JSANCiAgICBmaWx0ZXIobmNoYXIoWDEpID4gMCkgJT4lICAjIEhhcHVzIHN0cmluZyBrb3NvbmcNCiAgICBmaWx0ZXIoWDEgIT0gIlgxIikgICAgICAgICAjIEhhcHVzIGhlYWRlciBkdXBsaWthdCAoamlrYSBhZGEpDQogIA0KICAjIE1lbnlpbXBhbiBoYXNpbCB0YW5wYSBtZW5jZXRhayBsYW5nc3VuZw0KICBuYW1hX3Bha2V0IDwtIGhlYWQobmFtYV9wYWtldCwgLTEpICAjIE1lbmdoYXB1cyBiYXJpcyB0ZXJha2hpciBkYXJpIG91dHB1dA0KfSBlbHNlIHsNCiAgcHJpbnQocGFzdGUoIkdhZ2FsIG1lbmdha3NlcyBoYWxhbWFuLiBTdGF0dXMgY29kZToiLCBzdGF0dXNfY29kZShyZXNwb25zZSkpKQ0KfQ0KYGBgDQojIyAqKjMuMyBXZWIgU2NyYXBpbmcgZGVuZ2FuIExvb3AqKg0KDQpVbnR1ayBtZW5nYW1iaWwgZGF0YSBkYXJpIGJlYmVyYXBhIGhhbGFtYW4gd2ViLCBraXRhIGJpc2EgbWVuZ2d1bmFrYW4gbG9vcC4gU2ViYWdhaSBjb250b2gsIGtpdGEgYWthbiBtZW5nYW1iaWwgZGF0YSBwZW1haW4gZGFyaSBiZWJlcmFwYSB0aW0gc2VwYWsgYm9sYS4NCmBgYHtyIGVjaG89VFJVRX0NCiMgTG9hZCBsaWJyYXJ5IHlhbmcgZGlidXR1aGthbg0KbGlicmFyeShydmVzdCkNCmxpYnJhcnkoaHR0cikNCmxpYnJhcnkoZHBseXIpDQoNCiMgRGFmdGFyIFVSTCB5YW5nIGFrYW4gZGktc2NyYXBlDQp1cmxzIDwtIGMoImh0dHBzOi8vZXhhbXBsZS5jb20vdGVhbTEiLCANCiAgICAgICAgICAiaHR0cHM6Ly9leGFtcGxlLmNvbS90ZWFtMiIsIA0KICAgICAgICAgICJodHRwczovL2V4YW1wbGUuY29tL3RlYW0zIikNCg0KIyBJbmlzaWFsaXNhc2kgZGF0YSBmcmFtZSBrb3NvbmcgdW50dWsgbWVueWltcGFuIGhhc2lsDQpkYXRhX3BlbWFpbiA8LSBkYXRhLmZyYW1lKCkNCg0KIyBMb29wIG1lbGFsdWkgc2V0aWFwIFVSTA0KZm9yICh1cmwgaW4gdXJscykgew0KICAjIE1lbmdndW5ha2FuIEdFVCBkZW5nYW4gVXNlci1BZ2VudCBhZ2FyIHRpZGFrIGRpYmxva2lyDQogIHJlc3BvbnNlIDwtIEdFVCh1cmwsIHVzZXJfYWdlbnQoIk1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS85MS4wLjQ0NzIuMTI0IFNhZmFyaS81MzcuMzYiKSkNCiAgDQogICMgUGVyaWtzYSBhcGFrYWggaGFsYW1hbiBkYXBhdCBkaWFrc2VzDQogIGlmIChzdGF0dXNfY29kZShyZXNwb25zZSkgPT0gMjAwKSB7DQogICAgIyBCYWNhIEhUTUwgZGFyaSBrb250ZW4gcmVzcG9uc2UNCiAgICBwYWdlIDwtIHJlYWRfaHRtbChjb250ZW50KHJlc3BvbnNlLCBhcyA9ICJ0ZXh0IiwgZW5jb2RpbmcgPSAiVVRGLTgiKSkNCiAgICANCiAgICAjIE1lbmdhbWJpbCB0YWJlbCBwZXJ0YW1hIGRhcmkgaGFsYW1hbg0KICAgIHRhYmVsIDwtIHBhZ2UgJT4lIGh0bWxfbm9kZSgidGFibGUiKSAlPiUgaHRtbF90YWJsZShmaWxsID0gVFJVRSkNCiAgICANCiAgICAjIE1lbmFtYmFoa2FuIGtvbG9tIFVSTCB1bnR1ayBpZGVudGlmaWthc2kNCiAgICB0YWJlbCRTb3VyY2UgPC0gdXJsDQogICAgDQogICAgIyBNZW5nZ2FidW5na2FuIGRhdGEgZGVuZ2FuIGRhdGEgZnJhbWUgdXRhbWENCiAgICBkYXRhX3BlbWFpbiA8LSBiaW5kX3Jvd3MoZGF0YV9wZW1haW4sIHRhYmVsKQ0KICB9IGVsc2Ugew0KICAgIG1lc3NhZ2UocGFzdGUoIkdhZ2FsIG1lbmdha3NlczoiLCB1cmwsICItIFN0YXR1czoiLCBzdGF0dXNfY29kZShyZXNwb25zZSkpKQ0KICB9DQp9DQoNCiMgTWVuYW1waWxrYW4gYmViZXJhcGEgYmFyaXMgcGVydGFtYSBoYXNpbCBzY3JhcGluZw0KaGVhZChkYXRhX3BlbWFpbikNCmBgYA0KDQojICoqNC4gVGFudGFuZ2FuIGRhbGFtIFdlYiBTY3JhcGluZyoqDQoNCi0gQmViZXJhcGEgc2l0dXMgbWVtaWxpa2kgcGVybGluZHVuZ2FuIGFudGktc2NyYXBpbmcgeWFuZyBkYXBhdCBtZW1ibG9raXIgYWtzZXMgb3RvbWF0aXMuDQotIFN0cnVrdHVyIEhUTUwgZGFwYXQgYmVydWJhaCBkYXJpIHdha3R1IGtlIHdha3R1LCBzZWhpbmdnYSBza3JpcCBoYXJ1cyBkaXBlcmJhcnVpIHNlY2FyYSBiZXJrYWxhLg0KLSBVbnR1ayBzY3JhcGluZyBkYXRhIGRhbGFtIGp1bWxhaCBiZXNhciwgc2ViYWlrbnlhIGJhdGFzaSBqdW1sYWggcGVybWludGFhbiB1bnR1ayBtZW5naGluZGFyaSBwZW1ibG9raXJhbi4NCg0KDQojICoqNS4gS2VzaW1wdWxhbioqDQoNCldlYiBzY3JhcGluZyBtZW5nZ3VuYWthbiBgYHJ2ZXN0YGAgZGkgUiBtZW11bmdraW5rYW4gcGVuZ2FtYmlsYW4gZGF0YSBkYXJpIGJlcmJhZ2FpIHN1bWJlciB3ZWIgZGVuZ2FuIG11ZGFoLiBEZW5nYW4gbWVtYWhhbWkgZGFzYXItZGFzYXIgYGBydmVzdGBgLCBraXRhIGRhcGF0IG1lbmdla3N0cmFrIHRhYmVsLCB0ZWtzLCBkYW4gZWxlbWVuIEhUTUwgbGFpbm55YSB1bnR1ayBkaWFuYWxpc2lzIGxlYmloIGxhbmp1dC4=