爬蟲靠演技,表演得越像瀏覽器,抓取數據越容易,這是我多年爬蟲經驗的感悟。回顧下我的的爬蟲經歷,共分三個階段:第一階段,09年左右開始接觸爬蟲,那時因爲項目須要,要訪問各大國際社交網站,Facebook,myspace,filcker,youtube等等,國際上叫得上名字的社交網站都爬過,大部分網站提供restful api,有些功能沒有api,就只能用http抓包工具分析協議,本身爬;國內的優酷、土豆、校內網、web版qq、網頁郵箱等等也都爬過;那時候先用C#寫demo,項目是C++的,因此還要轉換成託管C++的代碼。第一階段的主要心得是cookie管理,比較難搞的cookie就藉助於webbrowser控件。html
第二階段是13年左右,作的是金融數據分析類軟件和網絡機器人,爬蟲編程語言依然藉助於C# ,發包收包全靠HttpWebRequest和HttpWebResponse,cookie管理靠CookieContainer,HTML分析靠HtmlAgilityPack,驗證碼識別靠本身預處理封裝過的tesseract,協議分析靠fiddler,元素選擇靠瀏覽器調試器,這套功夫在手基本能夠暢遊網絡,實現的機器人隨意遊走於博客、微博,自動留言、發帖、評論;各大金融網站、上交所、深交所、巨潮網絡、互動平臺等等數據任爬。git
第三階段就是如今了。四年多過去了,從新學習審視爬蟲技術,發現武器更強大了:go語言,goquery,colly,chromedp,webloop等,強大的語言及工具使爬蟲更簡單、更高效。github
多年的爬蟲經驗總結了開頭那句話。已知的爬蟲手段無外乎三大類:一,分析HTTP協議,構造請求;二,利用瀏覽器控件,獲取cookie、頁面元素、調用js腳本等;phantomjs、webloop屬於此類;第三類是直接操做瀏覽器,chromedp屬於此類;微軟還提供了操縱ie瀏覽器的com接口,很早之前用C++寫過,比較難用,代碼寫起來很噁心,須要較多的條件判斷。構造請求直接快速,瀏覽器控件和操縱瀏覽器可靠安全,能夠省去不少沒必要要的協議分析、js腳本分析,但速度慢,加載了不少無用的數據、圖片等;第2、三種與第一種混用效果更佳,只要表演地越像瀏覽器就越安全可靠,或者乾脆操縱瀏覽器,只要不超過服務器的人類操做閾值斷定,ip基本不會被封。單ip不夠用時,就設置代理來切換。web
學無止境,不斷用新的武器武裝本身。下面貢獻一個小例子,爬取上交所的AB股股票列表,簡單地show下演技。(哈哈哈)ajax
該頁面提供了下載功能,A股的下載地址 http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=1chrome
B股的下載地址 http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=2編程
拿到了這個地址就開始Visit了api
c.Visit("http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=1")
UserAgent設置成了Chrome瀏覽器
c.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36"
發現不行,程序會報錯,安全
2018/01/03 23:39:27 Forbidden
把這個網址直接在瀏覽器地址欄中打開也是不行的,會報告「Error 403: SRVE0190E: 找不到文件:/error/error_cn.jsp」
服務端作了些限制,打開fiddler看下協議
請求中有一大堆cookie,第一感受是可能沒有加cookie的緣故,因而利用chromedp打開頁面,再調用ajax去請求,剛開始ajax沒有帶cookie也請求成功了,
後來發現關鍵在於請求頭中的「Referer」,有了Referer就好了。
乾脆把全部的頭補全,更像瀏覽器些,這不會吃虧:
c.OnRequest(func(r *colly.Request) { r.Headers.Set("Host", "query.sse.com.cn") r.Headers.Set("Connection", "keep-alive") r.Headers.Set("Accept", "*/*") r.Headers.Set("Origin", "http://www.sse.com.cn") r.Headers.Set("Referer", "http://www.sse.com.cn/assortment/stock/list/share/") //關鍵頭 若是沒有 則返回 錯誤 r.Headers.Set("Accept-Encoding", "gzip, deflate") r.Headers.Set("Accept-Language", "zh-CN,zh;q=0.9") })
附上完整的代碼,將股票保存到CSV文件
package sse import ( "encoding/csv" "os" "strings" "github.com/gocolly/colly" ) /*GetStockListA 獲取上海證券交易所股票列表 A股 */ func GetStockListA(saveFile string) (err error) { stocks, err := getStockList("http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=1") if err != nil { return err } err = saveStockList2CSV(stocks, saveFile) return } /*GetStockListB 獲取上海證券交易所股票列表 B股 */ func GetStockListB(saveFile string) (err error) { stocks, err := getStockList("http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=2") if err != nil { return err } err = saveStockList2CSV(stocks, saveFile) return } func saveStockList2CSV(stockList string, file string) (err error) { vals := strings.Split(stockList, "\n") f, err := os.Create(file) if err != nil { return err } defer f.Close() fw := csv.NewWriter(f) for _, row := range vals { rSplits := strings.Split(row, "\t") rSplitsRslt := make([]string, 0) for _, sp := range rSplits { trimSp := strings.Trim(sp, " ") if len(trimSp) > 0 { rSplitsRslt = append(rSplitsRslt, trimSp) } } if len(rSplitsRslt) > 0 { err = fw.Write(rSplitsRslt) if err != nil { return err } } } fw.Flush() return } func getStockList(url string) (stockList string, err error) { //GET http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=1 HTTP/1.1 //Host: query.sse.com.cn //Connection: keep-alive //Accept: */* //Origin: http://www.sse.com.cn //User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36 //Referer: http://www.sse.com.cn/assortment/stock/list/share/ //Accept-Encoding: gzip, deflate //Accept-Language: zh-CN,zh;q=0.9` c := colly.NewCollector() c.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36" c.OnRequest(func(r *colly.Request) { r.Headers.Set("Host", "query.sse.com.cn") r.Headers.Set("Connection", "keep-alive") r.Headers.Set("Accept", "*/*") r.Headers.Set("Origin", "http://www.sse.com.cn") r.Headers.Set("Referer", "http://www.sse.com.cn/assortment/stock/list/share/") //關鍵頭 若是沒有 則返回 錯誤 r.Headers.Set("Accept-Encoding", "gzip, deflate") r.Headers.Set("Accept-Language", "zh-CN,zh;q=0.9") }) c.OnResponse(func(resp *colly.Response) { stockList = string(resp.Body) }) c.OnError(func(resp *colly.Response, errHttp error) { err = errHttp }) err = c.Visit(url) return }
func main() { var err error err = sse.GetStockListA("e:\\sseA.csv") if err != nil { log.Fatal(err) } err = sse.GetStockListB("e:\\sseB.csv") if err != nil { log.Fatal(err) } }
轉載請註明出處: http://www.cnblogs.com/majianguo/p/8186429.html