
Rã«ããAmazonã¬ãã¥ãŒã®åéãšããã¹ãåæ
2022/6/19
ã¹ã¯ã¬ã€ãã³ã°ãå¯èœã§ããããšã確èªããŸããã
ã¹ã¯ã¬ã€ãã³ã°ããã°ã©ã ãè±æã§ãååŸã§ããããæ¹åããŸããã
âã¯ããã«
Amazonã§è²·ãç©ãããéãã¬ãã¥ãŒãåèã«ããªãæ¹ã¯ããªãã§ãããã
é«è©äŸ¡ã§ãã£ãŠãäœè©äŸ¡ã§ãã£ãŠããã¬ãã¥ãŒã¯è²ŽéãªãŠãŒã¶ãŒã®æèŠã§ãããæ
å ±ã®å®åº«ã§ããããã倧éã«ã¬ãã¥ãŒããããšå
šãŠç®ãéãã®ã«ã¯æéãããããŸãããé«è©äŸ¡ãäœè©äŸ¡ãæ§ã
ã§æ
å ±ã®æŽçã¯é£ããã§ãããããã£ãšè¯ãæãã«èŠçŽãæ°å€åã§ããã°...ãšããããšã§ãä»åã¯Rã䜿ã£ãŠAmazonã¬ãã¥ãŒã®åéãšåæãããŸããã
ã¹ã¯ã¬ã€ãã³ã°çŠæ¢èŠå®ã«ã€ããŠã¯ãã¡ãã®ãµã€ããšåæ§ã®ç«å Žã§ãã...ãã£ãããŠã§ã€ãã¿ã€ã ãå
¥ããŠAmazonããã®ãµãŒããŒã®ãè¿·æã«ãªããªãããããã°ã€ã³ããèªå·±è²¬ä»»ã§ãã£ãããããŸããã...ã
ïŒãããŠãäžè©±ã«ãªã£ããè²·ããŸããããïŒ
åœæ¹Windowsã§ãã®ã§UTF-8ã®htmlãã¹ã¯ã¬ã€ãã³ã°ãããšæååãããŠããŸããŸãããã®ããDockeräžã®Ubuntuã§RStudioãšSeleniumã䜿çšããSeleniumçµç±ã§htmlãååŸããæ¹åŒã§åéããŸããã
以äžã®ç°å¢ã§å®æœããŠããŸãã
> devtools::session_info()
â Session info âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
setting value
version R version 4.0.5 (2021-03-31)
os Ubuntu 20.04.2 LTS
system x86_64, linux-gnu
ui RStudio
language (EN)
collate ja_JP.UTF-8
ctype ja_JP.UTF-8
tz Etc/UTC
date 2021-05-18 âåé
ãŸãã©ã€ãã©ãªãèªã¿èŸŒãã§RSeleniumã§chromeãéããŸãã
RSeleniumã®Vignetteéãã«ãããšãšã©ãŒãèµ·ããŠããŸããŸããã
ã©ãããç§ã®ç°å¢ã§ã¯remoteServerAddr = "localhost"ã§ãšã©ãŒãèµ·ããæ§åã
remoteServerAddrã«IPv4ã¢ãã¬ã¹ãæå®ããŠããããšã§ãšã©ãŒãè§£æ¶ã§ããŸããã
rm(list=ls())
gc(); gc();
if (!require("pacman")) install.packages("pacman")
pacman::p_load(tidyverse,
RSelenium,
rvest,
stringi,
XML,
openxlsx)
ip = "***.**.***.*"
remDr = remoteDriver(
remoteServerAddr = ip, # ipconfigã§IPv4ã¢ãã¬ã¹ã確èªããŠãã
port = 4444,
browserName = "chrome"
)
remDr$open()åéã®ã³ãŒãã¯ãã¡ããåèã«ãããŠããã ããŸããã
倿Žç¹ãšããŠã以äžã®ãããªå°ã¢ã¬ã³ãžãããŠããŸãã
ã»RSeleniumã§htmlãååŸããããã«remDr$getPageSourceãããã®ãread.html()
ã»ããã¹ãã®é£ç¶ããã¹ããŒã¹ã\nãåé€
ã»ã¬ãã¥ãŒææ°ãæ°å€ããŒã¿ã«åºæ¥ãããæ°åã®ã¿ãæœåº
ã»åŸãã確èªã§ããããurlãåååããã©ã³ããååŸ
ã»å€åœèªã®ã¬ãã¥ãŒã«åãæ¿ãã£ãå Žåã«classãç°ãªããšã©ãŒãšãªãç¹ã«å¯Ÿå¿
âŠçã ã
scrape_amazon <- function(ASIN, page_num){
url <- paste0("https://www.amazon.co.jp/product-reviews/",ASIN,"/?pageNumber=",page_num)
remDr$navigate(url)
Sys.sleep(5)
doc <- remDr$getPageSource() %>%
unlist() %>%
read_html()
# Review Title
# There are two types of titles: with links and without links, so both are combined in the list.
doc %>%
html_nodes("[class='a-size-base a-link-normal review-title a-color-base review-title-content a-text-bold']") %>%
html_text() %>%
stri_replace_all(regex = "\n| +|ã+", replacement = " ") %>%
stri_replace_all(regex = "^ +", replacement = "") -> review_title1
doc %>%
html_nodes("[class='a-size-base review-title a-color-base review-title-content a-text-bold']") %>%
html_text() %>%
stri_replace_all(regex = "\n| +|ã+", replacement = " ") %>%
stri_replace_all(regex = "^ +", replacement = "") -> review_title2
review_title <- append(review_title1, review_title2)
# Review Text
doc %>%
html_nodes("[class='a-size-base review-text review-text-content']") %>%
html_text() %>%
stri_replace_all(regex = "\n| +|ã+", replacement = " ") %>%
stri_replace_all(regex = "^ +", replacement = "") -> review_text
# Number of stars in review
# There are two types of stars, so both are combined in the list.
doc %>%
html_nodes("[data-hook='review-star-rating']") %>%
html_text() %>%
stri_replace_all(regex = "\n| +|ã+|5ã€æã®ãã¡", replacement = " ") %>%
as.numeric() -> review_star1
doc %>%
html_nodes("[data-hook='cmps-review-star-rating']") %>%
html_text() %>%
stri_replace_all(regex = "\n| +|ã+|5ã€æã®ãã¡", replacement = " ") %>%
as.numeric -> review_star2
review_star <- append(review_star1, review_star2)
# product_name
doc %>%
html_node('[class="a-size-large a-text-ellipsis"]') %>%
html_text() -> product_name
# supplier
doc %>%
html_node('[class="a-size-base a-link-normal"]') %>%
html_text() -> supplier
# Return a tibble
tibble(review_title,
review_text,
review_star,
page = page_num,
page_url = url,
product_name = product_name,
supplier = supplier) %>% return()
}ä»åã¯å®çªåºç€åç²§åãããã¢ãã®Amazonã¬ãã¥ãŒãèŠãŠã¿ãŸãã
ãã¡ãã¯äžã®ãµã€ãã®ã³ãŒãããã®ãŸãŸäœ¿çšãããŠããã ããŸããã
ããAmazonæ§ã«äººã®æã®ã¬ãããã£ãœããæããŠããã ãããã«Sys.sleepã«ãããŠã§ã€ãã¿ã€ã ãrunifã§ã©ã³ãã åããŠãŸããç¹ã«æå³ã¯ãããŸããã
ASIN <- "B00YQTWQB2" # ASIN
page_range <- 1:69 # scrape pages 1 to 69
match_key <- tibble(n = page_range,
key = sample(page_range,length(page_range)))
lapply(page_range, function(i){
j <- match_key[match_key$n==i,]$key
message("Getting page ",i, " of ",length(page_range), "; Actual: page ",j) # Progress bar
Sys.sleep(runif(1, 2.5, 3.5)) # 3-second break
if((i %% 3) == 0){ # After every three scrapes... take another two second break
message("Taking a break...") # message on your console
Sys.sleep(runif(1, 2, 3)) # Take an additional 2-3 second break
}
scrape_amazon(ASIN = ASIN, page_num = j) # Scrape
}) %>%
bind_rows() -> x
# save data
write.csv(x, paste(Sys.Date(), "amazon", ASIN, "reviewutf.csv", sep="_"), fileEncoding = "utf-8")
# For windows, write.xlsx from the openxlsx package can be used to save the file in a good way without line shift.
write.xlsx(x, paste(Sys.Date(), "amazon", ASIN, "review.xlsx", sep="_"), asTable = FALSE, overwrite = FALSE)ããŸãåããŸããã
âã¬ãã¥ãŒãããã¹ãåæãã
ã¬ãã¥ãŒããŒã¿ã®é¢çœããšããã¯ã¬ãã¥ãŒã®ææ°ã«ãã£ãŠæ°ã«å
¥ã£ãæ¹ãšäžæºã ã£ãæ¹ãåããŠåæã§ãããšããã§ããååã®å©ç¹ã𿬠ç¹ãæŽãåºããŠè³Œå
¥ã®åèã«ããããäŒæ¥ã§ããã°è²©ä¿ããŒã¿ãæ¹åç¹ã®èå¯ã«äœ¿çšãããã§ããŸãã
ãŸãreview_starãæ°å€ããŒã¿ã«ããããã¹ãåæçšã«ã¬ãã¥ãŒã¿ã€ãã«ãšå
容ãçµåããŸãã
# â5 data
x %>%
mutate(review_star = as.numeric(review_star)) %>% # 颿°ã§ãæ°å€åããŠãŸãã念ã®ããæ°å€å
mutate(text = paste(x$review_title, x$review_text)) %>%
filter(review_star == 5) %>%
write.csv("nivea_5star_amazon.csv", fileEncoding = "SHIFT-JIS")
# <â3 data
x %>%
mutate(review_star = as.numeric(review_star)) %>%
mutate(text = paste(x$review_title, x$review_text)) %>%
filter(review_star <= 3) %>%
write.csv("nivea_under3star_amazon.csv", fileEncoding = "SHIFT-JIS")
å
šãŠããã®ãŸãŸåæããŠãè¯ãã®ã§ãããã¬ãã¥ãŒã§ç¥ãããæ
å ±ã¯ãè¯ãã£ãç¹ããšãæªãã£ãç¹ãã§ã¯ãªãã§ããããã
ã¬ãã¥ãŒããŒã¿ã¯è©äŸ¡ã®ææ°ã«ãã£ãŠæ°ã«å
¥ã£ãæ¹ãšäžæºã ã£ãæ¹ãåããŠåæã§ãããããè¯ãã£ãç¹ãšæªãã£ãç¹ã容æã«å¯èŠåã»èŠçŽããããšãã§ããŸãã
以åã®èšäºã§ã䜿çšããŸããRMeCabãšwordcloudã䟿å©ã§ãããä»åã¯ããã¹ãåæçšãœããã®KH_coderã䜿ã£ãŠâ5ïŒæ°ã«å ¥ã£ãæ¹ïŒãšâ3以äžïŒäžæºã ã£ãæ¹ïŒãããããããã¹ãåæããŠã¿ãŸãã
ã»â5ïŒå¥œè©ãªæ¹ã®ã¬ãã¥ãŒïŒ444ä»¶ïŒ
å
±èµ·ãããã¯ãŒã¯ã«ãããã©ããªé¢šã«äœ¿ãããŠããããäœãç®çãšããŠäœ¿ãããŠããããåãããŸãã
æãåºçŸé »åºŠãå€ãã¯ãŒããçšéã¯å¬ã®ä¹Ÿç¥å¯Ÿçã»ä¹Ÿç¥èã±ã¢ã«åç²§æ°ŽãšåãããŠäœ¿ããšããããšãèªã¿åããŸãã
ã»ããããå
¥ã£ãŠããŠã³ã¹ããè¯ã
ã»ã颚åäžãã«äœ¿çš
ã»ãããã§è©±é¡ã«ãªã£ãŠãããã賌å
¥ãã
ã»èèããæ¹å
ã»ãã³ãã¯ãªãŒã ãšããŠäœ¿çš
ã»å
šèº«ã«äœ¿çš
...ãªã©ãªã©ãè¯ãã£ãç¹ãçšéã广ãªã©ã®æ§ã
ãªæ
å ±ãèªã¿åããŸãã
æ°ã«å
¥ãããŠããæ¹ã«ã¯åºãæ¯æãããŠããæ§åã§ãã
ã»â3以äžãŒäžæºãªæ¹ã®ã¬ãã¥ãŒïŒ119ä»¶ïŒ
è©äŸ¡ãäœãæ¹ã®ã¬ãã¥ãŒãåæããããšã§ãã©ããã£ãç¹ã«äžæºãå€ãã®ããèŠçŽããããšãã§ããŸãã
ã»äŒžã°ãã«ãã
ã»èã«åããªã人ã¯ããã¿ãããããåºãïŒïŒïŒ
ã»é
éã«åé¡ããã£ãŠèãå¹ãã
ã»ããããè¿æã®è¬å±ã«å£²ã£ãŠãã®ã§Amazonã ãšå²é«
...ãªã©ãæªãã£ãç¹ãèªã¿åããŸãã
åœç¶ã§ãããèã«åããªãæ¹ãããã£ãããæ§åã§ãã
èª¬ææžãã®éãããŸãã¯ç®ç«ããªããšããã§è©ŠããŠãã䜿çšããæ¹ãè¯ãã§ãããã
é
éãäŸ¡æ Œçãååèªäœä»¥å€ã®äºãå
¥ã£ãŠããŸãã®ã§æ³šæãå¿
èŠã§ãã
âã¬ãã¥ãŒã®ã¯ãŒãã¯ã©ãŠããæã
ãã£ãšè³æã§äœ¿ããããªèŠãç®ã€ã³ãã¯ãã®ããå³ãäœããã...ãšããããšã§ã以åã®èšäºã§ãè¡ã£ãã¯ãŒãã¯ã©ãŠããæããŠã¿ãŸãã
ä»åã¯é«æå
çã®åèªææ
極æ§è¡šã䜿çšããŠããã¬ãã£ããªã¯ãŒããšããžãã£ããªã¯ãŒãã§è²åããããŠã¿ãŸãã
library(RMeCab)
# ã¿ã€ãã«ãšããã¹ããçµå
text <- x %>%
mutate(text = paste(x$review_title, x$review_text))
dat$text <- dat$text %>%
str_replace_all(pattern = '\\p{ASCII}',replacement = "")
dat_text <- dat$text %>%
na.omit() %>%
iconv(from = "UTF-8", to = "CP932") %>% # windowsã®ã¿Encodeã®å€æŽãå¿
èŠã§ãã
paste(collapse = "")
textfile <- tempfile()
write(dat_text, textfile)
unlink(textfile)
cloud <- cloud %>%
select(everything(), FREQ = starts_with("file")) %>% # 4åç®ã®file****....ãšããååãé·ãããFREQãžå€æŽ
arrange(desc(FREQ))
# æ¶ãããäžèŠãªã¯ãŒããèšå®
exclude_word = c("ãã","ãªã","ãã","ãã","ãã","â ","â¡","â¢","-","âª","NA","NANA","NANANA","NANANANA","ïŒ","#")
# åè©ãšåè©ã§è¯ãæãã®ãã€ãæ®ã
cloud2 <- cloud %>%
filter(grepl(pattern = "åè©|åè©", x = POS1) &
!grepl(pattern = "å©åè©|代åè©", x = POS1) &
!grepl(pattern = "éèªç«|æ¥å°Ÿ|æ°|代åè©", x = POS2)
) %>%
filter(!TERM %in% exclude_word)
# åèªææ
極æ§è¡šã®èªã¿èŸŒã¿
sowdic <- read.table("http://www.lr.pi.titech.ac.jp/~takamura/pubs/pn_ja.dic",sep=":",col.names=c("term","kana","pos","value"),colClasses=c("character","character","factor","numeric"),fileEncoding="Shift_JIS")
# åè©ããšã«valueãç°ãªãå Žåãããã®ã§ãã£ã€ããŠå¹³åå€ããšã
sowdic2 <- aggregate(value~term+pos,sowdic,mean)
sowdic2$value <- sowdic2$value + 0.4 # ããã©ã«ãã ãšãã¬ãã£ãå¯ãã«ãªãã®ã§valueãè²ãå¡ãåããããšããã§èª¿æŽ
word <- subset(cloud2,TERM %in% sowdic2$term)
word <- merge(word,sowdic2,by.x=c("TERM","POS1"),by.y=c("term","pos"))
word2 <- word %>%
mutate(color = if_else(value > -0.1, "skyblue", "salmon")) # è²ãå¡ãåããããšããã§valueãèª¿æŽ ãã£ãšif_elseãéããŠå¡åããŠããããã
# æç»
word2 %>%
select(TERM, FREQ) %>%
mutate(FREQ = ((FREQ-mean(FREQ))/(sd(FREQ)))+1) %>% # æåã®å€§ããã®ãã©ã³ã¹ã調æŽ
arrange(desc(FREQ)) %>%
slice(1:150) %>% # æç»ããç¯å²ãèšå®
wordcloud2(color = word2$color, minRotation = 0, maxRotation = 0, size = 1)ãã¬ãã£ããèµ€ãããžãã£ããéã§å¡åããŠã¿ãŸããã
åèªææ
極æ§è¡šã®ïœ°0.5以äžã§æ©æ¢°çã«å¡åããŠããã®ã§äžæè°ãªå¡åãã«ãªã£ãŠãããšããããããŸãããvalueã®å¡åãç¹ã調æŽãããå¡åãè²æ°ãå¢ããã°ãã£ãšè¯ãæãã«ãªãããã§ãã
âããžãã£ããªèšèãšãã¬ãã£ããªèšèã§åºçŸé »åºŠãæ£ã°ã©ãã«ãã
ããžãã£ããªèšèãšãã¬ãã£ããªèšèã®åºçŸé »åºŠãæ£ã°ã©ãã«ããŠã¿ãŸãã
# ããžãã£ããªèšèåºçŸé »åºŠïŒValue > 0.3ïŒ
p_word <- word2 %>%
filter(value>0.3) %>%
aggregate(FREQ~TERM+POS1, ., FUN=sum) %>%
arrange(desc(FREQ)) %>%
mutate(TERM = fct_reorder(TERM, -FREQ, .desc=TRUE)) %>%
slice(1:30) %>%
ggplot() +
geom_bar(aes(TERM,FREQ), stat = "identity", fill = "steelblue") +
coord_flip() +
ggplot2::theme_minimal(base_size = 16, base_family = "TPM")+
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text.y = element_text(face = c('bold'),
size = 14,
color = "steelblue")) +
labs(title = "æããèªå¥ã®åºçŸé »åºŠ",
subtitle = "",
caption = "",
y = "åºçŸé »åºŠ",
x = "èªå¥")
# ãã¬ãã£ããªèšèåºçŸé »åºŠïŒValue < 0.1ïŒ
n_word <- word2 %>%
filter(value < -0.1) %>%
aggregate(FREQ~TERM+POS1, ., FUN=sum) %>%
arrange(desc(FREQ)) %>%
mutate(TERM = fct_reorder(TERM, -FREQ, .desc=TRUE)) %>%
slice(1:30) %>%
ggplot() +
geom_bar(aes(TERM,FREQ), stat = "identity", fill = "salmon") +
coord_flip() +
ggplot2::theme_minimal(base_size = 16, base_family = "TPM")+
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text.y = element_text(face = c('bold'),
size = 14,
color = "salmon")) +
labs(title = "æãèªå¥ã®åºçŸé »åºŠ",
subtitle = "",
caption = paste("Amazonã¬ãã¥ãŒïŒããŒã¿ååŸæ¥", Sys.Date()),
y = "åºçŸé »åºŠ",
x = "èªå¥")
install.packages("gridExtra")
gridExtra::grid.arrange(p_word, n_word, nrow = 1)æããèªå¥ãšããŠã¯å¹æãäŒããæºè¶³ãå®å¿çãããã確ããªå¹æãæºè¶³æãå®å¿æãé«ãè©äŸ¡ãããŠããããšãåãããŸãã
æãèªå¥ãšããŠã¯ä¹Ÿç¥ãå¬ãèãããªã©ããŠãŒã¶ãŒã®äœ¿çšåæ©ãäžå
·åãçŸããŠããããšãåãããŸãã
ããžãã£ããªèšèãšãã¬ãã£ããªèšèã®åºçŸé »åºŠãèŠãããšã§ããŠãŒã¶ãŒãååã«ã©ããªè¯ãå°è±¡ãæã£ãŠããããæªãå°è±¡ãæã£ãŠããããå¯èŠåã»èŠçŽã§ããåå賌å
¥ãåååæã®åèã«ããããšãã§ãããã§ãã
âãŸãšã
ä»åã¯Amazonã¬ãã¥ãŒã®åéã»åæã宿œããŸããã
äœçŸä»¶ãã¬ãã¥ãŒããããšå
šãŠã«ç®ãéãããšã¯ã§ããŸããããæ¬æ¹æ³ã§ããã°æ
å ±ã®èŠçŽã詳现ã®è§£æã¯å®¹æã§ãã
æ°ãä»ããªããšãããªãã®ã¯ãã¬ãã¥ãŒãæ®ãæ¹ã¯å
šäœã®äžéšã§ããããšãååã«å¯ŸããŠè¯ããæªãã倧ããªæãå
¥ããããæ¹ã®ã¿ã§ããããšã§ãã
å€ãã®æ¹ã¯ã¬ãã¥ãŒãæ®ãã賌å
¥ã䜿çšããŠããããŸãã¯âè©äŸ¡ã®ã¿ã§ã¬ãã¥ãŒæç« ã¯æçš¿ããŠããŸããããã®ããæ¥µç«¯ãªæèŠã®ã¿ãæœåºãããå¯èœæ§ããããŸãã
ãŸãååã«ãã£ãŠã¯ãµã¯ã©ãã¬ãã¥ãŒãæçš¿ããŠããå ŽåããããŸããè©äŸ¡ã極端ã«åã£ãŠãããã¬ãã¥ãŒæç« ã®æ¥æ¬èªãäžæ§ã§ãã£ããäžèªç¶ãªå Žåã«ã¯æ³šæãå¿
èŠã§ãã
ããããŠãŒã¶ãŒç®ç·ãšããŠã®ååéžæäžãäŒæ¥ç®ç·ãšããŠã®ç ç©¶éçºã»ããŒã±ãã£ã³ã°äžããŠãŒã¶ãŒã®å£°ã貎éã§ããæçšã§ããããšã¯å€ãããããŸããã
䜿ããæ å ±ã¯ææçŸ©ã«äœ¿ã£ãŠã楜ãããè²·ãç©ã»èª¿æ»ããŸãããïŒ
âåè
ã»ãèŠçŽãAmazonã®ã¹ã¯ã¬ã€ãã³ã°ã¯æ¬åœã«çŠæ¢ïŒèŠçŽã確èªããŠã¿ã https://teshi-learn.com/2020-08/amazon-scraping-regulation/
ã»RSelenium: Basics https://rpubs.com/johndharrison/RSelenium-Basics
ã»Vignette: Scraping Amazon Reviews in R https://martinctc.github.io/blog/vignette-scraping-amazon-reviews-in-r/
ã»Docker-composeã«ããRStudioç°å¢ã®æ§ç¯ãšç®¡ç https://qiita.com/g-k/items/42751a1839fbea25045f
ã»RSelenium ã®ããœã https://qiita.com/TsuchiyaYutaro/items/5e0933eda59d67d4b39e
ã»KH Coder https://khcoder.net/




