关键字:golang、股票、ohlc、指标
时间:2018年9月

// Copyright 2018 Wu Peng. All rights reserved.
//
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
)

func main() {
	downloadSymbols("symbols.csv")

	symbol := "sz002027"
	downloadOHLC(symbol, "ohlc-"symbol+".csv")

	symbol = "sh600585"
	downloadIndicators(symbol, "indicators-"+symbol+".json")

	symbol = "sh600660"
	downloadBonus(symbol, "bonus-"+symbol+".json")
}

// downloadSymbols() save symbols to a csv file.
//
// Format as follows:
// symbol
// --------
// sh600000
// sh600001
// sh600002
// ...
// --------
func downloadSymbols(filename string) {
	resp, err := http.Get("http://quote.eastmoney.com/stocklist.html")
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	content := ""
	reg := regexp.MustCompile("s[hz][60]0\\d{4,4}")
	for _, s := range reg.FindAllString(string(body), -1) {
		content += s + "\n"
	}
	err = ioutil.WriteFile(filename, []byte(content), 0666)
}

// downloadOHLC() save a stock's OHLC data to a file.
//
// Format as follows:
// date   open  close high  low   volume
// -------------------------------------
// 180102 14.11 14.08 14.51 13.98 550796
// 180103 14.15 13.79 14.29 13.73 727753
// 180104 13.85 13.66 13.86 13.51 729298
// 180105 13.65 13.79 14.10 13.52 573245
// 180108 13.66 13.51 13.98 13.30 697819
// ...
// -------------------------------------
func downloadOHLC(symbol string, filename string) error {
	url := fmt.Sprintf("http://data.gtimg.cn/flashdata/hushen/daily/18/%s.js", symbol)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("http.Get():", err)
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == 404 {
		fmt.Println("http.Get():", "404 page not find.")
		return errors.New("HTTP 404.")
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("ioutil.ReadAll()", err)
		return err
	}
	str := string(body)
	str = strings.Replace(str, "\\n\\", "", -1)
	str = strings.Replace(str, "daily_data_18=\"\n", "", -1)
	str = strings.Replace(str, "\";", "", -1)
	err = ioutil.WriteFile(filename, []byte(str), 0666)
	if err != nil {
		fmt.Println("ioutil.WriteFile()", err)
	}
	return nil
}

// downloadIndicators() save a stock's indicators to a file.
//
// Format as follows:
// ----------------------------------
// [{
//   "date"       :"报告期",
//   "jbmgsy"     :"基本每股收益(元)",
//   "kfmgsy"     :"扣非每股收益(元)",
//   "xsmgsy"     :"稀释每股收益(元)",
//   "mgjzc"      :"每股净资产(元)",
//   "mggjj"      :"每股公积金(元)",
//   "mgwfply"    :"每股未分配利润(元)",
//   "mgjyxjl"    :"每股经营现金流(元)",
//   "yyzsr"      :"营业总收入(元)",
//   "mlr"        :"毛利润(元)",
//   "gsjlr"      :"归属净利润(元)",
//   "kfjlr"      :"扣非净利润(元)",
//   "yyzsrtbzz"  :"营业总收入同比增长(%)",
//   "gsjlrtbzz"  :"归属净利润同比增长(%)",
//   "kfjlrtbzz"  :"扣非净利润同比增长(%)",
//   "yyzsrgdhbzz":"营业总收入滚动环比增长(%)",
//   "gsjlrgdhbzz":"归属净利润滚动环比增长(%)",
//   "kfjlrgdhbzz":"扣非净利润滚动环比增长(%)",
//   "jqjzcsyl"   :"加权净资产收益率(%)",
//   "tbjzcsyl"   :"摊薄净资产收益率(%)",
//   "tbzzcsyl"   :"摊薄总资产收益率(%)",
//   "mll"        :"毛利率(%)",
//   "jll"        :"净利率(%)",
//   "sjsl"       :"实际税率(%)",
//   "yskyysr"    :"预收款/营业收入",
//   "xsxjlyysr"  :"销售现金流/营业收入",
//   "jyxjlyysr"  :"经营现金流/营业收入",
//   "zzczzy"     :"总资产周转率(次)",
//   "yszkzzts"   :"应收账款周转天数(天)",
//   "chzzts"     :"存货周转天数(天)",
//   "zcfzl"      :"资产负债率(%)",
//   "ldzczfz"    :"流动负债/总负债(%)",
//   "ldbl"       :"流动比率",
//   "sdbl"       :"速动比率"
// },{
//   ...
// }]
// -------------------------------------
func downloadIndicators(symbol string, filename string) error {
	url := fmt.Sprintf("http://emweb.securities.eastmoney.com/NewFinanceAnalysis/MainTargetAjax?ctype=4&type=0&code=%s", symbol)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("http.Get():", err)
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == 404 {
		fmt.Println("http.Get():", "404 page not find.")
		return errors.New("HTTP 404.")
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("ioutil.ReadAll()", err)
		return err
	}
	err = ioutil.WriteFile(filename, body, 0666)
	if err != nil {
		fmt.Println("ioutil.WriteFile()", err)
		return err
	}
	return nil
}

