分享一個用go寫的爬取非同步載入網站資料的例子

阿神發表於2022-06-01

由於公司業務需要,需要爬取一些域名註冊商資訊,找到了一個網站,資料是非同步載入出來的,之前一般使用php的QueryList,正好最近在看go,就索性用go來練手了,例子是基於chromedp(chromedp是go寫的,支援Chrome DevTools Protocol 的一個驅動瀏覽器的庫。並且它不需要依賴其他的外界服務(比如 Selenium 和 PhantomJs))。關於chromedp,可以看github上的文件以及一些例子,我也只是看了一點我需要用的(直達chromedp),程式碼只是實現了業務,不夠優雅,大家大概看個思路和用法即可,其他的可以自己實現,廢話不多說,直接上程式碼吧,該註釋的都註釋了。

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
    "strconv"
    "time"

    "github.com/chromedp/cdproto/cdp"
    "github.com/chromedp/chromedp"
    _ "github.com/go-sql-driver/mysql"
)

type Registrar struct {
    r_id   int64
    name   string
    url    string
    contry string
    number string
    email  string
}

func addToDb(l Registrar, db *sql.DB) {
    stmt, err := db.Prepare("INSERT registrar SET r_id=?,name=?,url=?,contry=?,number=?,email=?")
    checkErr(err)
    res, err := stmt.Exec(l.r_id, l.name, l.url, l.contry, l.number, l.email)
    checkErr(err)
    insert_id, err := res.LastInsertId()
    checkErr(err)
    fmt.Println(insert_id)
}

func initDb() *sql.DB {
    connectionString := fmt.Sprintf("root:%s@tcp(ip:3306)/test?charset=utf8&parseTime=True&loc=Local",
        "password")
    db, err := sql.Open("mysql", connectionString)
    checkErr(err)
    err = db.Ping()
    checkErr(err)
    return db
}

func main() {
    //連線資料庫
    db := initDb()
    // 禁用chrome headless
    opts := append(chromedp.DefaultExecAllocatorOptions[:],
        chromedp.Flag("headless", false),
    )
    allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
    defer cancel()
    ctx, cancel := chromedp.NewContext(
        allocCtx,
        chromedp.WithLogf(log.Printf),
    )
    defer cancel()

    var t1, t2, t3, t4, t6, t7 []*cdp.Node
    //開啟一個chrome
    err := chromedp.Run(ctx,
        chromedp.Navigate("https://www.ggcx.com/main/globalRegistrar"), //開啟首頁
        chromedp.WaitVisible(`div[class="tabb"]`),                      //等著頁面非同步載入完成
        //獲取需要的資料
        chromedp.Nodes(`.//td[@class="t1"]`, &t1),
        chromedp.Nodes(`.//td[@class="t2"]`, &t2),
        chromedp.Nodes(`.//td[@class="t3"]`, &t3),
        chromedp.Nodes(`.//td[@class="t4"]`, &t4),
        chromedp.Nodes(`.//td[@class="t6"]`, &t6),
        chromedp.Nodes(`.//td[@class="t7"]`, &t7),
        chromedp.Sleep(3*time.Second),
        chromedp.Click(`div[class="right"]`, chromedp.NodeVisible), //點選下一頁
        chromedp.Sleep(5*time.Second),
    )
    checkErr(err)
    //組裝資料
    for k, node := range t1 {
        r_id, _ := strconv.ParseInt(node.Children[0].NodeValue, 10, 64)
        l := Registrar{
            r_id,
            t2[k].Children[0].NodeValue,
            t3[k].Children[0].NodeValue,
            t4[k].Children[0].NodeValue,
            t6[k].Children[0].NodeValue,
            t7[k].Children[0].NodeValue,
        }
        //資料加入資料庫
        addToDb(l, db)
    }

    //獲取分頁的資料
    for i := 0; i < 140; i++ {
        err := chromedp.Run(ctx,
            chromedp.WaitVisible(`div[class="tabb"]`),
            chromedp.Nodes(`.//td[@class="t1"]`, &t1),
            chromedp.Nodes(`.//td[@class="t2"]`, &t2),
            chromedp.Nodes(`.//td[@class="t3"]`, &t3),
            chromedp.Nodes(`.//td[@class="t4"]`, &t4),
            chromedp.Nodes(`.//td[@class="t6"]`, &t6),
            chromedp.Nodes(`.//td[@class="t7"]`, &t7),
            chromedp.Sleep(3*time.Second),
            chromedp.Click(`div[class="right"]`, chromedp.NodeVisible), //點選下一頁
            chromedp.Sleep(5*time.Second),
        )
        checkErr(err)
        for k, node := range t1 {
            r_id, _ := strconv.ParseInt(node.Children[0].NodeValue, 10, 64)
            l := Registrar{
                r_id,
                t2[k].Children[0].NodeValue,
                t3[k].Children[0].NodeValue,
                t4[k].Children[0].NodeValue,
                t6[k].Children[0].NodeValue,
                t7[k].Children[0].NodeValue,
            }
            addToDb(l, db)
        }
    }
}
func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

涉及到的資料表

CREATE TABLE `registrar` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `r_id` bigint(20) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `url` varchar(255) DEFAULT NULL,
  `contry` varchar(255) DEFAULT NULL,
  `number` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=281 DEFAULT CHARSET=utf8;

本作品採用《CC 協議》,轉載必須註明作者和本文連結
在等待的日子裡,努力工作,刻苦讀書,鍛鍊身體,謙卑做人,養得深根,日後才能枝葉茂盛

相關文章