R Crawler R爬蟲

Chih-Cheng

資料從哪來?

  • 資料庫來?
  • 工讀生key來?
  • 機器產生?如你的手機?
  • 網站?

網站可以拿到什麼?

http://www.asap.com.tw/

什麼是爬蟲

利用程式有系統地造訪網頁,取得網頁內容。
取出有用的資料以利後續應用。

觀察網頁

造訪網頁取得內容

剖析資料

儲存資料

爬蟲流程 pictures from https://pixabay.com

開始之前,和大家說,不要專注看code,專注在流程。

github.png

觀察網頁

檢查網頁元素

github_inspect.png

造訪網頁取得內容

使用「httr」套件

In [4]:
library(httr)
url = "https://github.com/dboyliao/DSC2015_RBasic"
response = GET(url)
content = content(response)
print(capture.output(content)[1:10]) # Capture first 10 lines of output
 [1] "<!DOCTYPE html>"                                                                                                                                                        
 [2] "<html lang=\"en\" class=\"\">"                                                                                                                                          
 [3] "<head prefix=\"og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# object: http://ogp.me/ns/object# article: http://ogp.me/ns/article# profile: http://ogp.me/ns/profile#\">"
 [4] "<meta charset=\"utf-8\">"                                                                                                                                               
 [5] "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"                                                                                                              
 [6] "<meta http-equiv=\"Content-Language\" content=\"en\">"                                                                                                                  
 [7] "<meta name=\"viewport\" content=\"width=1020\">"                                                                                                                        
 [8] "<title>dboyliao/DSC2015_RBasic · GitHub</title>"                                                                                                                       
 [9] "<link rel=\"search\" type=\"application/opensearchdescription+xml\" href=\"/opensearch.xml\" title=\"GitHub\">"                                                         
[10] "<link rel=\"fluid-icon\" href=\"https://github.com/fluidicon.png\" title=\"GitHub\">"                                                                                   

會有些許不同,但大多是一樣的

剖析資料

In [6]:
library(XML)
library(selectr)
nodes = querySelectorAll(content,".files .content a")
nodes
Out[6]:
[[1]]
<a href="/dboyliao/DSC2015_RBasic/tree/master/DSC2015" class="js-directory-link" id="85110664b2e3419869c3e238b643b754-aa21898809ee549f307bd1ebbda664142f0643ed" title="DSC2015">DSC2015</a> 

[[2]]
<a href="/dboyliao/DSC2015_RBasic/tree/master/slides" class="js-directory-link" id="f4d5c52946e476c66dfc36433c82aa78-fadb2e9686a43b3c997d430609eac0780c28798e" title="slides">slides</a> 

[[3]]
<a href="/dboyliao/DSC2015_RBasic/blob/master/.gitignore" class="js-directory-link" id="a084b794bc0759e7a6b77810e01874f2-2065e6d4cc27613d71d470bbc7bd7e3f5ee537a0" title=".gitignore">.gitignore</a> 

[[4]]
<a href="/dboyliao/DSC2015_RBasic/blob/master/DSC2015.zip" class="js-directory-link" id="26f66044f934a914bce37bd512eb33b4-c23bd23f1492a77d33284485aac7e85b8b2e9851" title="DSC2015.zip">DSC2015.zip</a> 

[[5]]
<a href="/dboyliao/DSC2015_RBasic/blob/master/RBasic1.R" class="js-directory-link" id="b232b3fb3a8cbf459c8344ec15365d24-c3c27450020f45945a0da10c98292f416ddfb3f9" title="RBasic1.R">RBasic1.R</a> 

[[6]]
<a href="/dboyliao/DSC2015_RBasic/blob/master/working_script.R" class="js-directory-link" id="1b3222ae4437d042ae6c959d0d998ab2-43be8ced685f462fec4f070be5a50413bcea5971" title="working_script.R">working_script.R</a> 

attr(,"class")
[1] "XMLNodeSet"

取出字串

In [10]:
xmlValue(nodes[[1]])
Out[10]:
'DSC2015'
In [71]:
for(node in nodes){
    print(xmlValue(node))
}
[1] "DSC2015"
[1] "slides"
[1] ".gitignore"
[1] "DSC2015.zip"
[1] "RBasic1.R"
[1] "working_script.R"

