引言
網(wǎng)上的數(shù)據(jù)和信息無(wú)窮無(wú)盡,如今人人都用百度谷歌來(lái)作為獲取知識(shí),了解新鮮事物的首要信息源很魂。所有的這些網(wǎng)上的信息都是直接可得的,而為了滿足日益增長(zhǎng)的數(shù)據(jù)需求檐涝,我堅(jiān)信網(wǎng)絡(luò)數(shù)據(jù)爬取已經(jīng)是每個(gè)數(shù)據(jù)科學(xué)家的必備技能了遏匆。在本文的幫助下,你將會(huì)突破網(wǎng)絡(luò)爬蟲的技術(shù)壁壘谁榜,實(shí)現(xiàn)從不會(huì)到會(huì)幅聘。
大部分網(wǎng)上呈現(xiàn)的信息都是以非結(jié)構(gòu)化的格式存儲(chǔ)(html)且不提供直接的下載鏈接,因此窃植,我們需要學(xué)習(xí)一些知識(shí)和經(jīng)驗(yàn)來(lái)獲取這些數(shù)據(jù)帝蒿。
本文我將帶你領(lǐng)略利用R做網(wǎng)絡(luò)數(shù)據(jù)采集的全過(guò)程,通讀文章后你將掌握如何來(lái)使用因特網(wǎng)上各位數(shù)據(jù)的技能巷怜。
目錄
- 什么是網(wǎng)絡(luò)數(shù)據(jù)爬取
- 為什么需要爬取數(shù)據(jù)
- 數(shù)據(jù)爬取方法
- 前提條件
- 使用R爬取網(wǎng)頁(yè)
- 分析從網(wǎng)頁(yè)爬取的數(shù)據(jù)
1. 什么是網(wǎng)絡(luò)數(shù)據(jù)爬取
網(wǎng)絡(luò)爬蟲是講呈現(xiàn)在網(wǎng)頁(yè)上以非結(jié)構(gòu)格式(html)存儲(chǔ)的數(shù)據(jù)轉(zhuǎn)化為結(jié)構(gòu)化數(shù)據(jù)的技術(shù)葛超,該技術(shù)非常簡(jiǎn)單易用。
幾乎所有的主流編程語(yǔ)言都提供了網(wǎng)絡(luò)數(shù)據(jù)爬取的實(shí)現(xiàn)方式延塑,本文我們會(huì)用R來(lái)爬取IMDB上2016年最熱門電影的一些特征绣张。
我們將采集2016年度最熱門電影的若干特征,同時(shí)我們也會(huì)遇到網(wǎng)頁(yè)代碼不一致的問(wèn)題并將其解決关带。這是在做網(wǎng)絡(luò)爬蟲時(shí)最常遇到的問(wèn)題之一侥涵。
如果你更喜歡用python變成,我建議你看這篇指南來(lái)學(xué)習(xí)如何用python做爬蟲豫缨。
2. 為什么需要爬取數(shù)據(jù)
我確信你現(xiàn)在肯定在問(wèn)“為什么需要爬取數(shù)據(jù)”独令,正如前文所述,爬取網(wǎng)頁(yè)數(shù)據(jù)極有可能好芭。(譯者注:原文如此燃箭,我沒(méi)看懂這個(gè)設(shè)問(wèn)的邏輯)
為了提供一些使用的知識(shí),我們將會(huì)爬取IMDB的數(shù)據(jù)舍败,同時(shí)招狸,利用爬蟲你還可以:
- 爬取電影評(píng)分來(lái)構(gòu)建推薦系統(tǒng)
- 爬取維基百科等信源的文本作為訓(xùn)練預(yù)料來(lái)構(gòu)建深度學(xué)習(xí)模型以實(shí)現(xiàn)主體識(shí)別等功能
- 爬取有標(biāo)簽的圖像(從Google敬拓,F(xiàn)lickr等網(wǎng)站)來(lái)訓(xùn)練圖像分類模型
- 爬取社交媒體數(shù)據(jù)(Facebook 和 Twitter 等)做情感分析,觀點(diǎn)挖掘等
- 爬取電商的用戶評(píng)論和反饋(從Amazon裙戏,F(xiàn)lipkart等)
3. 數(shù)據(jù)爬取方法
網(wǎng)絡(luò)數(shù)據(jù)抓取的方式有很多乘凸,常用的有:
- 人工復(fù)制粘貼:這是采集數(shù)據(jù)的緩慢但有效的方式,相關(guān)的工作人員會(huì)自行分析并把數(shù)據(jù)復(fù)制到本地累榜。
- 文本模式匹配:另一種簡(jiǎn)單有效的方法是利用編程語(yǔ)言中的正則表達(dá)式來(lái)匹配固定模式的文本,在這里你可以學(xué)到關(guān)于正則表達(dá)式的更多內(nèi)容葛作。
- 使用API:諸如Facebook,Twitter和Linkedin一類的許多網(wǎng)站都提供了公共或者私人的API赂蠢,它們提供了標(biāo)準(zhǔn)化的代碼供用戶請(qǐng)求規(guī)定格式的數(shù)據(jù)辨泳。
- DOM解析:程序可以使用瀏覽器來(lái)獲取客戶端腳本生成的動(dòng)態(tài)內(nèi)容嵌施〗担基于這些程序可以獲得的頁(yè)面來(lái)使用DOM樹來(lái)解析網(wǎng)頁(yè)也是可行的辦法,
我們會(huì)使用DOM解析的方式來(lái)獲取數(shù)據(jù)途乃,并基于網(wǎng)頁(yè)的CSS選擇器來(lái)尋找含有所需信息的網(wǎng)頁(yè)部分扔傅。但在開始之前,我們必須滿足一些前提條件猎塞。
4. 前提條件
利用R實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲的前提條件有兩大塊:
- 要寫R語(yǔ)言爬蟲试读,你對(duì)R必須有一定了解。如果你還是個(gè)新手荠耽,我強(qiáng)烈建議參照這個(gè)學(xué)習(xí)路徑 來(lái)學(xué)習(xí)钩骇。本文將使用“Hadley Wickham(Hadley我愛你!B亮俊L纫佟)”開發(fā)的“rvest”包來(lái)實(shí)現(xiàn)爬蟲。你可以從這里獲得這個(gè)包的文檔慢叨。如果你沒(méi)有安裝這個(gè)包纽匙,請(qǐng)執(zhí)行以下代碼。
install.packages('rvest')
- 除此之外拍谐,HTML烛缔,CSS的相關(guān)知識(shí)也很重要馏段。學(xué)習(xí)他們的有一個(gè)很好的資源。我見識(shí)過(guò)不少對(duì)HTML和CSS缺乏了解的數(shù)據(jù)科學(xué)家践瓷,因此我們將使用名為Selector Gadget的開源軟件來(lái)更高效地實(shí)現(xiàn)抓取院喜。你可以在這里下載這個(gè)工具包。請(qǐng)確保你的瀏覽器已經(jīng)安裝了這個(gè)插件(推薦用chrome瀏覽器)晕翠,并且能正常使用喷舀。(譯者注:chrome中的css viewer 和 xpath helper 也是
image.png
使用這個(gè)插件你可以通過(guò)點(diǎn)擊任一網(wǎng)頁(yè)中你需要的數(shù)據(jù)就能獲得相應(yīng)的標(biāo)簽。你也可以學(xué)習(xí)HTML和CSS的知識(shí)并且手動(dòng)實(shí)現(xiàn)這一過(guò)程淋肾。而且元咙,為了更深入地了解網(wǎng)絡(luò)爬取這一藝術(shù),我很推薦你學(xué)習(xí)下HTML和CSS來(lái)了解其背后的機(jī)理巫员。
5. 使用R爬取網(wǎng)頁(yè)
現(xiàn)在讓我們開始爬取IMDB上2016年度最流行的100部故事片庶香,你可以在這里查看相關(guān)信息。
# 加載包
library('rvest')
# 指定要爬取的url
url <- 'http://www.imdb.com/search/title?
count=100&release_date=2016,2016&title_type=feature'
# 從網(wǎng)頁(yè)讀取html代碼
webpage <- read_html(url)
現(xiàn)在简识,讓我們爬取網(wǎng)頁(yè)上的這些數(shù)據(jù):
Rank:從1到100赶掖,代表排名
Title:故事片的標(biāo)題
Description:電影內(nèi)容簡(jiǎn)介
Runtime: 電影時(shí)長(zhǎng)
Genre: 電影類型
Rating: IMDB提供的評(píng)級(jí)
Metascore: IMDB上該電影的評(píng)分
Votes: 電影的好評(píng)度
Gross_Earning_in_Mil: 電影總票房(百萬(wàn))
Director: 影片的總導(dǎo)演奢赂,如果有多位膳灶,取第一個(gè)
Actor: 影片的主演轧钓,如果有多位毕箍,取第一個(gè)
這是頁(yè)面的截圖
Step 1: 爬取的第一步是使用 selector gadget獲得排名的CSS選擇器。你可以點(diǎn)擊瀏覽器中的插件圖標(biāo)并用光標(biāo)點(diǎn)擊排名的區(qū)域荷逞。
要確保所有的排名都被選擇了涩澡,你也可以再次點(diǎn)擊選中區(qū)域來(lái)取消選擇筏养,最終只有高亮的那些部分會(huì)被爬取渐溶。
Step 2: 一旦你已經(jīng)選擇了正確的區(qū)域茎辐,你需要把在底部中心顯示的相應(yīng)的CSS選擇器復(fù)制下來(lái)弛槐。
Step 3: 只要CSS選擇器包含排名乎串,你就能用幾行簡(jiǎn)單的代碼來(lái)獲取所有的排名了:
# 用CSS選擇器獲取排名部分
rank_data_html <- html_nodes(webpage,'.text-primary')
# 把排名轉(zhuǎn)換為文本
rank_data <- html_text(rank_data_html)
# 檢查一下數(shù)據(jù)
head(rank_data)
[1] "1." "2." "3." "4." "5." "6."
Step 4: 獲取數(shù)據(jù)之后叹誉,請(qǐng)確保他們被你所需的格式存儲(chǔ)长豁,我會(huì)把排名處理成數(shù)值型。
# 數(shù)據(jù)預(yù)處理:把排名轉(zhuǎn)換為數(shù)值型
rank_data<-as.numeric(rank_data)
# 再檢查一遍
head(rank_data)
[1] 1 2 3 4 5 6
Step 5: 現(xiàn)在你可以清空選擇部分并開始選擇電影標(biāo)題了酸舍,你可以看見所有的標(biāo)題都被選擇了父腕,你依據(jù)個(gè)人需要做一些增刪。
Step 6: 正如從前斥难,再次復(fù)制CSS選擇器并用下列代碼爬取標(biāo)題哑诊。
# 爬取標(biāo)題
title_data_html <- html_nodes(webpage,'.lister-item-header a')
# 轉(zhuǎn)換為文本
title_data <- html_text(title_data_html)
# 檢查一下
head(title_data)
[1] "Sing" "Moana" "Moonlight" "Hacksaw Ridge"
[5] "Passengers" "Trolls"
Step 7: 下列代碼會(huì)爬取剩余的數(shù)據(jù)– Description, Runtime, Genre, Rating, Metascore, Votes, Gross_Earning_in_Mil , Director and Actor data.
# 爬取描述
description_data_html <- html_nodes(webpage,'.ratings-bar+ .text-muted')
# 轉(zhuǎn)為文本
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.
# 移除 '\n'
description_data<-gsub("\n","",description_data)
# 再檢查一下
head(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."
# 爬取runtime section
runtime_data_html <- html_nodes(webpage,'.text-muted .runtime')
# 轉(zhuǎn)為文本
runtime_data <- html_text(runtime_data_html)
# 檢查一下
head(runtime_data)
[1] "108 min" "107 min" "111 min" "139 min" "116 min" "92 min"
# 數(shù)據(jù)預(yù)處理: 去除“min”并把數(shù)字轉(zhuǎn)換為數(shù)值型
runtime_data <- gsub(" min","",runtime_data)
runtime_data <- as.numeric(runtime_data)
# 再檢查一下
head(rank_data)
[1] 1 2 3 4 5 6
# 爬取genre
genre_data_html <- html_nodes(webpage,'.genre')
# 轉(zhuǎn)為文本
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 "
# 去除“\n”
genre_data<-gsub("\n","",genre_data)
# 去除多余空格
genre_data<-gsub(" ","",genre_data)
# 每部電影只保留第一種類型
genre_data<-gsub(",.*","",genre_data)
# 轉(zhuǎn)化為因子
genre_data<-as.factor(genre_data)
# 再檢查一下
head(genre_data)
[1] Animation Animation Drama Biography Adventure Animation
爬取IMDB rating
rating_data_html <- html_nodes(webpage,'.ratings-imdb-rating strong')
# 轉(zhuǎn)為文本
rating_data <- html_text(rating_data_html)
# 檢查一下
head(rating_data)
[1] "7.2" "7.7" "7.6" "8.2" "7.0" "6.5"
# 轉(zhuǎn)為數(shù)值型
rating_data<-as.numeric(rating_data)
# 再檢查一下
head(rating_data)
[1] 7.2 7.7 7.6 8.2 7.0 6.5
# 爬取votes section
votes_data_html <- html_nodes(webpage,'.sort-num_votes-visible span:nth-child(2)')
# 轉(zhuǎn)為文本
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)
# 轉(zhuǎn)為數(shù)值型
votes_data<-as.numeric(votes_data)
# 再檢查一下
head(votes_data)
[1] 40603 91333 112609 177229 148467 32497
# 爬取directors section
directors_data_html <- html_nodes(webpage,'.text-muted+ p a:nth-child(1)')
# 轉(zhuǎn)為文本
directors_data <- html_text(directors_data_html)
# 檢查一下
head(directors_data)
[1] "Christophe Lourdelet" "Ron Clements" "Barry Jenkins"
[4] "Mel Gibson" "Morten Tyldum" "Walt Dohrn"
# 轉(zhuǎn)為因子
directors_data<-as.factor(directors_data)
# 爬取actors section
actors_data_html <- html_nodes(webpage,'.lister-item-content .ghost+ a')
# 轉(zhuǎn)為文本
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"
# 轉(zhuǎn)為因子
actors_data<-as.factor(actors_data)
我時(shí)爬Metascore時(shí)遇到問(wèn)題幕垦,我希望你能仔細(xì)看看先改。
# 爬取metascore section
metascore_data_html <- html_nodes(webpage,'.metascore')
# 轉(zhuǎn)為文本
metascore_data <- html_text(metascore_data_html)
# 檢查一下
head(metascore_data)
[1] "59 " "81 " "99 " "71 " "41 "
[6] "56 "
# 去除多余空格
metascore_data<-gsub(" ","",metascore_data)
# 檢查metascore data的長(zhǎng)度
length(metascore_data)
[1] 96
meta score只有96個(gè)數(shù)據(jù),可我們卻爬取了100部電影猜嘱。這個(gè)問(wèn)題產(chǎn)生的原型是由4部電影沒(méi)有Metascore數(shù)據(jù)朗伶。
這是爬取所有網(wǎng)頁(yè)都會(huì)遇到的常見問(wèn)題论皆,如果我們只是簡(jiǎn)單地用NA來(lái)填充這四個(gè)缺失值点晴,它會(huì)自動(dòng)填充第97到100部電影粒督。通過(guò)一些可視化檢查屠橄,我們發(fā)缺失matascore的是第39锐墙,73,80和89部電影溪北。我用下面的函數(shù)來(lái)解決這個(gè)問(wèn)題之拨。
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)
}
# 轉(zhuǎn)換為數(shù)值型
metascore_data <- as.numeric(metascore_data)
# 再次檢查下長(zhǎng)度
length(metascore_data)
[1] 100
# 看看描述性統(tǒng)計(jì)量
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
同樣的問(wèn)題也會(huì)發(fā)生在Gross變量上蚀乔,我用同樣的方式來(lái)解決乙墙。
# 爬取revenue section
gross_data_html <- html_nodes(webpage,'.ghost~ .text-muted+ span')
# 轉(zhuǎn)為文本
gross_data <- html_text(gross_data_html)
# 檢查一下
head(gross_data)
[1] "$269.36M" "$248.04M" "$27.50M" "$67.12M" "$99.47M" "$153.67M"
# 去除'$' 和 'M' 標(biāo)記
gross_data <- gsub("M", "", gross_data)
gross_data <- substring(gross_data, 2, 6)
# 檢查長(zhǎng)度
length(gross_data)
[1] 86
# 填充缺失值
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)
}
# 轉(zhuǎn)為數(shù)值
gross_data<-as.numeric(gross_data)
# 再次檢車長(zhǎng)度
length(gross_data)
[1] 100
summary(gross_data)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.08 15.52 54.69 96.91 119.50 530.70 14
我們已經(jīng)成功爬取了100部電影的11個(gè)特征听想,讓我們創(chuàng)建一個(gè)數(shù)據(jù)框并看看結(jié)構(gòu)汉买。
# 合并所有l(wèi)ist來(lái)創(chuàng)建一個(gè)數(shù)據(jù)框
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
)
# 查看數(shù)據(jù)框結(jié)構(gòu)
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 ...
現(xiàn)在2016年上映的最流行的100部故事片在IMDB上的數(shù)據(jù)已經(jīng)爬取成功了垫卤!
- 分析從網(wǎng)頁(yè)爬取的數(shù)據(jù)
爬取好數(shù)據(jù)后穴肘,你們隊(duì)數(shù)據(jù)進(jìn)行一些分析與推斷评抚,訓(xùn)練一些機(jī)器學(xué)習(xí)模型慨代。我在上面這個(gè)數(shù)據(jù)集的基礎(chǔ)上做了一些有趣的可視化來(lái)回答下面的問(wèn)題侍匙。
library('ggplot2')
qplot(data = movies_df,Runtime,fill = Genre,bins = 30)
Question 1: 那個(gè)類型的電影市場(chǎng)最長(zhǎng)想暗?
ggplot(movies_df,aes(x=Runtime,y=Rating))+
geom_point(aes(size=Votes,col=Genre))
Question 2: 市場(chǎng)130-160分鐘的電影里江滨,哪一類型東西好評(píng)率最高?
ggplot(movies_df,aes(x=Runtime,y=Gross_Earning_in_Mil))+
geom_point(aes(size=Rating,col=Genre))
添加我的微信吧