title: go使用chromedp爬蟲
tags: go,chromedp
author: Clown95
由於chromedp的更新,該文章的代碼已經不兼容,因此我重寫了一篇,並且代碼更爲詳細,小夥伴們可以點擊下面的超鏈接閱讀。
背景
最近朋友讓我幫忙爬取一個網站上面的數據,當時看到頁面覺得很簡單,雖然有25000多頁,但是網站沒有反爬蟲機制,只要多開幾個協程就行。
當我簡單的爬取第一頁之後,發現url沒有page參數。好吧,查看翻頁的源代碼看看,javascript:__doPostBack('anpDataPager','2')
看到這個翻頁我有點頭疼,是aps寫的後臺,通過js調dll內部跳轉的。
考慮到一般翻頁無非是url和ajax。我又去控制檯查看ajax,打開控制檯選中XHR
,結果讓我有點懵逼,根本就沒有ajax。
我當時就打退堂鼓了,但是又考慮到跟朋友一口一個保證,沒辦法硬着頭皮來吧,那就在url上嘗試使用anpDataPager傳參看看,還是不行,那就再換curpage依然不行,當我嘗試了N遍了常用的curpage參數後,這種方法也以失敗告終。
沒法比,答應別人的事情當然得盡力完成,就突發奇想使用按鍵精靈這裏腳本語言,模擬手動操作。因爲需要查找翻頁按鈕操作,我只能前臺運行。但是有一個很棘手的問題,網站所在服務器速度很慢,大概2-4秒才能顯示出數據,而且數據量稍大25675頁,我這模擬下去,我得幾天不幹活。
百度搜索看看有沒有其他大佬遇到過類似的情況,你別說還真有,但是大佬使用的Python 模擬提交form __VIEWSTATE __EVENTTARGET __EVENTARGUMENT __EVENTVALIDATION
這四個數據,我用go模仿大佬的方法不知道怎麼就是不行。
那就在繼續搜索下,看到可以用selenium
和chromedp
來模擬瀏覽器操作,最終選擇了chromedp
chromedp 介紹
chromedp包是一種更快,更簡單的方法,可以使用無外部依賴關係(即Selenium,PhantomJS等)來驅動支持Go中的Chrome DevTools協議的瀏覽器 。
安裝
go get -u github.com/chromedp/chromedp
注意:需要安裝chrome瀏覽器或者chromedrive
使用
官方提供的Demo
https://github.com/chromedp/examples
Api查詢地址
https://godoc.org/github.com/chromedp/chromedp
需要爬的內容
<tbody>
<tr>
<td>1</td>
<td>2019/3/22 11:11:31</td>
<td>400</td>
<td>1,000,000</td>
<td>1,000</td>
<td>1,000,400</td>
<td>aabb123</td>
<td> </td>
<td>92fox</td>
<td>92fox</td>
<td>上分</td>
</tr>
.....
</tbody>
我直接貼上我爬蟲的代碼
package main
import (
"context"
"errors"
"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/network"
"time"
"log"
"github.com/chromedp/chromedp"
)
var res string // 定義全局變量,用來保存爬蟲的數據
func main() {
var err error
// 創建鏈接
ctxt, cancel := context.WithCancel(context.Background())
defer cancel()
//創建chrome.New()創建新的chrome實例
c, err := chromedp.New(ctxt, chromedp.WithLog(log.Printf))
if err != nil {
log.Fatal(err)
}
x, err := chromedp.New(ctxt, chromedp.WithLog(log.Printf))
if err != nil {
log.Fatal(err)
}
//執行任務
err = c.Run(ctxt, visitWeb("http://dl.gaggjz.pw:8086/OpRoot/MemberScoreList.aspx?uid=0&op=0&uname=sdafsadsaf"))
if err != nil {
log.Fatal(err)
}
// 循環翻頁
for i := 1; i < 25000; i++ {
//執行
err = x.Run(ctxt, DoCrawler()) //執行爬蟲任務
WirteTXT(res)// res的內容寫入文本
}
}
// 任務 主要用來設置cookie ,獲取登錄賬號後的頁面
func visitWeb(url string) chromedp.Tasks {
return chromedp.Tasks{
chromedp.ActionFunc(func(ctxt context.Context, h cdp.Executor) error {
expr := cdp.TimeSinceEpoch(time.Now().Add(180 * 24 * time.Hour))
success, err := network.SetCookie("ASP.NET_SessionId", "這裏是值"). //設置cookie
WithExpires(&expr).
WithDomain("dl.gaggjz.pw:8086"). //訪問網站主體
WithHTTPOnly(true).
Do(ctxt, h)
if err != nil {
return err
}
if !success {
return errors.New("could not set cookie")
}
return nil
}),
chromedp.Navigate(url), //頁面跳轉
}
}
// 任務 主要執行翻頁功能和或者html
func DoCrawler() chromedp.Tasks {
//sel =fmt.Sprintf(`javascript:__doPostBack('anpDataPager','%s')`,"2")
return chromedp.Tasks{
chromedp.Sleep(1*time.Second), // 等待
chromedp.WaitVisible(`#form1`, chromedp.ByQuery),//等待id=from1頁面可見 ByQuery是使用DOM選擇器查找
chromedp.Sleep(1*time.Second),
chromedp.Click(`.pagination li:nth-last-child(4) a`, chromedp.ByQuery),//點擊翻頁
chromedp.OuterHTML(`tbody`, &res, chromedp.ByQuery), //獲取改 tbody標籤的html
}
}
func WirteTXT(txt string ) {
f, err := os.OpenFile("1.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
if err != nil {
fmt.Println("os Create error: ", err)
return
}
defer f.Close()
bw := bufio.NewWriter(f)
bw.WriteString(txt+"\n")
bw.Flush()
}
總結
通過代碼可以看到我創建了chromedp.Tasks
任務,第一個任務主要就是爲了設置cookie即模擬登錄,第二個任務是最重要的,它主要用來點擊下一頁按鈕和獲取指定html內容。因爲第一次接觸chromedp,所以我剛開始把兩個任務放在一個任務裏,就造成了第一頁和第二頁來回跳轉。所以突發奇想創建了2個任務。當然我後來才知道,單獨執行第二個任務的時候,可以手動輸入地址和登錄賬號,程序也是可以執行的,就是有個缺點,執行一段時間可能cookie失效,需要重新登錄。要注意的是chromedp使用的是DOM 原生選擇器,我剛開始看到ByQuery 以爲可以使用JQuery選擇器,一直篩選不到內容。 還有一個坑點就是,我爬取的td標籤是在tbody內的,直接使用chromedp.Text()
獲取不到內容,所以退而求次,獲取了html,但是我發現goquery 讀取文本里面的html也不能獲取到td的內容,最後我把tbody替換成table,可以成功的獲取到內容。