分享一个用go写的爬取异步加载网站数据的例子

由于公司业务需要,需要爬取一些域名注册商信息,找到了一个网站,数据是异步加载出来的,之前一般使用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 协议》,转载必须注明作者和本文链接
在等待的日子里,努力工作,刻苦读书,锻炼身体,谦卑做人,养得深根,日后才能枝叶茂盛
本帖由系统于 1年前 自动加精
阿神
讨论数量: 3

非常棒!very good

1年前 评论

Nice! 感谢分享

1年前 评论

// 采集(本页标题)的结果有些出乎意料: // 执行结果:childrenCount =2 children =[] , 取不到标题咋回事儿?

start chromedp len nodes= 1 children: [] childrenCount: 2

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!