R Basic with Web Crawler

Week 1

Chia-Chi Chang

本週學習目標

  • 基礎網路知識
  • 安裝與使用套件
  • 爬蟲 GUI 工具的使用
  • 讀取本機端或遠端資料
  • R 基本資料結構與操作

Packages

R 是個開源免費的軟體。

有非常多人 (駭客們) 幫它撰寫各式各樣的套件。

我們要去哪裡看說 R 有什麼套件呢?

主要有兩大管道:

  • CRAN
  • Github

    • 到底我們該怎麼使用這些套件呢 = =?

Packages

為了使用這些套件,我們必須先安裝它們。

  • 在 R 裡安裝套件也非常簡單
  • 以接下來爬蟲例子中會需要的 XML 套件為例。
install.packages("XML")
## 
## The downloaded binary packages are in
##  /var/folders/5c/0p5zr2_n4xvbt2j6hkqczhph0000gn/T//RtmpUI1qaF/downloaded_packages

Packages

  • 很好! 你已經成功安裝 XML 套件了。
  • 為了把包含在 XML 這個套件中的相關函數"引入"供你差遣,我們需要 library() 這個函數。
library("XML")

That's it! You are ready for building simple web-crawler now.

眼見為憑,我們用一個小例子示範一下。

Example: TWSE
(臺灣證券交易所)


This way to TWSE

TWSE 英文首頁

twse-home

twse-domestic

twse-data

The Code

MOPS_URL.TWSE_ALL <-
  "http://www.twse.com.tw/en/listed/listed_company/apply_listing.php?page=1"

web_page <- htmlParse(MOPS_URL.TWSE_ALL,encoding="big5")
data <- readHTMLTable(web_page, which=6, stringsAsFactors=F, header = T)
names(data) <- 
  c("Application Date", "Code", "Company", "Chairman","Amount of Capital",
    "Underwriter")
data <- data[-1,]
head(data, n=3)
##   Application Date Code Company Chairman Amount of Capital Underwriter
## 2       2015.03.03 6442  Ezconn                    600,000            
## 3       2014.10.16 3416 WinMate                    610,664            
## 4       2014.10.07 8341      SF                  1,000,000

Magic!

Your Turn!

  • 平常上班不能上網亂看東西,今天可以!
  • 找幾個有興趣的網站,並且把有興趣的資料在網站哪裡標註起來。
    (print screen)
  • 你想抓的網站有可能是靜態網頁,也可能是動態的。(What!?)
  • 別緊張,四週課程過去後,你會了解之間的差別,也會知道如何處理它。
  • 記得把網址都記下來噢!等等會有用。

爬蟲心法

模仿你的瀏覽器

爬蟲心法:模仿你的瀏覽器

  • 基本上來說,寫爬蟲就是寫程式去模仿瀏覽器的行為。
  • 把你想要的資料從瀏覽器讀到的資料中取出來,就是一隻基本的爬蟲。
  • 那到底瀏覽器讀到了什麼資料啊??
  • Chrome 與 Firfox 是你的好朋友。
  • IE 咧?

不要問....很可怕 = =+

爬蟲小幫手

GUI 小道具

爬蟲小幫手: GUI 小道具

  • 開發人員工具 in Chrome & Firfox
    • Mac: Cmd + Shift + I
    • Linux: Ctrl + Shift + I

dev_shell

  • Postman (Chrome Plug-in)
    • 簡單的圖形介面讓你可以對伺服器發 request。
    • 不懂啥是 request? 沒關係,等等會解釋。
    • 先偷看一下它長什麼樣子就好。

postman

HTTP

What is HTTP?

  • HTTP 是 "Hypertext Transfer Protocol" 的縮寫。
有說等於沒說....= =
  • HTTP 是一種通訊協定,規定好客戶端 (client) 要如何跟伺服器端 (server) 做溝通。
  • 簡而言之,HTTP 之於網路,就如同文法之於英文。
  • 粗略來分,可以分為 POST、GET、PUT、DELETE ....等。
其中寫爬蟲最常用的,就是 GET 與 POST 這兩種方法。
  • 更詳盡的資料可以參考 Wiki 上的說明。

簡而言之,一般網路的運作方式大致上入下圖所示:

GET METHOD

忙碌的瀏覽器

打從我們打開一個網頁開始,瀏覽器就沒有停止工作過。
我們先來看看瀏覽器做了些什麼事。
Chrome 開發人員工具

Network

network

滿坑滿谷的 GET 啊!
  • 瀏覽器在我們打開這個網頁的時候,會幫我們下載了這個網頁所有需要的圖片、聲音與資料。
  • 等檔案下載好後,再排版成我們肉眼看到的網頁。
  • 瀏覽器 OS: 我把資料都藏在這裡了,想要的人就去拿吧!
  • 在我們登上偉大的航道前,再多觀察一些其他的東西吧。

