本文記錄了使用rvest & RSelenium 包進(jìn)行爬蟲(chóng)與網(wǎng)頁(yè)渲染的相關(guān)知識(shí)點(diǎn)及本人的編程操作過(guò)程。涉及到基本爬取操作、爬取缺失部分如何處理、操作網(wǎng)頁(yè)過(guò)濾等步驟。
本人非計(jì)算機(jī)專(zhuān)業(yè),如有措辭不慎敬請(qǐng)?zhí)岢觥?/p>
爬蟲(chóng)目標(biāo)
這學(xué)期為了湊學(xué)分,選了一門(mén)R語(yǔ)言的課,才發(fā)現(xiàn)R語(yǔ)言遠(yuǎn)比我們想象的要強(qiáng)大。至少問(wèn)過(guò)身邊同學(xué),他們都不知道R還能爬蟲(chóng)qaqq
為了防止自己學(xué)過(guò)就忘..寫(xiě)一篇blog記錄一下被rvest與Rselenium折磨的這一個(gè)周的成果。
以下是代碼爬取的要求:
- 從IMDb首頁(yè)出發(fā),提取該榜單所有電影的user reviews各100條
- 使用Rselenium進(jìn)行網(wǎng)頁(yè)操作:User Reviews可能存在劇透(Spoilers),需要進(jìn)行隱藏(Hide Spoilers);每個(gè)review網(wǎng)頁(yè)初始顯示評(píng)論25條,需要點(diǎn)擊Load More加載更多評(píng)論。
- 保存每條評(píng)論及對(duì)應(yīng)的電影評(píng)分。Tips:存在只有內(nèi)容沒(méi)有評(píng)分的評(píng)論。
1 爬蟲(chóng)知識(shí)整理
在進(jìn)行實(shí)例展示之前,先回顧一下R語(yǔ)言爬蟲(chóng)需要用到的知識(shí)。主要涉及兩個(gè)庫(kù):rvest與rselenium
1.1 Rvest包
Rvest功能強(qiáng)大,語(yǔ)法簡(jiǎn)潔,用起來(lái)真的很順手。由于本文只用到了讀取與提取的API,在這里也只對(duì)相關(guān)的API進(jìn)行說(shuō)明。
編號(hào) | 函數(shù)名 | 作用 |
---|---|---|
1 | read_html() | 根據(jù)給定的url,【讀取其html文檔】 |
2 | html_nodes() | 提取html文檔中的對(duì)應(yīng)【節(jié)點(diǎn)與元素】 |
3 | html_name() | 提取【標(biāo)簽名稱(chēng)】 |
4 | html_text() | 提取標(biāo)簽內(nèi)的【文本內(nèi)容】 |
5 | html_attrs() | 提取【所有的屬性名稱(chēng)及其內(nèi)容】 |
6 | html_attr() | 提取【指定】屬性的內(nèi)容 |
7 | html_table() | 將網(wǎng)頁(yè)數(shù)據(jù)表的數(shù)據(jù)解析到R的dataframe中 |
8 | html_form() | 提取表單 |
我們結(jié)合具體環(huán)境看一下部分代碼的作用:
1.1.1 read_html()
在read_html()的參數(shù)部分,我們給定一個(gè)網(wǎng)址,這個(gè)函數(shù)就能夠返回這個(gè)網(wǎng)址指向的HTML網(wǎng)頁(yè)數(shù)據(jù)。
# 使用read_html()下載IMDB首頁(yè)的html文件
url <- "https://www.imdb.com/chart/moviemeter?sort=rk,asc&mode=simple&page=1"
webpage <- read_html(url)
webpage
# 返回結(jié)果:
#{html_document}
#<html xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml">
#[1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n<script #...
#[2] <body id="styleguide-v2" class="fixed">\n <img height="1" width="1" style #...
1.1.2 html_*()
不可避免的,我們需要了解一些HTML的知識(shí):
下圖中<>框起的部分就是標(biāo)簽,而每個(gè)<>后對(duì)應(yīng)的第一個(gè)紫色英文單詞就是我們所說(shuō)的標(biāo)簽(如div);黃色部分是屬性名稱(chēng)(如name, class);屬性名稱(chēng) = “?” ,引號(hào)中的內(nèi)容就是我們說(shuō)的屬性內(nèi)容;黑色部分就是顯示的文本
將各部分拆解了以后,我們也就能明白每個(gè)函數(shù)能提取出什么來(lái)——
html_nodes():使用標(biāo)簽進(jìn)行定位;
html_attrs():提取等號(hào)后的屬性內(nèi)容;
html_text():提取“黑色部分”
1. html_nodes()
參數(shù)有二:
編號(hào) | 參數(shù) | 說(shuō)明 |
---|---|---|
1 | x | 可以是html文件,也可以是之前提取過(guò)的nodes文件 |
2 | css/xpath | 需要提取的節(jié)點(diǎn)位置 |
比較重要的就是節(jié)點(diǎn)位置的選取,也就是我們的第二個(gè)參數(shù)部分。以下圖為例,我們想提取header部分,我們可以采用以下方法提?。?/p>
① css/xpath:直接復(fù)制型
?如上圖所示,我們將鼠標(biāo)移到對(duì)應(yīng)元素的html文檔上面,點(diǎn)擊右鍵→復(fù)制→復(fù)制selector/復(fù)制XPath,即可獲得路徑如下→GET!
#css:
"#main > section > div.lister > div.header"
#xpath:
"http://*[@id="main"]/section/div[2]/div[1]"
② 標(biāo)簽.屬性內(nèi)容 型
標(biāo)簽:div;屬性:name;屬性內(nèi)容:header。將屬性(黃色部分)劃了去,直接div.header→GET!定位成功!
P.S 有時(shí)如果掌握不好這個(gè)方法,也可以將元素選擇鼠標(biāo)放到對(duì)應(yīng)的位置上,就會(huì)自動(dòng)顯示出節(jié)點(diǎn)。比如說(shuō)圖中左側(cè)的div.header
③? 標(biāo)簽(空格)標(biāo)簽 型
這里換一個(gè)例子比較合適。使用薄荷健康食品庫(kù)的網(wǎng)頁(yè)(因?yàn)楸容^簡(jiǎn)單)。
?如果我們想爬取食品類(lèi)別和烹飪方式,發(fā)現(xiàn)對(duì)應(yīng)元素標(biāo)簽<li> <a href....>
那么我們的節(jié)點(diǎn)就可以寫(xiě)為:
cooking_style <- start %>% html_nodes("li a")
# 返回結(jié)果
#{xml_nodeset (40)}
# [1] <a href="/foods/list?ifood_group_id=1">谷薯主食</a>
# [2] <a href="/foods/list?ifood_group_id=2">肉蛋及制品</a>
# ...
2. html_text():
這個(gè)函數(shù)正如我們上面所說(shuō),爬的是黑色部分——文本。
那還是薄荷健康的例子,在獲取了cooking_style這個(gè)具體的元素塊了以后,我們對(duì)文本進(jìn)行提?。?/p>
cooking_style <- start %>% html_nodes("li a") %>% html_text(trim = TRUE)
# 返回結(jié)果
# [1] "谷薯主食" "肉蛋及制品" "奶類(lèi)及制品" "蔬果和菌藻" "堅(jiān)果及制品" "飲料" "油類(lèi)及制品"
# [8] "調(diào)味品" "零食及冷飲" "其它" "扒" "拔絲" "炒" "燉"
# ...
trim = TRUE能夠?qū)⑽谋局械目崭襁M(jìn)行剔除,這樣獲得的文本也會(huì)更加規(guī)范。
3. html_attrs():
這個(gè)函數(shù)最常見(jiàn)的用途一定是獲得其他網(wǎng)頁(yè)的url了!而url出現(xiàn)的位置:a href = "巴拉巴拉巴" 就是我們說(shuō)的【引號(hào)中的部分】
在薄荷健康中,如果我們想獲取每種食物or烹飪方式的網(wǎng)址,那么:
website <- start %>% html_nodes("div.knowledgeTagTableviewBorder a") %>%
html_attr("href")
# 返回結(jié)果:
# website
# [1] "/foods/list?ifood_group_id=1" "/foods/list?ifood_group_id=2" "/foods/list?ifood_group_id=3"
# [4] "/foods/list?ifood_group_id=4" "/foods/list?ifood_group_id=5" "/foods/list?ifood_group_id=6"
But 我們一看,這個(gè)網(wǎng)址和實(shí)際上的不一樣嘛!
這就需要我們使用paste()進(jìn)行網(wǎng)址的補(bǔ)全:
website <- paste("http://m.boohee.com/", website, sep = "")
# 返回結(jié)果:
# website
# [1] "http://m.boohee.com//foods/list?ifood_group_id=1"
# [2] "http://m.boohee.com//foods/list?ifood_group_id=2"
# ...
這樣就對(duì)了!可以訪問(wèn)這些鏈接了!
while Rvest包中還存在著一些處理亂碼、進(jìn)行行為模擬操作的API。雖然我沒(méi)有用到,但在這里還是展示一下:
分類(lèi) | 編號(hào) | 函數(shù)名 | 作用 |
---|---|---|---|
亂碼處理 *_encoding() |
1 | guess_encoding() | 用來(lái)探測(cè)文檔的編碼,方便我們?cè)谧x入HTML文檔時(shí)設(shè)置正確的編碼格式 |
2 | repair_encoding() | 修復(fù)HTML文檔讀入后的亂碼問(wèn)題 | |
行為模擬(如輸入賬號(hào)、密碼等) | 1 | set_values() | 修改表單 |
2 | submit_form() | 提交表單 | |
3 | html_session() | 模擬HTML瀏覽器會(huì)話 | |
4 | jump_to() | 得到相對(duì)鏈接或絕對(duì)鏈接 | |
5 | follow_link() | 通過(guò)表達(dá)式找到當(dāng)前頁(yè)面下的鏈接 | |
6 | session_history() | 歷史記錄導(dǎo)航工具 |
1.2 RSelenium包
Rvest包是幫助你對(duì)給定網(wǎng)頁(yè)直接進(jìn)行信息爬取的工具,而RSelenium包則是進(jìn)行“網(wǎng)頁(yè)自動(dòng)控制”的工具。它能夠自動(dòng)打開(kāi)網(wǎng)頁(yè),并根據(jù)我們的代碼指令進(jìn)行比如說(shuō)像點(diǎn)擊、查詢特定信息等活動(dòng),幫助我們進(jìn)行更精確的爬蟲(chóng)操作。
P.S rselenium包是基于JAVA存在的,因此在使用之前需要提前下載安裝Java SE Development Kit
加載如下packages:
library(tidyverse)
library(rvest)
# install.packages("RSelenium")
library(RSelenium)
# install.packages("netstat")
library(netstat) # we are going to use the function free_port() in this package
1.2.1 打開(kāi)瀏覽器(谷歌chrome)
rs_driver_object <- rsDriver(browser = "chrome",chromever = "106.0.5249.21",verbose = F, port = free_port())
remDr <- rs_driver_object$client
可以通過(guò)在Chrome瀏覽器中鍵入“chrome://settings/help”或在R中輸入binman::list_versions("chromedriver")查看自己的瀏覽器版本。
1.2.2 網(wǎng)頁(yè)導(dǎo)航
1. 打開(kāi)一個(gè)網(wǎng)頁(yè):
remDr$navigate("http://www.imdb.com/title/tt1856010/reviews")
2. 刷新操作:
remDr$refresh()
3. 得到當(dāng)前頁(yè)面網(wǎng)址:
remDr$getCurrentUrl()
這個(gè)功能還是需要說(shuō)一下的。正如我們之前所說(shuō),Rvest包與RSelenium包功能不同,所以也不能用一種邏輯使用兩種工具包。
我們?cè)趯?duì)一個(gè)網(wǎng)頁(yè)進(jìn)行所有selenium操作后,需要注意,此時(shí)的url和之前是不同的,會(huì)顯示很多條件。
?
?這時(shí),我們需要使用remDr$getCurrentUrl()語(yǔ)句得到當(dāng)前的url,然后使用read_html()讀入其HTML文件,進(jìn)行爬取工作。
4. 前進(jìn)、回撤動(dòng)作
remDr$goBack()
remDr$goForward()
1.2.3 定位HTML元素
我們可以使用id, name, class 或css selector、xpath等定位方式尋找我們需要的元素。
使用語(yǔ)句findElement()即可。第一個(gè)參數(shù)為定位方式,第二個(gè)參數(shù)為元素名稱(chēng)。
# id方式
loadmore <- remDr$findElement(using = "id", value = "load-more-trigger")
# css方式
loadmore <- remDr$findElement(using = "css", "#load-more-trigger")
1.2.4 將操作傳遞給元素
1. 點(diǎn)擊操作click
(1) 直接單擊操作:clickElement()
loadmore$clickElement()
(2) 如果要控制點(diǎn)擊的左右鍵:click(buttonId=0/1/2)
click(buttonId = 0)
0單擊左鍵,1單擊滾動(dòng)條,2單擊右鍵。
(3) 雙擊:doubleclick()
doubleclick(buttonId = 0)
2. 模仿鼠標(biāo)的一些其他操作,如使用滾輪滑到頁(yè)面最下端
bottom <- remDr$findElement("css", "body")
bottom$sendKeysToElement(list(key = "end")) #“滾動(dòng)到最下端”
sendKeysToElement()功能主要用于在指定元素中輸入文本。一般是先使用findelement()定位到該元素,再使用sendKeysToElement()進(jìn)行內(nèi)容與操作的傳遞。sendKeysToElement()的參數(shù)必須為list形式。
3. 傳遞文本信息
query <- remDr$findElement(using = "css", "#suggestion-search")
query$sendKeysToElement(list("The Godfather", key = "enter"))
# 輸入文本the godfather, 然后回車(chē)搜索
1.2.5?檢索頁(yè)面源代碼
正如前面getCurrentUrl()步驟所說(shuō),在進(jìn)行完一系列操作后,我們需要獲得此時(shí)的url,然后才能進(jìn)行接下來(lái)的爬取工作。
page_source <- remDr$getPageSource()[[1]]
remDr$getCurrentUrl()
page_source %>% read_html()
2. 爬取IMDb電影網(wǎng)的數(shù)據(jù)
(1)安裝相應(yīng)的package
library(tidyverse)
library(rvest)
library(xml2)
library(RSelenium)
library(netstat)
(2)從電影網(wǎng)首頁(yè)出發(fā),獲取其HTML文件
# 使用read_html()下載IMDB首頁(yè)的html文件
webpage <- read_html("https://www.imdb.com/chart/moviemeter?sort=rk,asc&mode=simple&page=1")
webpage
(3)使用html_nodes() %>% html_attrs(),獲取排行榜中各電影主頁(yè)的鏈接
# 從首頁(yè)信息出發(fā),提取100部電影首頁(yè)的網(wǎng)址
website <- html_nodes(webpage, "td.titleColumn a") %>% html_attr("href")
# 使用paste()將url補(bǔ)充完整
website_title <- paste("http://www.imdb.com",website,sep = "")
由于提取的URL缺少部分內(nèi)容,我們使用字符串拼接paste()函數(shù)將其補(bǔ)全
paste(...,?sep?=?"?",?collapse?=?NULL) ① ...:需要拼接的文本 ② sep:使用什么符號(hào)將文本分隔開(kāi),default = 空格 ③ collapse:若不指定值(default = NULL),則結(jié)果返回由sep中指定符號(hào)連接而成的字符型向量;若指定值(如“,”),則將字符型向量間通過(guò)collapse的值連接形成一個(gè)字符串 |
(4) 由于各電影主頁(yè)中包含多種“評(píng)論”的鏈接,我們需要在各電影首頁(yè)提取user reviews的網(wǎng)址
review_page <- c()
for (i in 1:100){
all_page <- read_html(website_title[i])
reviews <- html_nodes(all_page, ".isReview") %>% html_attr("href")
# 上一步會(huì)返回user/critic/metascore 三個(gè)網(wǎng)址,在這里只保留返回的第一個(gè)結(jié)果
review_page <- c(review_page, reviews[1])
}
# 補(bǔ)充url
review_title <- paste("http://www.imdb.com",review_page,sep = "")
review_title
·定義review_page()為一個(gè)空的向量,在每次讀取URL后將新的內(nèi)容補(bǔ)充進(jìn)去。
·使用paste補(bǔ)充網(wǎng)址,將結(jié)果存儲(chǔ)到review_title變量中。
(5)使用RSelenium對(duì)網(wǎng)頁(yè)進(jìn)行操作
# 連接chrome瀏覽器
rs_driver_object <- rsDriver(browser = "chrome", chromever = "106.0.5249.61",
verbose = F, port = free_port())
remDr <- rs_driver_object$client
# total用于存儲(chǔ)所有頁(yè)面的評(píng)論與評(píng)分信息
total <- data.frame()
在100部電影的user reviews頁(yè)面,我們需要勾選“隱藏劇透”的單選框,并通過(guò)點(diǎn)擊load more鍵,獲取100條評(píng)論及其評(píng)分。
① 對(duì)于review_title中的網(wǎng)址,我們需要將其轉(zhuǎn)換為character格式,然后再進(jìn)行navigate導(dǎo)航。
② 隱藏劇透:使用findElement進(jìn)行定位,通過(guò)clickElement點(diǎn)擊單選框
③ 加載更多:
A. 在不點(diǎn)擊的情況下,一個(gè)界面提供25條評(píng)論;
B. 確認(rèn)該電影的評(píng)論數(shù)量:使用read_html()讀取界面HTML文檔,定位至評(píng)論數(shù)量。由于返回的文本格式為“xxx Reviews”,我們使用字符串截取substr()函數(shù)進(jìn)行文本提取。
substr(s, first, last) ① s:需要處理的字符串 ② first:起始位置(包含在內(nèi)) ③ last:結(jié)束位置(包含在內(nèi)) 【nchar:確認(rèn)字符串長(zhǎng)度】 |
我們需要將上一步的評(píng)論數(shù)量轉(zhuǎn)化為數(shù)值型,以便進(jìn)行下一步判斷。如果評(píng)論數(shù)量上千會(huì)出現(xiàn)分位符,我們使用替換字符gsub()函數(shù)將其消掉。
gsub("目標(biāo)字符", "替換字符", 對(duì)象) |
然后使用if else語(yǔ)句進(jìn)行判斷。
(6) 爬取100條評(píng)論:
將Rselenium操作后的網(wǎng)址保存,再使用read_html讀取其HTML文件。將評(píng)論區(qū)的一整塊保存到all變量中。
對(duì)于該電影的所有評(píng)論(seq_along(all)),如果評(píng)分存在,則讀取其評(píng)分與評(píng)論內(nèi)容。
判斷評(píng)分是否存在的代碼為:length != 0 ,如果不存在則是空值,其長(zhǎng)度為0,也就不進(jìn)行接下來(lái)的操作。
使用rbind()函數(shù),將每一次讀取的評(píng)分與評(píng)論內(nèi)容補(bǔ)充到dataframe中。
(record用于存儲(chǔ)某一部電影的內(nèi)容;total用于存儲(chǔ)全部?jī)?nèi)容)
for (i in 1:100){
url <- as.character(review_title[i])
remDr$navigate(url)
# remDr$refresh()
# 隱藏劇透
hide <- remDr$findElement(using = "xpath", value = "http://*[@id='main']/section/div[2]/div[1]/form/div/div[1]/label/span[1]")
hide$clickElement()
# 加載更多
thispage <- read_html(url)
numreview <- c()
# 讀取當(dāng)前頁(yè)面的評(píng)論數(shù)量,用于分析如何進(jìn)行點(diǎn)擊操作
numreview <- html_nodes(thispage,"#main > section > div.lister > div.header > div > span") %>%
html_text(trim = TRUE) %>% substr(1,nchar(.)-8)
numreview <- as.numeric(gsub(",","", numreview))
if (numreview >= 25){
loadmore <- remDr$findElement(using = "id", value = "load-more-trigger")
loadmore$clickElement()# 一次會(huì)出現(xiàn)25條評(píng)論,點(diǎn)4次即可獲得100條評(píng)論
Sys.sleep(2) # 由于網(wǎng)速限制,中間需要有間隔時(shí)間暫停
loadmore$clickElement()
Sys.sleep(2)
loadmore$clickElement()
Sys.sleep(2)
loadmore$clickElement()
} else {
Sys.sleep(1)
}
# 獲取前面步驟已完成后的網(wǎng)址,用于后續(xù)爬取工作
page_source <- remDr$getPageSource()[[1]]
remDr$getCurrentUrl()
page_source <- read_html(page_source)
# all中存儲(chǔ)了每一個(gè)評(píng)論塊的xml文件
all <- html_nodes(page_source, "div.review-container")
# records用于存儲(chǔ)當(dāng)前頁(yè)面的評(píng)論與評(píng)分信息
records <- data.frame()
# 爬取當(dāng)前頁(yè)面的評(píng)論與評(píng)分
for (j in seq_along(all)){
#設(shè)置判斷:避免評(píng)分空值
if (html_nodes(all[j], "span.rating-other-user-rating") %>% length != 0){
nodes <- html_nodes(all[j], "span.rating-other-user-rating") %>%
html_text(trim = TRUE) %>% substr(1,nchar(.)-3)
notes <- html_nodes(all[j], "div.text.show-more__control")%>% html_text(trim = TRUE)
}
# 設(shè)置一個(gè)新的dataframe,用于存儲(chǔ)每一次新爬取的評(píng)論與評(píng)分信息
new_record <- data.frame(RANKS = nodes, REVIEWS = notes)
# 將新的信息組合到records中。
records <- rbind(records, new_record)
}
# 每一次循環(huán)的信息保存到total的數(shù)據(jù)框中
total <- rbind(total, records)
}
# 保存爬取的所有文件
write_excel_csv(total, "111.csv")
在這里使用write_excel_csv()函數(shù)保存內(nèi)容。用了其他的保存文件函數(shù)都出現(xiàn)了亂碼...設(shè)置encoding = UTF8也不行,只有這個(gè)函數(shù)是好用的。如果出現(xiàn)亂碼大家也可以自行百度,多試試幾個(gè)函數(shù)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-782464.html
這樣,我們就爬取了100部電影及其評(píng)論文本&評(píng)論評(píng)分!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-782464.html
到了這里,關(guān)于R語(yǔ)言爬蟲(chóng)實(shí)例 初學(xué)者自用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!