Web Scraping in R : rvest
Mendownload data dari beberapa website penyedia data mungkin sudah biasa kita lakukan. Namun bagaimana jika kita ingin melakukan pengambilan data, namun data tersebut merupakan data dari website itu sendiri? Nah disinilah yang disebut dengan melakukan scraping data.
Trustpilot merupakan salah satu website yang cukup populer yang digunakan para pelanggan dalam melakukan ulasan mengenai bisnis dan pelayanan dari suatu e-commerce. Nah pada tutorial kali ini, kita akan belajar bagaimana cara scraping informasi yang berguna dari website Trustpilot lalu membuat insight sederhana dari data atau informasi yang akan kita dapatkan dengan menggunakan R.
Library Used
Nah sebelum melakukan scraping data dari website Trustpilot, kita memerlukan package yang harus kita install di lembar kerja R kita. Berikut ini beberapa package yang dapat digunakan.
Find All Page
Sebagai contoh, misal kita akan melakukan pengambilan informasi tentang ulasan dari e-commerce seperti Amazon. Yang perlu kita butuhkan untuk mengambil informasi dari suatu website adalah dengan menggunakan URL pada website tersebut.
URL yang kita miliki itu nantinya akan kita simpan sebagai objek.
Biasanya dalam perusahaan besar, apalagi itu merupakan e-commerce, tentunya memiliki review yang sangat banyak, bisa lebih dari ratusan.
Untuk memperoleh data dari website kita perlu menggunakan fungsi dari rvest package. Untuk mengubah website menjadi objek XML, kita gunakan function read_html(). Nah tapi jangan lupa untuk menyediakan URL target yang akan kita gunakan untuk mengumpulkan data, memanggil server web, dan memparsing data dari web tersebut. Untuk melakukan ekstraksi node dari XML objek kita gunakan html_nodes(), dan diiikuti dengan . untuk menandakan class deskriptornya. Output yang akan dihasilkan adalah list dari semua node yang ditemukan. Untuk mengekstraksi tagged data, kita menggunakan html_text()pada node yang sudah kita temukan. Pada case ketika kita butuh mengekstraksi suatu atribut dalam website, kita gunakan html_attrs(). Function ini dapat mengembalikan atribut-atribut yang ingin kita atur ulang dan kita ekstrak.
Nah yaudah gausah lama-lama, mari kita coba sama-sama.
Pada tutorial kali ini, kita akn banyak bermain-main dengan menggunakan fuction() yang tujuannya adalah untuk mengekstraksi data-data yang akan kita ambil dari suatu website. Dalam function yang akan kita buat nantinya, kita perlu mengetahui tag dari informasi yang akan kita ambil.
Karena kali ini saya ingin mencontohkan scrapping data dari e-commerce Amazon, berikut tag yang kita perlukan.
.pagination-page: tag untuk melihat berapa banyak halaman atau page review.consumer-information__names: tag untuk mengtahui nama reviewer.star-rating: tag untuk mengetahui rating penilaian terhadap suatu e-commerce.review-content__text: tag untuk mengetahui review yang diberikan dari masing-masing reviewer.consumer-information__location: tag untuk mengetahui asal reviewer.consumer-information__review-count: tag untuk mengetahui reviewer pernah melakukan review berapa kali
Perlu diingat bahwa untuk masing-masing website memiliki nama tag dan class yang berbeda, sehingga harus disesuaikan untuk website tertentu
Berikut ini function yang dapat digunakan untuk beberapa tag diatas.
last.page <- function(html){
pages.data <- html %>%
html_nodes('.pagination-page') %>%
# ekstrak raw teks ke list
html_text()
# mengambil halaman kedua hingga terakhir
pages.data[(length(pages.data)-1)] %>%
# mengambil raw string
unname() %>%
# convert ke angka
as.numeric()
}Tahapan diatas mengaplikasikan function html_nodes() yang mana kita ingin mengekstrak pagination class. Fungsi yang terakhir yang dibuat adalah fungsi untuk mengambil item yang benar dari list, halaman kedua sampai terkahir, dan mengubahnya menjadi nilai numerik.
Untuk melakukan testing terhadap fungsi yang kita buat bisa menggunakan function read_html() dan aplikasikan pada fungsi yang telah kita tulis :
Setelah memperoleh angka halaman akhir dari web reviewnya, kita bisa melihat URL apa saja yang berkaitan dengan review untuk Amazon.
## [1] "http://www.trustpilot.com/review/www.amazon.com?page=225?page=1"
## [2] "http://www.trustpilot.com/review/www.amazon.com?page=225?page=2"
## [3] "http://www.trustpilot.com/review/www.amazon.com?page=225?page=3"
## [4] "http://www.trustpilot.com/review/www.amazon.com?page=225?page=4"
## [5] "http://www.trustpilot.com/review/www.amazon.com?page=225?page=5"
## [6] "http://www.trustpilot.com/review/www.amazon.com?page=225?page=6"
Kita dapat mengecek secara manual dari list.of.pages memang valid dan dapat diakses via web browser.
Extract Information of One Page
Jika kita ingin mengekstrak teks review, rating, nama author, dan waktu dari pengumpulan review dari subpage. Kita dapat mengulangi langkah dari awal dari masing-masing fields yang ingin kita cari.
information <- function(html, tag){
html %>%
# relevant tag
html_nodes(tag) %>%
html_text() %>%
# trim additional white space
str_trim() %>%
# mengubah dari list ke vector
unlist()
}Terakhir, kita akan membuat fungsi untuk mengekstrak rating review. Rating disebut sebagai atribut dalam tag. Rating bukan termasuk dalam angka, namun masuk dalam star-rating-X, di mana X adalah angka yang kita inginkan.
Kita mengaplikasikan fungsi tersebut pada list URL yang akan kita generalisasikan. Untuk melakukannya, kita gunakan map() function dari purrr package yang mana termasuk dalam tidyverse.
star.rating.information <- function(html){
# pattern you look for : the first digit after 'star-rating-'
pattern = 'star-rating-' %R% capture(DIGIT)
rating <- html %>%
html_nodes('.star-rating') %>%
html_attrs() %>%
# apply the pattern match to all attribtes
map(str_match, pattern = pattern) %>%
# str_match[1] is fully matched string, the second entry
# is the part you extract with the capture in your pattern
map(2) %>%
unlist()
# leave out first instance, as it is not part of a review
rating[3:length(rating)]
}Selanjutnya yang terakhir adalah kita akan mengambil data tanggal atau waktu dari review memberikan review.
dates <- function(html){
read_html(url) %>%
html_nodes('.review-card .review__content .review-content .review-content__header .review-content-header .review-content-header__dates') %>%
html_text() %>%
purrr::map(1) %>%
# parse string into a datetime object with lubridate
ymd_hms() %>%
unlist()
}Selanjutnya, kita akan gabungkan semua data yang kita ingin dapatkan dalam satu tabel.
get.data.table <- function(html, company.name){
# extract basic information from HTML
name.review <- information(html, '.consumer-information__name')
text.review <- information(html, '.review-content__text')
location.review <- information(html, '.consumer-information__location')
review.count <- information(html, '.consumer-information__review-count')
rating <- star.rating.information(html)
dates <- dates(html)
#combine into tibble
combine.data <- tibble(Name = name.review, Dates = dates, Review = text.review, Location = location.review,
Review.count = review.count, Rating = rating)
# tag individual data with the company name
combine.data %>%
mutate(company = company.name) %>%
select(company, Name, Dates, Review, Location, Review.count, Rating)
}get.data.url <- function(url, company.name){
html <- read_html(url)
get.data.table(html, company.name)
}Terakhir kalinya, kita akan memuat function untuk melakukan scrapping dari data-data yang ingin kita ambil dari URL yang telah kita pilih dengan memilih satu nama company dan menggabungkan semua hasil scraping data kita kedalam satu tibble.
scrape.table <- function(url, company.name){
# baca halaman pertama
first.page <- read_html(url)
# ekstrak nomor halaman
latest.page <- last.page(first.page)
# masukin target URL
list.of.pages <- str_c(url, '?page=', 1:latest.page)
# apply the extraction and bind the individuals resuts back into one table
list.of.pages %>%
purrr::map(get.data.url, company.name) %>%
# combine the tibbles into one tibble
bind_rows() %>%
# write a tab-eparated file
write_tsv(str_c(company.name, '.tsv'))
}Mari kita coba untuk scrap dari review yang diberikan untuk website Amazon.
temp <- scrape.table(url, 'amazon')
# menyimpan data hasil scraping
amazon <- read_tsv('amazon.tsv')## Parsed with column specification:
## cols(
## company = col_character(),
## Name = col_character(),
## Dates = col_datetime(format = ""),
## Review = col_character(),
## Location = col_character(),
## Review.count = col_character(),
## Rating = col_double()
## )
## # A tibble: 10 x 7
## company Name Dates Review Location Review.count Rating
## <chr> <chr> <dttm> <chr> <chr> <chr> <dbl>
## 1 amazon GOLDe~ 2011-02-24 21:26:06 Honestl~ N/A, US 2 reviews 5
## 2 amazon Gary 2011-01-10 04:36:18 Highly ~ N/A, GB 14 reviews 5
## 3 amazon Nikki~ 2011-01-06 06:01:17 - Deliv~ N/A, US 7 reviews 4
## 4 amazon Amor ~ 2010-11-26 23:31:39 Great o~ N/A, OM 2 reviews 4
## 5 amazon Mick 2010-11-04 13:38:52 "Two op~ N/A, US 1 review 2
## 6 amazon Luthe~ 2010-11-03 12:15:51 I belie~ N/A, MY 6 reviews 3
## 7 amazon Bling~ 2010-10-27 16:55:03 "purcha~ lincoln~ 6 reviews 1
## 8 amazon Terry 2010-10-26 12:33:55 I got s~ N/A, US 3 reviews 2
## 9 amazon Zaher~ 2010-10-26 07:11:46 They ar~ N/A, IL 2 reviews 5
## 10 amazon Lovat~ 2010-10-15 00:08:55 I love ~ N/A, CN 2 reviews 5
Visualisation Data
Beberapa data review yang diberikan beberapa orang sudah kita dapatkan. Berdasarkan hasil scraping yang diperoleh, terdapat 4500 reviewer yang memberikan review terhadap website Amazon. Dari 4500 reviewer yang memberikan review, jumlah rating yang diberikan berdasarkan banyak bintangnya adalah sebagai berikut.
Total Rating
Dari grafik diatas, kita memperoleh informasi bahwa banyak reviewer yang memberikan rating 5 (Star 5) terhadap Amazon. Bila kita lihat pergerakan dari dari rata-rata rating tiap bulan dan mingguannya pada Amazon adalah sebagai berikut.
Montly Average Rating
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
##
## Attaching package: 'xts'
## The following objects are masked from 'package:dplyr':
##
## first, last
amazon.ts <- xts(amazon.new$Rating, amazon.new$Date)
colnames(amazon.ts) <- 'Rating'
ended.interval <- '2009-01-01/'
amazon.xts <- amazon.ts[ended.interval]
avg.rating <- apply.monthly(amazon.xts, colMeans)
count.rating <- apply.monthly(amazon.xts, FUN = length)avg.rating <- avg.rating %>%
as.data.frame()
avg.rating$month <- row.names(avg.rating)
# avg.rating <- avg.rating[,c(ncol(avg.rating), 1:(ncol(avg.rating)-1))]
avg.rating <- avg.rating %>%
mutate(month = as.Date(month),
Year = year(month),
Month_ = month(month),
Month = factor(paste( Year, Month_, sep = "-")),
Month = factor(Month, levels = Month)) %>%
select(-Year, -Month_, -month)
count.rating <- count.rating %>%
as.data.frame()
count.rating$month <- row.names(count.rating)
# avg.rating <- avg.rating[,c(ncol(avg.rating), 1:(ncol(avg.rating)-1))]
count.rating <- count.rating %>%
mutate(month = as.Date(month),
Year = year(month),
Month_ = month(month),
Month = factor(paste( Year, Month_, sep = "-")),
Month = factor(Month, levels = Month)) %>%
select(-Year, -Month_, -month)