GET - More Than You Can Imagine

  • URL (網址) 也可能包含了一些額外資訊,讓瀏覽器可以從 server 拿到不一樣的資料。
  • 這些資訊可能包含 id 、當前頁數、日期等等資訊。
  • 瀏覽一下 TWSE 的網站並觀察它網址的變化吧!

常見的 URL 形態:

  1. domain_name/path/to/specific/page
  2. ../../..?field1=value1&field2=value2&field3=value3


上述兩種 URL 再 TWSE 的網站上都找得到。

舉例來說,我們現在瀏覽的是英文界面,如果想瀏覽中文的呢? 它們的 URL 有什麼變化?



Try it!

What Can We "Get"?

常見的資料形態:

  • HTML/XML text
  • Files. (csv, tsv...etc)
  • JSON
來看看它們長啥樣子吧!

瀏覽器之HTML

瀏覽器之HTML

  • HTML 是 Hyper Text Markup Language 的縮寫。
  • 以一個 tag 為基本單位,一般稱一個 tag 為一個 element 。
  • 所謂的一個 tag ,指的是一組 <>...</>。 (有些 tag 只有一個 <>)
  • 有樹狀結構。(DOM tree)
  • 瀏覽器會根據下載到的 html 檔案去排版,成為人肉眼看到的網頁。

Example: DOM tree

dom_tree

圖片來源

DOM tree: The Code

    <html>
      <head>
        <meta></meta>
        <title></title>
        <style type="text/css"></style>
      </head>
      <body>
        <h1></h1>
        <section></section>
        <footer></footer>
      </body>
    </html>

Excersice:

  • 用 Chrome 或 Firefox 的開發人員工具,把剛剛你找到的那些網頁的 html 檔案打開看看。
  • 找看看你有興趣的資料在這些 html 檔案中的哪裡?

Files

Market Info > Historical Trading Info/Data >Daily Quotes

JSON

以 PChome 網路商城為例

破解後台

後台抵家

以這個後台的 URL 來說,你可以猜到它代表的意義嗎?

想想看
  • 什麼是 fields ?
  • 'fields=' 後面的東西又是什麼?
  • 如果我不想要商家的名字,要怎麼做呢?



在 Ajax 技術一章會有進一步的介紹。

GET Request by PostMan

用 PostMan 把這個 csv 下載下來!

Is there anything wrong with this csv file?
What is 'csv file' anyway?
Google the information you need and think about it!

Parsor for Files

What does a parsor do in a spider?

  • Collecting data from connector.
  • The more important is extracting neccessary information out of raw data.
  • We will see more examples in later chater R Basic.

The Basic Components of a WebSpider

R Code - Review

rm(list=ls())
MOPS_URL.TWSE_ALL <-
  "http://www.twse.com.tw/en/listed/listed_company/apply_listing.php?page=1"

web_page <- htmlParse(MOPS_URL.TWSE_ALL,encoding="big5")
data <- readHTMLTable(web_page, which=6, stringsAsFactors=F, header = T)
names(data) <- 
  c("Application Date", "Code", "Company", "Chairman","Amount of Capital",
    "Underwriter")
data <- data[-1,]
head(data, n=3)
根據目前為止學到的網路與爬蟲知識,你可以說說看上面每一行的指令在做些什麼嗎?


Try and find it out!

R Basic

Basic Data Types

(s <- "I love R!") # This is string
(b <- T) # Boolean
(f <- pi) # Floating number (real number).
(i <- 3) # Integer
## [1] "I love R!"
## [1] TRUE
## [1] 3.142
## [1] 3

Basic Data Structure - Vector

v1 <- c(1, 2, 3) # c() is the concatenate function in R.
v2 <- c(2, 3)
(r1 <- v1 + v2)
## Warning: 較長的物件長度並非較短物件長度的倍數
(r2 <- v1 * v2)
## Warning: 較長的物件長度並非較短物件長度的倍數
## [1] 3 5 5
## [1] 2 6 6

c() can be used to combine vectors into one vector.

c(v1, v2)
## [1] 1 2 3 2 3

All elements in a vector must be of the same type

v <- c(1, 2, "3")
v
## [1] "1" "2" "3"

Basic Data Structure - List

List, different from vector, can be used for storing data of different types.

c3h3 <- list(name="c3h3", age=30, height=172, weight=65.3)
c3h3
## $name
## [1] "c3h3"
## 
## $age
## [1] 30
## 
## $height
## [1] 172
## 
## $weight
## [1] 65.3

c() can be also used for combining lists.

c3h3 <- c(c3h3, list(zip_code='100'))
c3h3
## $name
## [1] "c3h3"
## 
## $age
## [1] 30
## 
## $height
## [1] 172
## 
## $weight
## [1] 65.3
## 
## $zip_code
## [1] "100"

