如何用 Go 實作一個簡單的 PTT 爬蟲

更新於 發佈於 閱讀時間約 9 分鐘


raw-image

👨‍💻 簡介

最近想要透過小實作來撰寫筆記,達到做中學的效果,因此就來實作個小爬蟲順便結合前面學到的package做一個小複習。

建立HTTP Client

Go的net/http package 提供了一個HTTP Client,用來發送各種HTTP請求。

  • http.Get:發送GET請求。
  • http.Post:發送POST請求。
  • http.NewRequest:建立一個新的HTTP請求。

語法如下:

// 發送GET請求
func http.Get(url string) (resp *http.Response, err error)

// 發送POST請求
func http.Post(url, contentType string, body io.Reader) (resp *http.Response, err error)

// 建立一個新的HTTP請求
func http.NewRequest(method, url string, body io.Reader) (*http.Request, error)

常見的Response可以使用以下欄位

type Response struct {
Status string // e.g. "200 OK"
StatusCode int // e.g. 200
Proto string // e.g. "HTTP/1.0"
Header Header
Body io.ReadCloser
...
}

接著看一下io.ReadCloser

type ReadCloser interface {
Reader
Closer
}

可以看到ReadCloser是 interface,接著來看一下Reader

type Reader interface {
Read(p []byte) (n int, err error)
}

Reader也是一個interface,裡面有定義了Read方法,因此可以推測出resp.Body(作為一個 io.ReadCloser)也實現了 io.Reader interface。

package main

import (
"fmt"
"io"
"net/http"
)
func main() {
// 使用 http.Get 發送GET請求
resp, err := http.Get("https://www.example.com")
if err != nil {
fmt.Println("Error:", err)
return
}

// 拿到Body最後要確保有關閉連線
defer resp.Body.Close()

// 語法為func ReadAll(r io.Reader) ([]byte, error)
// 會回傳[]byte,要透過string package轉為string
body, _ := io.ReadAll(resp.Body)
fmt.Println("status code: ", resp.StatusCode)
fmt.Println("body: ")
fmt.Println(string(body))
}

在上面的範例中,簡單的使用http.Get取得網頁的相關資訊。接下來試著寫一個簡單的爬蟲,來爬取ptt。

爬取PTT八卦版標題

1. 建立http client以及自定義請求

client := &http.Client{}
req, err := http.NewRequest("GET", "https://www.ptt.cc/bbs/Gossiping/index.html", nil)
if err != nil {
log.Fatal(err)
}

因為八卦版會有詢問是否年滿18,因此會需要設定cookie,而要設定cookie則必須使用自定義請求的方式,使用自定義請求則必須自己建立client進行請求的發送。

2. 設定cookie

先去網站上看cookie的name跟value為多少

raw-image

接著設定對應的參數

// 設定cookie,可
req.AddCookie(&http.Cookie{Name: "over18", Value: "1"})

3. 發送請求

resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

使用剛剛建立的client放入自定義請求就可以完成請求的發送並取得回傳。

4. 解析回傳資訊

在進行這一步之前需要先安裝一個 go package,github.com/PuerkitoBio/goquery,主要用來解析html的標籤屬性。

go get github.com/PuerkitoBio/goquery

等一下會用到的方法為

func NewDocumentFromReader(r io.Reader) (*Document, error)

而Document是一個struct

type Document struct {
*Selection
}

要根據css selector下去找資料,可以使用Find方法,而當找到匹配的元素時,對這個元素接著使用Each方法取得相關屬性,像是Text或是Attr等

func (s *Selection) Find(selector string) *Selection
func (s *Selection) Each(f func(int, *Selection)) *Selection

接著依照ptt的網站結構查看title的標籤為div,class為title,而裡面還有一層包著超連結,因此要爬取的路徑就可以依照下面的格式撰寫

doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
log.Fatal(err)
}

// 提取文章標題
doc.Find("div.title a").Each(func(index int, item *goquery.Selection) {
title := item.Text()
fmt.Println(title)
})

以下為完整程式碼

package main

import (
"fmt"
"log"
"net/http"
"github.com/PuerkitoBio/goquery"
)

func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://www.ptt.cc/bbs/Gossiping/index.html", nil)
if err != nil {
log.Fatal(err)
}

// 設定cookie模擬已滿18歲的使用者
req.AddCookie(&http.Cookie{Name: "over18", Value: "1"})

resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
log.Fatal(err)
}

// 提取文章標題
doc.Find("div.title a").Each(func(index int, item *goquery.Selection) {
title := item.Text()
fmt.Println(title)
})
}

📚Reference