// downloadBonus() save a stock's bonus to a file.
func downloadBonus(symbol string, filename string) error {
	url := fmt.Sprintf("http://emweb.securities.eastmoney.com/PC_HSF10/BonusFinancing/BonusFinancingAjax?code=%s", symbol)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("http.Get():", err)
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == 404 {
		fmt.Println("http.Get():", "404 page not find.")
		return errors.New("HTTP 404.")
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("ioutil.ReadAll()", err)
		return err
	}
	err = ioutil.WriteFile(filename, body, 0666)
	if err != nil {
		fmt.Println("ioutil.WriteFile()", err)
		return err
	}
	return nil
}

关键字:golang、百度、图像识别、ocr、api
时间:2018年9月

开通API

通过ai.baidu.com或cloud.baidu.com进行登录,进入“管理控制台”后,在“已开通服务”板块下的“文字识别”中申请开通API。

API使用流程

1、调用获取access token的接口,需提供客户API Key和Secret Key;
2、调用具体api,需提供access token;
3、access token可以反复使用,但有一定的有效期,失效后需要重新获取。

Demo代码

// Copyright 2018 Wu Peng. All rights reserved.

// Example of how to use baidu OCR APIs.
package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type Session struct {
	AccessToken   string `json:"access_token"`
	RefreshToken  string `json:"refresh_token"`
	SessionKey    string `json:"session_key"`
	SessionSecret string `json:"session_secret"`
	Scope         string `json:"scope"`
	ExpiresIn     int    `json:"expires_in"`
}

type Result struct {
	LogId     int64       `json:"log_id"`
	Direction int         `json:"direction"`
	Wrn       int         `json:"words_result_num"`
	Wr        WordsResult `json:"words_result"`
}

type WordsResult struct {
	Model        Words `json:"品牌型号"`
	IssueDate    Words `json:"发证日期"`
	UseCharacter Words `json:"使用性质"`
	EngineNo     Words `json:"发动机号码"`
	PlateNo      Words `json:"号牌号码"`
	Owner        Words `json:"所有人"`
	Address      Words `json:"住址"`
	RegisterDate Words `json:"注册日期"`
	Vin          Words `json:"车辆识别代号"`
	VehicleType  Words `json:"车辆类型"`
}

type Words struct {
	Words string `json:"words"`
}

type VehicleLicense struct {
	PlateNo      string
	VehicleType  string
	Owner        string
	Address      string
	UseCharacter string
	Model        string
	Vin          string
	EngineNo     string
	RegisterDate string
	IssueDate    string
}

const (
	clientID     = "LQeYIP5a*********RjGP9W7"          // API Key
	clientSecret = "laLiGBQuEbYu******AZ55urYRAYOFy9"  // Secret Key
)

func main() {
	token, err := accessToken(clientID, clientSecret)
	if err != nil {
		fmt.Println("accessToken()", err)
		return
	}
	fmt.Println("Token:", *token)

	img, err := ioutil.ReadFile("1.JPG")
	if err != nil {
		fmt.Println("ioutil.ReadFile()", err)
		return
	}
	var vl VehicleLicense
	err = ImageToText(*token, img, &vl)
	if err != nil {
		fmt.Println("ImageToText()", err)
		return
	}
	fmt.Println("PlateNo: ", vl.PlateNo)
	fmt.Println("VIN: ", vl.Vin)

}

func accessToken(id string, secret string) (token *string, err error) {
	apiURL := fmt.Sprintf("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s", id, secret)
	resp, err := http.Get(apiURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	var session Session
	err = json.Unmarshal(body, &session)
	if err != nil {
		return nil, err
	}
	token = &session.AccessToken
	return token, nil
}

func ImageToText(token string, image []byte, vl *VehicleLicense) error {
	apiURL := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/ocr/v1/vehicle_license?access_token=%s", token)
	param := "image=" + url.QueryEscape(base64.StdEncoding.EncodeToString(image))
	resp, err := http.Post(apiURL, "application/x-www-form-urlencoded", strings.NewReader(param))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var result Result
	err = json.Unmarshal(body, &result)
	if err != nil {
		return err
	}
	convert(result.Wr, vl)
	return nil
}

func convert(wr WordsResult, vl *VehicleLicense) {
	vl.PlateNo = wr.PlateNo.Words
	vl.VehicleType = wr.VehicleType.Words
	vl.Owner = wr.Owner.Words
	vl.Address = wr.Address.Words
	vl.UseCharacter = wr.UseCharacter.Words
	vl.Model = wr.Model.Words
	vl.Vin = wr.Vin.Words
	vl.EngineNo = wr.EngineNo.Words
	vl.RegisterDate = wr.RegisterDate.Words
	vl.IssueDate = wr.IssueDate.Words
	return
}