2018/1/4

第十六天:做一個最簡單的爬蟲( 2018 iT邦幫忙鐵人賽-只要有心,人人都可以作卡米狗 )

markdown 一直以來,我們都是用瀏覽器發出 `HTTP request`,打到我們的 Rails Server,然後我們的 Rails Server 再傳回 `HTTP Response`。 我們還沒有試過用 Rails Server 發出 `HTTP request`,今天就來試一下。 # 用 Rails 發 HTTP request ### Routes 在 `config/routes.rb` 加入 ``` get '/kamigo/sent_request', to: 'kamigo#sent_request' ``` ### Action 在 `app/controllers/kamigo_controller.rb` 加入 ``` def sent_request uri = URI('http://localhost:3000/kamigo/response_body') response = Net::HTTP.get(uri) render plain: response end ``` 這是最簡單能發出 `Request` 的方法,我們先把網址字串轉換成 `URI` 物件,透過 `Net::HTTP.get` 這個方法,他會接受一個網址,然後把 `Response` 的 `Body` 部分以字串的形式傳回。在這段程式裡面我們只能看到傳入網址跟最終結果,我們不能調整 `Request Header` 和 `Request Body`,也不能觀察 `Response Header`。 我讓他連去 [http://localhost:3000/kamigo/response_body](http://localhost:3000/kamigo/response_body),這是我們昨天作好的東西。 ### 在瀏覽器開啟網址 網址:[http://localhost:3000/kamigo/sent_request](http://localhost:3000/kamigo/sent_request) 開啟網址後會看到: ``` 虎哇花哈哈哈 ``` ### 小黑框的顯示結果 ``` Started GET "/kamigo/sent_request" for 127.0.0.1 at 2018-01-04 01:06:38 +0800 Processing by KamigoController#sent_request as HTML Started GET "/kamigo/response_body" for 127.0.0.1 at 2018-01-04 01:06:39 +0800 Processing by KamigoController#show_response_body as */* ===這是設定前的response.body:=== Rendering text template Rendered text template (0.0ms) ===這是設定後的response.body:虎哇花哈哈哈=== Completed 200 OK in 344ms (Views: 9.2ms) Rendering text template Rendered text template (0.0ms) Completed 200 OK in 1400ms (Views: 2.7ms) ``` 突然想到有個東西還沒講,那就是 Ruby 的函數。 # Ruby 的函數 一個函數可以把一個東西變成另一個東西,或者把多個東西變成另一個東西。 舉個例:我們來作一個函數,輸入是一句話,我們要把這句話變成韓文。 ### 函數宣告 ``` def translate_to_korean(message) "#{message}油~" end ``` 一個函數裡面可以寫一段程式,最後一行程式的執行結果就會是函數的傳回值。 不管傳入什麼,我們就在後面加上`油~`。 ### 函數呼叫 ``` translate_to_korean('愛老虎') ``` 你想要使用小括號或者使用空白框住傳入的參數都可以。 ``` translate_to_korean '愛老虎' ``` ### 結果 ``` "愛老虎油~" ``` 成功翻譯成韓文! # 把翻譯韓文的功能加入到上面的爬蟲 ### 修改 Action ``` def sent_request uri = URI('http://localhost:3000/kamigo/response_body') response = Net::HTTP.get(uri) render plain: translate_to_korean(response) end ``` ### 開啟網頁 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9MdSRnLh1llHSA6vjV0L2PIpk2vHDkN24yQojeqoZoi_o9qVpC4FweKUukoDZxTis3cZLZXR7-XI0LssKlU1a_AW-M0_7dSGpF-iqzqS_rlG2Cc43ATtI14Yp2aApZwDgD3TrbA4jZBs/s1600/1.jpg) 壞掉囉,錯誤訊息是 `incompatible character encodings: ASCII-8BIT and UTF-8`。 下面那個深灰色區域是給我們除錯用的,有點類似 `irb`,我們可以在下面輸入一點東西: ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiubOD2gKe_ypBCjGrsay6W5JLpo946U0rvcn0lVbGndI6XsXh9et3HGSJwMbVqfAjC0py6bJbl-ypO2X-THDfllUyfl-ydpwJeuDeoy4RF5oZ-PPIGXaMroXGvz1nP9cpN1KCviL6jzIk/s1600/2.jpg) 我輸入了 `message`,看看 message 的值是什麼,結果看到是這樣: ``` >> message => "\xE8\x99\x8E\xE5\x93\x87\xE8\x8A\xB1\xE5\x93\x88\xE5\x93\x88\xE5\x93\x88" >> ``` 因為我們獲得的 response 字串的編碼是 `ASCII-8BIT`,沒辦法直接跟 `UTF-8` 編碼的 "油~" 加在一起。 所以修正的方法就是把 response 字串轉碼為 UTF-8 即可: ### 再次修改 Action ``` def sent_request uri = URI('http://localhost:3000/kamigo/response_body') response = Net::HTTP.get(uri).force_encoding("UTF-8") render plain: translate_to_korean(response) end ``` 在字串後面寫 `.force_encoding("UTF-8")` 就可以把編碼轉為 `UTF-8` 格式了。 ### 再次開啟網頁 ``` 虎哇花哈哈哈油~ ``` 玩樂時間結束,來作點正事,目前的寫法沒辦法觀察 `request` 和 `response`,我們需要能觀察 `request` 和 `response` 的寫法。 # 能觀察 request 和 response 的寫法 ### 修改 Action ``` def sent_request uri = URI('http://localhost:3000/kamigo/eat') http = Net::HTTP.new(uri.host, uri.port) http_request = Net::HTTP::Get.new(uri) http_response = http.request(http_request) render plain: JSON.pretty_generate({ request_class: request.class, response_class: response.class, http_request_class: http_request.class, http_response_class: http_response.class }) end ``` 越來越複雜了。通常要作到細膩的控制,就要寫比較多的程式碼。 我們先觀察一下這四個物件:`request`、`response`、`http_request`、`http_response`,前兩個是 Rails 內建的,後兩個是我們在 Action 中定義的,為了能夠同時輸出 4 個字串,我用一個雜湊陣列把想看的字串都放進去,然後使用 `JSON.pretty_generate` 幫我排成比較好閱讀的格式,讓我們來看一下個別對應的類別: ### 開啟網頁 ``` { "request_class": "ActionDispatch::Request", "response_class": "ActionDispatch::Response", "http_request_class": "Net::HTTP::Get", "http_response_class": "Net::HTTPOK" } ``` ### 圖解 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4WpA9VIeFxBdMV5WVTmXAlmBdgq_Y8xE2cBurbze0HcbV5cb3wsgD0mNjA8ZskFBAgyOIaogEUqjJv_eeM8nQ_ic9wSx1aG1LjfyC-s65rz-qyG7tlSn3s0_t-SoKjNRuQ3BlDLTG1KA/s1600/3.jpg) 昨天我們學的是 `1` 跟 `4`,今天學 `2` 跟 `3`,這裡的四個物件,都是屬於不同的類別,所以我們可能需要不同的語法去存取他們。 今天只要了解到這裡就行了。 # 總結 - 不是只有瀏覽器才能發出 HTTP Request,網頁伺服器一樣可以發出 HTTP Request - 今天學會了宣告函數和呼叫函數的正確觀念。 明天講怎麼讓別人能連到你作的網站。

沒有留言: