Golang 網絡爬蟲框架gocolly/colly 四

Golang 網絡爬蟲框架gocolly/colly 四

       爬蟲靠演技,表演得越像瀏覽器,抓取數據越容易,這是我多年爬蟲經驗的感悟。回顧下我的的爬蟲經歷,共分三個階段:第一階段,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

相關文章
相關標籤/搜索