留言
avatar-img
留言分享你的想法!
avatar-img
Alan的開發者天地
18會員
83內容數
golang
Alan的開發者天地的其他內容
2023/09/28
👨‍💻簡介 今天來介紹一個自己開發後端蠻常用的一個 package,promptui,拿來做menu真的很方便,promptui有兩個主要的輸入模式: Prompt:跳出單行使用者輸入。 Select:提供一個選項列表供使用者選擇。
Thumbnail
2023/09/28
👨‍💻簡介 今天來介紹一個自己開發後端蠻常用的一個 package,promptui,拿來做menu真的很方便,promptui有兩個主要的輸入模式: Prompt:跳出單行使用者輸入。 Select:提供一個選項列表供使用者選擇。
Thumbnail
2023/09/26
👨‍💻 簡介 在處理string時,正則表達式是一個非常有用的工具。Go語言的regexp package 可以使用正則表達式,用來執行如檢查string是否匹配某個模式、提取匹配的subString等操作。
Thumbnail
2023/09/26
👨‍💻 簡介 在處理string時,正則表達式是一個非常有用的工具。Go語言的regexp package 可以使用正則表達式,用來執行如檢查string是否匹配某個模式、提取匹配的subString等操作。
Thumbnail
2023/09/22
👨‍💻 簡介 一開始介紹基本資料型別時有稍微提到一點string的處理,今天介紹string的一些操作,像是檢查的功能、修改的功能、比較的功能等等。
Thumbnail
2023/09/22
👨‍💻 簡介 一開始介紹基本資料型別時有稍微提到一點string的處理,今天介紹string的一些操作,像是檢查的功能、修改的功能、比較的功能等等。
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
GOB Go官方有提供net/rpc的RPC套件。此套件提供GOB的編/解碼,且支援TCP或HTTP傳輸方式。它可以在伺服器端註冊多個不同類型物件。 遠端存取的要求條件 方法的類型可輸出 方法的本體可輸出 方法必須要有兩個參數是輸出或內建 方法的第二個參數是指標型 方法的返回類型為
Thumbnail
GOB Go官方有提供net/rpc的RPC套件。此套件提供GOB的編/解碼,且支援TCP或HTTP傳輸方式。它可以在伺服器端註冊多個不同類型物件。 遠端存取的要求條件 方法的類型可輸出 方法的本體可輸出 方法必須要有兩個參數是輸出或內建 方法的第二個參數是指標型 方法的返回類型為
Thumbnail
Request內容 package main import ( "fmt" "log" "net/http" "strings" ) func request(w http.ResponseWriter, r *http.Request) { //這些資訊是輸出到伺服器端的列印訊息
Thumbnail
Request內容 package main import ( "fmt" "log" "net/http" "strings" ) func request(w http.ResponseWriter, r *http.Request) { //這些資訊是輸出到伺服器端的列印訊息
Thumbnail
HTTP伺服器端 package main import ( "net/http" ) type Refer struct { handler http.Handler refer string } //實現中介軟體邏輯​ func (this *Refer) ServeHTTP(
Thumbnail
HTTP伺服器端 package main import ( "net/http" ) type Refer struct { handler http.Handler refer string } //實現中介軟體邏輯​ func (this *Refer) ServeHTTP(
Thumbnail
htmx前陣子還只是網路迷因,近期拿來搭配go的系列影片越來越多了,兩者都可以用很少量的程式實作出功能,很適合拿來做些簡單的project。
Thumbnail
htmx前陣子還只是網路迷因,近期拿來搭配go的系列影片越來越多了,兩者都可以用很少量的程式實作出功能,很適合拿來做些簡單的project。
Thumbnail
👨‍💻 簡介 最近想要透過小實作來撰寫筆記,達到做中學的效果,因此就來實作個小爬蟲順便結合前面學到的package做一個小複習。
Thumbnail
👨‍💻 簡介 最近想要透過小實作來撰寫筆記,達到做中學的效果,因此就來實作個小爬蟲順便結合前面學到的package做一個小複習。
Thumbnail
在這一課中,我們將學習如何在Python中使用requests模塊發送HTTP請求。 發送GET請求requests.get() 函數用於發送GET請求。
Thumbnail
在這一課中,我們將學習如何在Python中使用requests模塊發送HTTP請求。 發送GET請求requests.get() 函數用於發送GET請求。
Thumbnail
這篇想來寫,剛碰到js得時候,為了讓程式可以運作而安裝Node.js 。Node.js 是能夠在伺服器上面運行 JavaScript 的應用平台環境,透過 Node.js 提供的函式庫與執行環境能完成伺服器端服務。此篇幅就直接從純後端的角度切入摟(對不起拉我寫來寫去還是不知道怎麼順順的寫好文章開頭Q
Thumbnail
這篇想來寫,剛碰到js得時候,為了讓程式可以運作而安裝Node.js 。Node.js 是能夠在伺服器上面運行 JavaScript 的應用平台環境,透過 Node.js 提供的函式庫與執行環境能完成伺服器端服務。此篇幅就直接從純後端的角度切入摟(對不起拉我寫來寫去還是不知道怎麼順順的寫好文章開頭Q
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News