Your Best Friend - Dataframe

require(gdata)
domestic_ins_company <- read.xls("http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E7%94%A2%E9%9A%AA%E5%85%AC%E5%8F%B8_20140822.xls")
(names(domestic_ins_company))
head(domestic_ins_company, n=2)
## [1] "名.稱.與.所.在.地" "設立日期"          "董事長"           
## [4] "總經理"            "電話.電傳"         "網址"
##                                                                                                                             名.稱.與.所.在.地
## 1                                                                                                                                            
## 2 臺灣產物保險股份有限公司\n100 台北市館前路49號 8、9樓\nTaiwan Fire & Marine Insurance Co., Ltd\n8-9 F1., No.49, Kuan Chien Road, Taipei 100
##   設立日期            董事長               總經理
## 1                                                
## 2 37/03/12 李泰宏\nSteve Lee 宋道平\nCharles Song
##                                           電話.電傳
## 1                                      與免付費電話
## 2 Tel. (02)23821666\nFax. (02)23882555\n0809-068888
##                         網址
## 1 (公司、資訊公開與重大訊息)
## 2    http://www.tfmi.com.tw/
Frankly speaking, dataframe is just like a table with bunch of useful features.
domestic_ins_company 是直接從金管會業務統計資料中的保險機構一覽表中拿到的本國保險機構資料。

聰明的你猜得出來我是從哪裡找到 xls 的網址的嗎?

在此例中,read.xls 就是扮演著 parsor 的角色,讓你可以把資料讀進 R 中做進一步的處理。

想想看,如果要爬金管會的資料,你的作業流程會是如何?
程式大概又應該怎麼寫呢?

情境

我有 8 個網址要爬,難不成我要複製貼上 8 次 read.xls !?

For loop is all you need!


我們一樣用金管會的網站當例子

This way to the website

urls <- list("http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E7%94%A2%E9%9A%AA%E5%85%AC%E5%8F%B8_20140822.xls",
            "http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E7%94%A2%E9%9A%AA%E5%85%AC%E5%8F%B8_20141030.xls",
            "http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E5%A3%BD%E9%9A%AA%E5%85%AC%E5%8F%B8_20150113.xls",
            "http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E5%A3%BD%E9%9A%AA%E5%85%AC%E5%8F%B8_20140904.xls",
            "http://www.tii.org.tw/images_P2/%E5%B0%88%E6%A5%AD%E5%86%8D%E4%BF%9D%E9%9A%AA%E5%85%AC%E5%8F%B81001228.xls",
            "http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E4%BF%9D%E9%9A%AA%E5%85%AC%E5%8F%B8%E5%9C%A8%E5%8F%B0%E8%81%AF%E7%B5%A1%E8%99%9520140703.xls",
            "http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E4%BF%9D%E9%9A%AA%E7%9B%B8%E9%97%9C%E6%A9%9F%E6%A7%8B_20141227.xls",
            "http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E9%87%91%E8%9E%8D%E4%BF%9D%E9%9A%AA%E7%9B%B8%E9%97%9C%E6%A9%9F%E6%A7%8B1010102.xls")
results <- list()
for (url in urls){
  print(paste("Processing ", url))
  data <- read.xls(url, header=T, stringsAsFactors=F)
  results <- c(results, list(data))
}
## [1] "Processing  http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E7%94%A2%E9%9A%AA%E5%85%AC%E5%8F%B8_20140822.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E7%94%A2%E9%9A%AA%E5%85%AC%E5%8F%B8_20141030.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E5%A3%BD%E9%9A%AA%E5%85%AC%E5%8F%B8_20150113.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E5%A3%BD%E9%9A%AA%E5%85%AC%E5%8F%B8_20140904.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E5%B0%88%E6%A5%AD%E5%86%8D%E4%BF%9D%E9%9A%AA%E5%85%AC%E5%8F%B81001228.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E4%BF%9D%E9%9A%AA%E5%85%AC%E5%8F%B8%E5%9C%A8%E5%8F%B0%E8%81%AF%E7%B5%A1%E8%99%9520140703.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E6%9C%AC%E5%9C%8B%E4%BF%9D%E9%9A%AA%E7%9B%B8%E9%97%9C%E6%A9%9F%E6%A7%8B_20141227.xls"
## [1] "Processing  http://www.tii.org.tw/images_P2/%E5%A4%96%E5%9C%8B%E9%87%91%E8%9E%8D%E4%BF%9D%E9%9A%AA%E7%9B%B8%E9%97%9C%E6%A9%9F%E6%A7%8B1010102.xls"
head(results[[1]], n = 3)
##                                                                                                                             名.稱.與.所.在.地
## 1                                                                                                                                            
## 2 臺灣產物保險股份有限公司\n100 台北市館前路49號 8、9樓\nTaiwan Fire & Marine Insurance Co., Ltd\n8-9 F1., No.49, Kuan Chien Road, Taipei 100
## 3                                                                                                                                            
##   設立日期            董事長               總經理
## 1                                                
## 2 37/03/12 李泰宏\nSteve Lee 宋道平\nCharles Song
## 3                                                
##                                           電話.電傳
## 1                                      與免付費電話
## 2 Tel. (02)23821666\nFax. (02)23882555\n0809-068888
## 3                                                  
##                                                網址
## 1                        (公司、資訊公開與重大訊息)
## 2                           http://www.tfmi.com.tw/
## 3 http://www.tfmi.com.tw/ec/news/news-public.screen
咦!? results[[1]] 是什麼?

Slice

  • 在上面的例子裡,我們可以在 R 裡面可以用很簡單直觀的方式,也就是 [index1[, index2, ...]] 之類的語法來存取特定區塊的資料。稱之為 slicing。
  • vector、list 與 data.frame 有著不同的 slicing 語法。

範例

v <- c(1:20) # v 是個 vector
l <- list(pi, 306, "Taishin") # l 是個 list
df <- data(iris) # df 是個 data.frame

print(c("v[3]", v[3])) # 抽取 v 的第三個 element
print(c(class(l[3]), class(l[[3]]))) # [] 與 [[]] 的差別
print(iris[c(2, 5), c(1, 3, 5)]) # data.frame 的 slicing 語法
## [1] "v[3]" "3"
## [1] "list"      "character"
##   Sepal.Length Petal.Length Species
## 2          4.9          1.4  setosa
## 5          5.0          1.4  setosa

補充說明 - Slicing for Dataframe

Dataframe 長得像這樣子 (以 iris 為例)

irirs[c(2, 5), c(1, 3, 5)]
或者是
iris[c(2, 5), c("Sepal.Length", "Petal.Length", "Species")]

head(df, n=6) 顧名思義,會顯示 df 前 n 筆資料

head(iris) # 預設 n 是 6 筆
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
做人做事就要有頭 (head) 有尾 (tail) !


寫 code 學人生哲理。(茶)


tail(df, n=6)head() 成對,會顯示 df 最後 n 筆資料
tail(iris) # 預設一樣是 6
##     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
## 145          6.7         3.3          5.7         2.5 virginica
## 146          6.7         3.0          5.2         2.3 virginica
## 147          6.3         2.5          5.0         1.9 virginica
## 148          6.5         3.0          5.2         2.0 virginica
## 149          6.2         3.4          5.4         2.3 virginica
## 150          5.9         3.0          5.1         1.8 virginica

除了 list 可以取名字、data.frame 有欄位名,vector 也是可以取名字並且用 [] 取存取它!

weights <- c(hsiang=70, c3h3=62, dboy=82)
weights["c3h3"]
## c3h3 
##   62

[][[]] 是不一樣的

class(c3h3["weight"]) # 是個 list
class(c3h3[["weight"]]) # 是個數字
## [1] "list"
## [1] "numeric"

回頭看看之前抓到的資料

(head(results[[1]], n=2))
##                                                                                                                             名.稱.與.所.在.地
## 1                                                                                                                                            
## 2 臺灣產物保險股份有限公司\n100 台北市館前路49號 8、9樓\nTaiwan Fire & Marine Insurance Co., Ltd\n8-9 F1., No.49, Kuan Chien Road, Taipei 100
##   設立日期            董事長               總經理
## 1                                                
## 2 37/03/12 李泰宏\nSteve Lee 宋道平\nCharles Song
##                                           電話.電傳
## 1                                      與免付費電話
## 2 Tel. (02)23821666\nFax. (02)23882555\n0809-068888
##                         網址
## 1 (公司、資訊公開與重大訊息)
## 2    http://www.tfmi.com.tw/
大家覺得抓下來的資料有什麼問題?


用剛剛學到的 slicing 語法看看這些 data.frame 吧!

? 與 ??

?/?? 是 R 裡的 helper function


可使用 ?command 去查詢相關文件與指令說明。

範例

?c # 查詢 c 的說明文件
??c # 搜尋所有有關 c 的說明文件

Homework

  • 政府資料開放平台
    • 下載不動產買賣實價登錄批次資料 (csv)
    • 請問瀏覽器這時是發 GET 還是 POST request? 哪裡可以看到相關資訊呢?
    • 下載下來的 csv 檔如何讀進 R ?
    • Hint: read.csv
  • Post Method
    • 運用 Chrome (Firefox) 開發者工具找出可以發 post request 的網站
    • Hint: 尋找 form tag。
      <form action='...'>.....</form>

Q & A