由於公司業務需要,需要爬取一些域名註冊商資訊,找到了一個網站,資料是非同步載入出來的,之前一般使用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 協議》,轉載必須註明作者和本文連結