前言
網頁上的資料和資訊正在呈指數級增長。如今我們都使用谷歌作為知識的首要來源——無論是尋找對某地的評論還是了解新的術語。所有這些資訊都已經可以從網上輕而易舉地獲得。
網絡中可用資料的增多為資料科學家開辟了可能性的新天地。我非常相信網頁爬取是任何一個資料科學家的必備技能。在如今的世界裡,我們所需的資料都在網際網路上,使用它們唯一受限的是我們對資料的擷取能力。有了本文的幫助,您定會克服這個困難。
網上大多數的可用資料并不容易擷取。它們以非結構化的形式(html格式)表示,并且不能下載下傳。是以,這便需要知識和專業技能來使用它們。
我在本文中準備帶您走一遍用r來實作網頁爬取的過程。讓您學會如何使用網際網路上任何類型的可用資料。
先決條件
用r來進行網頁爬取的先決條件分為兩個:
要進行網頁爬取,您必須具備r語言的操作知識。如果您正處于初識階段或者想重新整理基礎知識,我強烈建議您按這個學習路徑(https://www.analyticsvidhya.com/learning-paths-data-science-business-analytics-business-intelligence-big-data/learning-path-r-data-science/)學習r語言。在本文中,我們将使用r語言中由hadley wickham撰寫的“rvest”包。您可以從下面的連結(https://cran.r-project.org/web/packages/rvest/rvest.pdf)獲得rvest包的文檔。請確定您安裝了這個包。如果您現在還沒有這個包,請按下面的代碼來安裝。
install.packages('rvest')
此外,如果有關于html和css的知識就更好了。我能找到的關于學習html和css的最好資源在這裡(http://flukeout.github.io)。根據觀察而言大多數資料科學家對于html和css不是那麼精通。是以,我們會利用一個名為“selector gadget”的開源軟體,對所有人來講,用它來執行網頁爬取是足夠的。您可以從這裡(http://selectorgadget.com)通路和下載下傳selector gadge的擴充程式。請確定跟随該網站上的訓示來安裝這個擴充程式。我已經完成了這一步,現在正在使用谷歌chrome,并且可以通過chrome右上角的擴充欄上的這個圖示使用它。
有了它,隻需要輕輕的點選,您便可以選擇網站的任何部分并獲得相關标簽。請注意:這是一個實際學習html和css并手動操作的方法。但是,要掌握網頁爬取,我強烈建議您學習html和css以更好地了解和體味在搜尋引擎背後發生的故事。
使用r語言實作網頁爬取
現在,讓我們開始爬取imdb網站中2016年上映的100部最受歡迎的電影。您可以點選這裡http://www.imdb.com/search/title?count=100&release;_date=2016,2016&title;_type=feature通路網站。
#加載rvest包
library('rvest')
#定義需要爬取網站的url
url <- 'http://www.imdb.com/search/title?count=100&release;_date=2016,2016&title;_type=feature'
#從網站中讀取html代碼
webpage <- read_html(url)
現在,我們将從這個網站上爬取以下資料。
rank:電影排名(1-100),包括2016年上映的100個最受歡迎的電影。
title:電影标題。
description:電影描述。
runtime:電影時長。
genre:電影類型。
rating:電影的imdb評分(使用者打分)。
metascore:電影在imdb網站上的metascore評分(評論家打分)。
votes:電影贊成票數。
gross_earning_in_mil:電影總收入,以百萬為機關。
director:電影的主要導演。注意,如果有多個導演,我隻選取第一個。
actor:電影的主要演員。注意,如果有多個演員,我隻選取第一個。
這是一個包含如何排列所有這些字段的截圖。
步驟1:現在,我們先來爬取rank字段。為此,我們将使用selector gadget來擷取包含排名的特定css選擇器。您可以在浏覽器中點選這個擴充程式,并用光标選擇排名字段。
請確定所有的排名都被選中。您可以選擇更多的排名部分,以防您無法擷取所有這些排名,也可以通過單擊所選部分以取消選擇,用以確定隻突出了您想要爬取的内容。
步驟2:當您确定已正确選擇後,您需要複制相應的css選擇器,這可以在底部中心檢視。
步驟3:當您知道css選擇器已包含了排名順序之後,您可以使用這個簡單的r語言代碼來擷取所有的排名:
#使用css選擇器來爬取排名部分
rank_data_html <- html_nodes(webpage,'.text-primary')
#将排名資料轉化為文本
rank_data <- html_text(rank_data_html)
#讓我們來看看排名
head(rank_data)
[1] "1." "2." "3." "4." "5." "6."
步驟4:當您有了資料後,請確定它看起來是您所需的格式。我在對資料進行預處理,将其轉換為數字格式。
#資料預處理:将排名轉換為數字格式
rank_data<-as.numeric(rank_data)
#我們再來看看排名
[1] 1 2 3 4 5 6
步驟5:現在您可以清除選擇器部分并選擇所有标題。您可以直覺地檢查所有标題是否被選中。使用您的光标進行任何所需的添加和删除。我在這裡做了同樣的事情。
步驟6:再一次,我有了相應标題的css選擇器-- .lister-item-essay-header a。我将使用該選擇器和以下代碼爬取所有标題。
#使用css選擇器來爬取标題部分
title_data_html <- html_nodes(webpage,'.lister-item-essay-header a')
#将标題資料轉化為文本
title_data <- html_text(title_data_html)
#讓我們來看看标題
head(title_data)
[1] "sing" "moana" "moonlight" "hacksaw ridge"
[5] "passengers" "trolls"
步驟7:在下面的代碼中,我對description、runtime、genre、rating、metascore、votes、gross_earning_in_mil、director和actor資料做了同樣的操作。
#使用css選擇器來爬取描述部分
description_data_html <- html_nodes(webpage,'.ratings-bar+ .text-muted')
#将描述資料轉化為文本
description_data <- html_text(description_data_html)
#讓我們來看看描述資料
head(description_data)
[1] "\nin a city of humanoid animals, a hustling theater impresario's attempt to save his theater with a singing competition becomes grander than he anticipates even as its finalists' find that their lives will never be the same."
[2] "\nin ancient polynesia, when a terrible curse incurred by the demigod maui reaches an impetuous chieftain's daughter's island, she answers the ocean's call to seek out the demigod to set things right."
[3] "\na chronicle of the childhood, adolescence and burgeoning adulthood of a young, african-american, gay man growing up in a rough neighborhood of miami."
[4] "\nwwii american army medic desmond t. doss, who served during the battle of okinawa, refuses to kill people, and becomes the first man in american history to receive the medal of honor without firing a shot."
[5] "\na spacecraft traveling to a distant colony planet and transporting thousands of people has a malfunction in its sleep chambers. as a result, two passengers are awakened 90 years early."
[6] "\nafter the bergens invade troll village, poppy, the happiest troll ever born, and the curmudgeonly branch set off on a journey to rescue her friends.
#data-preprocessing: removing '\n'
#資料預處理:去掉'\n'
description_data<-gsub("\n","",description_data)
#let's have another look at the description data
#我們再來看看描述資料
[1] "in a city of humanoid animals, a hustling theater impresario's attempt to save his theater with a singing competition becomes grander than he anticipates even as its finalists' find that their lives will never be the same."
[2] "in ancient polynesia, when a terrible curse incurred by the demigod maui reaches an impetuous chieftain's daughter's island, she answers the ocean's call to seek out the demigod to set things right."
[3] "a chronicle of the childhood, adolescence and burgeoning adulthood of a young, african-american, gay man growing up in a rough neighborhood of miami."
[4] "wwii american army medic desmond t. doss, who served during the battle of okinawa, refuses to kill people, and becomes the first man in american history to receive the medal of honor without firing a shot."
[5] "a spacecraft traveling to a distant colony planet and transporting thousands of people has a malfunction in its sleep chambers. as a result, two passengers are awakened 90 years early."
[6] "after the bergens invade troll village, poppy, the happiest troll ever born, and the curmudgeonly branch set off on a journey to rescue her friends."
#使用css選擇器來爬取電影時長部分
runtime_data_html <- html_nodes(webpage,'.text-muted .runtime')
#将時長資料轉化為文本
runtime_data <- html_text(runtime_data_html)
#讓我們來看看時長
head(runtime_data)
[1] "108 min" "107 min" "111 min" "139 min" "116 min" "92 min"
#資料預處理:去掉'mins',并轉換為數字格式
runtime_data<-gsub(" min","",runtime_data)
runtime_data<-as.numeric(runtime_data)
#我們再來看看時長資料
#使用css選擇器來爬取電影類型部分
genre_data_html <- html_nodes(webpage,'.genre')
#将類型資料轉化為文本
genre_data <- html_text(genre_data_html)
#讓我們來看看類型
head(genre_data)
[1] "\nanimation, comedy, family "
[2] "\nanimation, adventure, comedy "
[3] "\ndrama "
[4] "\nbiography, drama, history "
[5] "\nadventure, drama, romance "
[6] "\nanimation, adventure, comedy "
genre_data<-gsub("\n","",genre_data)
#資料預處理:去掉多餘的空格
genre_data<-gsub(" ","",genre_data)
#隻選取每一部電影的第一種類型
genre_data<-gsub(",.*","",genre_data)
#将每種類型從文本轉換為因子
genre_data<-as.factor(genre_data)
#我們再來看看類型資料
[1] animation animation drama biography adventure animation
10 levels: action adventure animation biography comedy crime drama ... thriller
#使用css選擇器來爬取imdb評分部分
rating_data_html <- html_nodes(webpage,'.ratings-imdb-rating strong')
#将評分資料轉化為文本
rating_data <- html_text(rating_data_html)
#讓我們來看看評分
head(rating_data)
[1] "7.2" "7.7" "7.6" "8.2" "7.0" "6.5"
#資料預處理:将評分轉換為數字格式
rating_data<-as.numeric(rating_data)
#我們再來看看評分資料
[1] 7.2 7.7 7.6 8.2 7.0 6.5
#使用css選擇器來爬取贊成票部分
votes_data_html <- html_nodes(webpage,'.sort-num_votes-visible span:nth-child(2)')
#将贊成票資料轉化為文本
votes_data <- html_text(votes_data_html)
#讓我們來看看贊成票資料
head(votes_data)
[1] "40,603" "91,333" "112,609" "177,229" "148,467" "32,497"
#資料預處理:去掉逗号
votes_data<-gsub(",","",votes_data)
#資料預處理:将贊成票資料轉換為數字格式
votes_data<-as.numeric(votes_data)
#我們再來看看贊成票資料
[1] 40603 91333 112609 177229 148467 32497
#使用css選擇器來爬取導演部分
directors_data_html <- html_nodes(webpage,'.text-muted+ p a:nth-child(1)')
#将導演資料轉化為文本
directors_data <- html_text(directors_data_html)
#讓我們來看看導演資料
head(directors_data)
[1] "christophe lourdelet" "ron clements" "barry jenkins"
[4] "mel gibson" "morten tyldum" "walt dohrn"
#資料預處理:将導演資料轉換為因子
directors_data<-as.factor(directors_data)
#使用css選擇器來爬取演員部分
actors_data_html <- html_nodes(webpage,'.lister-item-content .ghost+ a')
#将演員資料轉化為文本
actors_data <- html_text(actors_data_html)
#讓我們來看看演員資料
head(actors_data)
[1] "matthew mcconaughey" "auli'i cravalho" "mahershala ali"
[4] "andrew garfield" "jennifer lawrence" "anna kendrick"
#資料預處理:将演員資料轉換為因子
actors_data<-as.factor(actors_data)
但是,我想讓您緊跟我看看當我對metascore評分資料做同樣的事情時會發生什麼。
#使用css選擇器來爬取metascore評分部分
metascore_data_html <- html_nodes(webpage,'.metascore')
#将metascore資料轉化為文本
metascore_data <- html_text(metascore_data_html)
#讓我們來看看metascore
data head(metascore_data)
[1] "59 " "81 " "99 " "71 " "41 "
[6] "56 "
#資料預處理:去掉metascore中多餘的空格
metascore_data<-gsub(" ","",metascore_data)
#讓我們檢查一下metascore資料的長度
length(metascore_data)
[1] 96
步驟8:我們正在爬取100部電影的資料,而metascore評分資料的長度是96。原因是因為有4部電影沒有相應的metascore字段。
步驟9:它是在爬取任何網站時都會發生的實際情況。不幸的是,如果我們簡單地添加na到最後4個條目,它将metascrore資料中的na映射到第96到100個電影,而實際上,資料丢失的是其他的一些電影。經過直覺地檢查,我發現缺失的是電影39、73、80和89的metascore資料。我寫了以下函數來解決這個問題。
for (i in c(39,73,80,89)){
a<-metascore_data[1:(i-1)]
b<-metascore_data[i:length(metascore_data)]
metascore_data<-append(a,list("na"))
metascore_data<-append(metascore_data,b)
}
#資料預處理:将metascore轉換為數字格式
metascore_data<-as.numeric(metascore_data)
#let's have another look at length of the metascore data
#我們再來看看metascore資料長度
[1] 100
#讓我們來看看summary statistics
summary(metascore_data)
min. 1st qu. median mean 3rd qu. max. na's
23.00 47.00 60.00 60.22 74.00 99.00 4
步驟10:同樣的事情發生在gross變量,這個數字代表這部電影的總收入。我使用相同的解決方案來解決這個問題:
#使用css選擇器來爬取總收入部分
gross_data_html <- html_nodes(webpage,'.ghost~ .text-muted+ span')
#将總收入資料轉化為文本
gross_data <- html_text(gross_data_html)
#讓我們來看看總收入
head(gross_data)
[1] "$269.36m" "$248.04m" "$27.50m" "$67.12m" "$99.47m" "$153.67m"
#資料預處理:去掉符号'$'和'm'
gross_data<-gsub("m","",gross_data)
gross_data<-substring(gross_data,2,6)
#我們再來看看總收入資料長度
length(gross_data)
[1] 86
#用na填補缺失條目
for (i in c(17,39,49,52,57,64,66,73,76,77,80,87,88,89)){
a<-gross_data[1:(i-1)]
b<-gross_data[i:length(gross_data)]
gross_data<-append(a,list("na"))
gross_data<-append(gross_data,b)
#資料預處理:将總收入轉換為數字格式
gross_data<-as.numeric(gross_data)
summary(gross_data)
0.08 15.52 54.69 96.91 119.50 530.70 14
步驟11:現在我們已經成功地爬取了2016年上映的100部最受歡迎的電影的所有11個特征。讓我們合并它們到一個資料框并檢查它的結構。
#合并所有清單構造一個資料框
movies_df<-data.frame(rank = rank_data, title = title_data,
description = description_data, runtime = runtime_data,
genre = genre_data, rating = rating_data,
metascore = metascore_data, votes = votes_data, gross_earning_in_mil = gross_data,
director = directors_data, actor = actors_data)
#資料框的結構
str(movies_df)
'data.frame': 100 obs. of 11 variables:
$ rank : num 1 2 3 4 5 6 7 8 9 10 ...
$ title : factor w/ 99 levels "10 cloverfield lane",..: 66 53 54 32 58 93 8 43 97 7 ...
$ description : factor w/ 100 levels "19-year-old billy lynn is brought home for a victory tour after a harrowing iraq battle. through flashbacks the film shows what"| __truncated__,..: 57 59 3 100 21 33 90 14 13 97 ...
$ runtime : num 108 107 111 139 116 92 115 128 111 116 ...
$ genre : factor w/ 10 levels "action","adventure",..: 3 3 7 4 2 3 1 5 5 7 ...
$ rating : num 7.2 7.7 7.6 8.2 7 6.5 6.1 8.4 6.3 8 ...
$ metascore : num 59 81 99 71 41 56 36 93 39 81 ...
$ votes : num 40603 91333 112609 177229 148467 ...
$ gross_earning_in_mil: num 269.3 248 27.5 67.1 99.5 ...
$ director : factor w/ 98 levels "andrew stanton",..: 17 80 9 64 67 95 56 19 49 28 ...
$ actor : factor w/ 86 levels "aaron eckhart",..: 59 7 56 5 42 6 64 71 86 3 ...
您現在已經成功地在imdb網站上爬取了2016年上映的最受歡迎的100部電影資料。
分析從網頁上爬取到的資料
一旦您有了資料,就可以進行不少操作,如分析資料、從資料中進行推算、在獲得的資料上訓練機器學習模型等等。我已經利用我們剛爬取到的資料做了一些有趣的資料可視化。跟着這些資料可視化并回答下面的問題。請在評論區留下您的答案,謝謝。
問1:基于上面的資料,哪種類型的電影時長最長?
問2:基于上面的資料,時長在130到160分鐘的電影中,哪種類型的電影最受青睐?
問3:基于上面的資料,所有時長在100到120分鐘的電影中,哪種類型的電影收入最高?
結語:
我相信本文将幫助您了解如何利用r語言進行網頁爬取。現在,你也許對遇到的問題和解決方案有了一些主意。由于大多數網頁上的資料是以非結構化的形式表示的,對任何一個資料科學家來說,網頁爬取都是一個極其友善帶勁的技能。
原文釋出時間為:2017-04-25
本文來自雲栖社群合作夥伴“大資料文摘”,了解相關資訊可以關注“bigdatadigest”微信公衆号