github.png

剛剛發生什麼事

made with www.draw.io

made with www.draw.io

實作範例2

  • 所需參數:
參數 意義 格式、範例
service_time 出勤日期 格式為 YYYY-mm-dd
service_unit[] 出勤分隊 例如 樹林分隊 、 板橋分隊
service_type[] 出勤任務類型 目前只能輸入 救護、火災 、 災害

http://ha2.tw/ntpcfd/api/json?service_time=2015-07-25

取得2015年7月25日的當日出勤記錄

  • 回傳結果:
    • service_type:出勤任務類型
    • service_unit:出勤分隊
    • service_addr:出勤地址
    • service_time:報案時間
    • lat:出勤地址約略緯度
    • lng:出勤地址約略經度
  • 備註:若不帶入任何參數,則取出當天所有報案紀錄。
  • 資料來源:http://epaper.tpf.gov.tw/liveview/default.asp

觀察網頁

造訪網頁取得內容 + 剖析資料

In [8]:
library(jsonlite)

url = "http://ha2.tw/ntpcfd/api/json?service_time=2015-07-25"
data = jsonlite::fromJSON(url)
head(data)
Out[8]:
service_typeservice_unitservice_addrservice_timelatlng
1災害蘆洲分隊新北市蘆洲區三民路2015-07-25 00:00:0025.089419500121.468358900
2災害中和分隊新北市中和區中和路2015-07-25 00:13:0024.999690800121.505932400
3災害蘆洲分隊新北市蘆洲區永平街 永樂街38巷交叉路2015-07-25 00:13:0025.091188200121.458256100
4災害裕民分隊新北市新莊區民安西路88巷1弄2015-07-25 00:14:0025.019213600121.426440000
5災害德音分隊新北市泰山區泰林路二段2015-07-25 00:16:0025.060163500121.434021400
6災害新店分隊新北市新店區中興路三段2015-07-25 00:19:0024.981748900121.543918800

檢視資料

In [11]:
summary(data)
Out[11]:
 service_type       service_unit       service_addr       service_time      
 Length:490         Length:490         Length:490         Length:490        
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
     lat                lng           
 Length:490         Length:490        
 Class :character   Class :character  
 Mode  :character   Mode  :character  

預處理

In [64]:
library(knitr)
data$service_type = as.factor(data$service_type)
data$service_unit = as.factor(data$service_unit)
data$lat = as.numeric(data$lat)
data$lng = as.numeric(data$lng)
dataWithGeoPoint = data[ data$lng>0, ]
head(summary(dataWithGeoPoint)) # 這裡用head只是為了讓summary漂亮顯示
Out[64]:
service_type service_unitservice_addrservice_time lat lng
災害:486 淡水分隊: 26 Length:486 Length:486 Min. :24.91 Min. :121.3
NA 南勢分隊: 21 Class :character Class :character 1st Qu.:25.00 1st Qu.:121.4
NA 新店分隊: 18 Mode :character Mode :character Median :25.02 Median :121.5
NA 三重分隊: 17 NA NA Mean :25.04 Mean :121.5
NA 重陽分隊: 17 NA NA 3rd Qu.:25.07 3rd Qu.:121.5
NA 莒光分隊: 16 NA NA Max. :25.26 Max. :122.0

資料整理乾淨了,其實可以存起來

In [69]:
write.table(dataWithGeoPoint, file = "ntc.csv", sep = "," )

後續應用:簡單視覺化

In [28]:
library(ggplot2)
library(ggmap)

box = make_bbox(dataWithGeoPoint$lng, dataWithGeoPoint$lat) 
p = ggmap(get_map(box), darken = c(0.5, "black")) 
q = p + geom_point(data=dataWithGeoPoint, aes(lng, lat, colour="red", alpha = 1/10 ), size =3)+ theme(legend.position="none")
q
Warning message:
: bounding box given to google - spatial extent only approximate.converting bounding box to center/zoom specification. (experimental)
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=25.084719,121.661214&zoom=10&size=640x640&scale=2&maptype=terrain&language=en-EN&sensor=false

Take Away

爬蟲能幫助我們取得網頁上的資料

爬蟲的流程

針對不同的資料格式(HTML、JSON),使用專門的R套件剖析