123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- package tick
- import "net/http"
- import "io/ioutil"
- import "regexp"
- import "strings"
- import "bytes"
- import "errors"
- import "fmt"
- import "strconv"
- import "time"
- import "log"
- import "golang.org/x/text/encoding/simplifiedchinese"
- var symbolurl = "http://quote.eastmoney.com/stock_list.html"
- var lastMarket map[string]string
- func GetInstrument() ([]string, []string, error) {
- return getInstrument()
- }
- func getInstrument() ([]string, []string, error) {
- resp, err := http.Get(symbolurl)
- if err != nil {
- return nil, nil, err
- }
- defer resp.Body.Close()
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, nil, err
- }
- symbolcontent := bytes.SplitN(data, []byte(`<div id="quotesearch">`), 2)
- if len(symbolcontent) != 2 {
- return nil, nil, errors.New("no symbolcontent-0")
- }
- symbolcontent = bytes.SplitN(symbolcontent[1], []byte("</ul>"), 3)
- if len(symbolcontent) != 3 {
- return nil, nil, errors.New("no symbolcontent-1")
- }
- //sh symbol list
- shlist, err := getli(symbolcontent[0])
- if err != nil {
- return nil, nil, err
- }
- //sz
- szlist, err := getli(symbolcontent[1])
- if err != nil {
- return nil, nil, err
- }
- var sh []string
- for i := 0; i < len(shlist); i++ {
- sym , _ := getsymbol(string(shlist[i]))
- sh = append(sh, sym)
- }
- var sz []string
- for i := 0; i < len(szlist); i++ {
- sym, _ := getsymbol(string(szlist[i]))
- sz = append(sz, sym)
- }
- return sh, sz, nil
- }
- func GetInstrument2() ([]string, []string, []string, []string, error) {
- resp, err := http.Get(symbolurl)
- if err != nil {
- return nil, nil,nil, nil, err
- }
- defer resp.Body.Close()
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, nil, nil, nil, err
- }
- symbolcontent := bytes.SplitN(data, []byte(`<div id="quotesearch">`), 2)
- if len(symbolcontent) != 2 {
- return nil, nil, nil, nil, errors.New("no symbolcontent-0")
- }
- symbolcontent = bytes.SplitN(symbolcontent[1], []byte("</ul>"), 3)
- if len(symbolcontent) != 3 {
- return nil, nil, nil, nil, errors.New("no symbolcontent-1")
- }
- //sh symbol list
- shlist, err := getli(symbolcontent[0])
- if err != nil {
- return nil, nil, nil, nil, err
- }
- //sz
- szlist, err := getli(symbolcontent[1])
- if err != nil {
- return nil, nil, nil ,nil, err
- }
- var sh []string
- var shname []string
- trans := simplifiedchinese.GBK.NewDecoder()
- dst := make([]byte, 1024)
-
- for i := 0; i < len(shlist); i++ {
- symbol, name := getsymbol(string(shlist[i]))
- sh = append(sh, symbol)
- nDst, _, err := trans.Transform(dst, []byte(name), true)
- if err == nil {
- name = string(dst[0:nDst])
- }
- shname = append(shname, name)
- }
- var sz []string
- var szname []string
- for i := 0; i < len(szlist); i++ {
- symbol, name := getsymbol(string(szlist[i]))
- sz = append(sz, symbol)
- nDst, _, err := trans.Transform(dst, []byte(name), true)
- if err == nil {
- name = string(dst[0:nDst])
- }
- szname = append(szname, name)
- }
- return sh, sz, shname, szname, nil
- }
- func getli(data []byte) ([][]byte, error) {
- return matchTag(data, "li")
- }
- func getsymbol(data string) (string, string) {
- data = stripTags(data)
- o, _, err := strRange(data, "(", ")", 0)
- if err != nil {
- return "", ""
- }
- name := strings.Replace(data, "("+o+")", "", -1)
- return o, name
- }
- func strRange(html string, start string, end string, offset int) (string, int, error) {
- istart := strings.SplitN(html, start, 2)
- if len(istart) != 2 {
- return "", 0, errors.New("start string not found")
- }
- if offset > 0 {
- istart[1] = istart[1][offset:]
- }
- iend := strings.SplitN(istart[1], end, 2)
- if len(iend) != 2 {
- return "", 0, errors.New("end string not found")
- }
- return iend[0], len(istart[0]) + len(iend[0]), nil
- }
- func matchTag(html []byte, tagname string) ([][]byte, error) {
- exp, err := regexp.Compile("(?i)<" + tagname + "[^>]*>(.*?)</" + tagname + ">")
- if err != nil {
- return nil, err
- }
- matched := exp.FindAllSubmatch(html, -1)
- if len(matched) == 0 {
- return nil, fmt.Errorf("tagname=%s not matched", tagname)
- }
- ret := make([][]byte, len(matched))
- for i := 0; i < len(ret); i++ {
- ret[i] = matched[i][1]
- }
- return ret, nil
- }
- var tagsRegexp = regexp.MustCompile("<[^>]+>")
- func stripTags(html string) string {
- return tagsRegexp.ReplaceAllString(html, "")
- }
- type SinaDS struct {
- *DSBase
- }
- func init() {
- drivers[Sina] = newSinaDS
- }
- func newSinaDS(conf *DsConf) (DataSource, error) {
- return &SinaDS{
- DSBase: NewDsBase(conf), // lmax 自己下载历史数据, 所以参数db==nil
- }, nil
- }
- func (lds *SinaDS) Name() string {
- return Sina
- }
- func (lds *SinaDS) Run() {
- if !lds.conf.Run {
- log.Println("SinaDS.run config NOT run")
- return
- }
- log.Println("SinaDS.run")
- fn := func() {
- var urls []string
- for {
- sh, sz, err := getInstrument()
- if err != nil {
- time.Sleep(time.Second)
- continue
- }
- urls = getfetchurls(sh, sz)
- break
- }
- lastMarket = make(map[string]string)
- datach := make(chan []string, 20)
- // 登录产生新的Session
- for i := 0; i < len(urls); i++ {
- go func(n int) {
- for {
- fetchurl(IntSina, urls[n], datach)
- time.Sleep(time.Millisecond * 200)
- }
- }(i)
- }
- for {
- item := <-datach
- newdata := getnewdata(IntSina, item)
- for i := 0; i < len(newdata); i++ {
- lds.Save(newdata[i])
- }
- }
- }
- fn()
- }
- func getfetchurls(sh []string, sz []string) []string {
- baseurl := "http://hq.sinajs.cn/list="
- for i := 0; i < len(sh); i++ {
- sh[i] = "sh" + sh[i]
- }
- for i := 0; i < len(sz); i++ {
- sz[i] = "sz" + sz[i]
- }
- sh = append(sh, sz...)
- var ret []string
- for i := 0; i < len(sh); i = i + 200 {
- end := 200
- if i+200 > len(sh) {
- end = len(sh) - i
- }
- data := sh[i : i+end]
- query := strings.Join(data, ",")
- ret = append(ret, baseurl+query)
- }
- return ret
- }
- func fetchurl(typ int, url string, ch chan []string) {
- if typ == IntSinaFuture {
- //log.Println("sinafuture fetchurl begin")
- }
- resp, err := http.Get(url)
- if err != nil {
- ch <- nil
- log.Println("fetchurl get", err)
- return
- }
- defer resp.Body.Close()
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- ch <- nil
- log.Println("fetchurl readall", err)
- return
- }
- tickdatas := parsetick(string(data))
- ch <- tickdatas
- if typ == IntSinaFuture {
- //log.Println("sinafuture fetchurl end")
- }
- }
- func parsetick(data string) []string {
- return strings.Split(data, "\n")
- }
- func getnewdata(typ int, datas []string) []*Market {
- var ret []*Market
- for i := 0; i < len(datas); i++ {
- datas[i] = strings.Replace(datas[i], "var hq_str_", "", -1)
- marketdata := strings.SplitN(datas[i], "=", 2)
- if len(marketdata) != 2 {
- continue
- }
- if lastdata, ok := lastMarket[marketdata[0]]; ok {
- if lastdata == marketdata[1] {
- if typ == IntSinaFuture {
- //log.Println("sinafuture same")
- }
- continue
- }
- }
- lastMarket[marketdata[0]] = marketdata[1]
- var market *Market
- var err error
- if typ == IntSina {
- market, err = parsemarket(marketdata[0], marketdata[1])
- } else {
- market, err = parsemarketfuture(marketdata[0], marketdata[1])
- }
- if err != nil {
- log.Println(datas[i], err)
- continue
- }
- ret = append(ret, market)
- }
- return ret
- }
- func parsemarket(name string, data string) (*Market, error) {
- data = strings.Trim(data, "\";\r\n")
- datas := strings.Split(data, ",")
- if len(datas) < 32 {
- return nil, errors.New("makret data error")
- }
- var market Market
- var err error
- market.Open, err = strconv.ParseFloat(datas[1], 64)
- if err != nil {
- return nil, err
- }
- market.LastPrice, err = strconv.ParseFloat(datas[2], 64)
- if err != nil {
- return nil, err
- }
- market.Close, err = strconv.ParseFloat(datas[3], 64)
- if err != nil {
- return nil, err
- }
- market.High, err = strconv.ParseFloat(datas[4], 64)
- if err != nil {
- return nil, err
- }
- market.Low, err = strconv.ParseFloat(datas[5], 64)
- if err != nil {
- return nil, err
- }
- market.AllVolume, err = strconv.ParseFloat(datas[8], 64)
- if err != nil {
- return nil, err
- }
- market.AllAmount, err = strconv.ParseFloat(datas[9], 64)
- if err != nil {
- return nil, err
- }
- bids := make([]PP, 5)
- bids[0][1], err = strconv.ParseFloat(datas[10], 64)
- if err != nil {
- return nil, err
- }
- bids[0][0], err = strconv.ParseFloat(datas[11], 64)
- if err != nil {
- return nil, err
- }
- bids[1][1], err = strconv.ParseFloat(datas[12], 64)
- if err != nil {
- return nil, err
- }
- bids[1][0], err = strconv.ParseFloat(datas[13], 64)
- if err != nil {
- return nil, err
- }
- bids[2][1], err = strconv.ParseFloat(datas[14], 64)
- if err != nil {
- return nil, err
- }
- bids[2][0], err = strconv.ParseFloat(datas[15], 64)
- if err != nil {
- return nil, err
- }
- bids[3][1], err = strconv.ParseFloat(datas[16], 64)
- if err != nil {
- return nil, err
- }
- bids[3][0], err = strconv.ParseFloat(datas[17], 64)
- if err != nil {
- return nil, err
- }
- bids[4][1], err = strconv.ParseFloat(datas[18], 64)
- if err != nil {
- return nil, err
- }
- bids[4][0], err = strconv.ParseFloat(datas[19], 64)
- if err != nil {
- return nil, err
- }
- market.Bids = bids
- asks := make([]PP, 5)
- asks[0][1], err = strconv.ParseFloat(datas[20], 64)
- if err != nil {
- return nil, err
- }
- asks[0][0], err = strconv.ParseFloat(datas[21], 64)
- if err != nil {
- return nil, err
- }
- asks[1][1], err = strconv.ParseFloat(datas[22], 64)
- if err != nil {
- return nil, err
- }
- asks[1][0], err = strconv.ParseFloat(datas[23], 64)
- if err != nil {
- return nil, err
- }
- asks[2][1], err = strconv.ParseFloat(datas[24], 64)
- if err != nil {
- return nil, err
- }
- asks[2][0], err = strconv.ParseFloat(datas[25], 64)
- if err != nil {
- return nil, err
- }
- asks[3][1], err = strconv.ParseFloat(datas[26], 64)
- if err != nil {
- return nil, err
- }
- asks[3][0], err = strconv.ParseFloat(datas[27], 64)
- if err != nil {
- return nil, err
- }
- asks[4][1], err = strconv.ParseFloat(datas[28], 64)
- if err != nil {
- return nil, err
- }
- asks[4][0], err = strconv.ParseFloat(datas[29], 64)
- if err != nil {
- return nil, err
- }
- market.Asks = asks
- t, err := time.Parse(time.RFC3339, datas[30]+"T"+datas[31]+"+08:00")
- if err != nil {
- return nil, err
- }
- market.Timestamp = t.Unix() * 1000
- name = name[2:]
- market.InsId, err = strconv.ParseInt(name, 10, 64)
- if err != nil {
- return nil, err
- }
- market.Type = IntSina
- return &market, nil
- }
|