分享一个用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 协议》,转载必须注明作者和本文链接
本帖由系统于 2年前 自动加精
非常棒!very good
Nice! 感谢分享
// 采集(本页标题)的结果有些出乎意料: // 执行结果:childrenCount =2 children =[] , 取不到标题咋回事儿?
start chromedp len nodes= 1 children: [] childrenCount: 2