最近在写爬虫,用的是go的chromedp包
其中遇到两个问题:
1、爬取多个网页URL,每次爬取都要好几秒。
2、解决了上面问题1,又出现context deadline exceed问题。
我们先谈谈第1个问题
网上很多文章都写了chromedp.run()爬取代码,都在生成context时cancel()了,所以再次调用chrome.run()时又要重新生成新的context,相当于重新启动chrome浏览器,所以慢。根据某个网友文章,其实只要暂时不要cancel(),最后一个url爬取以后再cancel()就行,这样相当于还是开着浏览器,而只是重新打开别的新URL页面。
如下代码:
// GetHttpHtmlContent 获取HTML内容
func GetHttpHtmlContent(url string, ctx context.Context) (string, context.Context, context.CancelFunc, error){
alock.Lock()
defer alock.Unlock()
var res string
var cancel context.CancelFunc
//新建生成对象
options := []chromedp.ExecAllocatorOption{
// false意味展示浏览器窗口,默认true
chromedp.Flag("headless", true),
chromedp.Flag("hide-scrollbars", false),
chromedp.Flag("mute-audio", false),
chromedp.Flag("blink-settings", "imagesEnabled=false"), //不加载图片,提高速度
chromedp.UserAgent(`Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36`),
}
if ctx == nil {
options = append(chromedp.DefaultExecAllocatorOptions[:], options...)
ctx, _ = chromedp.NewExecAllocator(context.Background(), options...)
ctx, _ = chromedp.NewContext(ctx)
}
ctx, cancel = context.WithTimeout(ctx, 5*time.Second)
//defer cancel() //这里隐藏掉cancel,不要在这里做
// listen network event
//listenForNetworkEvent(ctx)
//远程获取内容并生成pdf
err := chromedp.Run(ctx, chromedp.Tasks{
network.Enable(),
chromedp.Navigate(url),
chromedp.WaitReady("body"),
chromedp.Sleep(200 * time.Millisecond),
chromedp.OuterHTML(`document.querySelector("html")`, &res, chromedp.ByJSPath),
})
return res, ctx, cancel, err
}
下面我们就可以多次执行同个chrome实例,性能快了几倍,之前我测试有6到8秒。现在只要几百毫秒。
func main() {
var ctx context.Context
//第一次传nil的context
url := "http://www.ocara.cn"
s := time.Now()
_, ctx, _, _ = GetHttpHtmlContent(url, nil) //爬取
fmt.Println("use time:", time.Since(s))
//10000次测试。新的都传已生成的context参数ctx
for i := 1; i < 10000; i++ {
s := time.Now()
_, ctx, _, _ = GetHttpHtmlContent(url, ctx) //爬取
fmt.Println("use time",i, ":", time.Since(s))
}
}
执行go run main.go,