前些日子,收到一个做网约车的朋友的需求,他手下目前有几百号司机和车子。每隔1个月需要去交警队官方网站查询车和人的信息:
车:是否预约出租客运
人:是否有从业资格
流程大致如下:
1. 内部系统调用本服务,传入车和人的信息
2. 本服务去交管局网站查询
难点在于查询网站有个验证码,当然这也是他请我帮忙做的原因吧,谈好价格后,开干!
大致解决方案:
1. 使用 golang 搭建一个web站点
2. 接受参数后,去交管局网站查询
3. 模拟访问查询网站,下载验证码和cookie
4. 破解验证码
5. 模拟查询网站提交查询信息
6. 返回查询结果给调用者
验证码我使用的是百度AI文字识别方案,经测试,识别效果很好,但是有日使用次数限制。
部分源代码:
package openapi import ( "fmt" "strings" // "strconv" // "bytes" "os" // "io" "net/http" "net/url" // "time" "io/ioutil" "encoding/json" "encoding/base64" "errors" "github.com/astaxie/beego" ) type BaiduAI struct { ApiKey string SecretKey string } func (this *BaiduAI) setApi(){ this.ApiKey = "###" this.SecretKey = "###" } func (this *BaiduAI) GetToken() (string, error){ if len(this.ApiKey) == 0{ this.setApi() } //beego.AppConfig.Set("baidu_ai_access_token", this.SecretKey) str := beego.AppConfig.String("baidu_ai_access_token") if str != "0"{ return str , nil } urlstr := "https://aip.baidubce.com/oauth/2.0/token" v := url.Values{} v.Add("client_id", this.ApiKey) v.Add("client_secret", this.SecretKey) v.Add("grant_type", "client_credentials") param := v.Encode() reqest, _ := http.NewRequest("POST", urlstr, strings.NewReader(param)) reqest.Header.Add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); client := &http.Client{} response, err := client.Do(reqest) if(err != nil){ defer response.Body.Close() return "", err } body, _ := ioutil.ReadAll(response.Body) content := string(body) fmt.Println("BAIDU API TOKEN :" , content) m := map[string]interface{}{} errx := json.Unmarshal([]byte(content), &m) if errx != nil { return "", errx } token := m["access_token"].(string) beego.AppConfig.Set("baidu_ai_access_token", token) return token, nil } func (this *BaiduAI) TryGetBase64Captcha(path string) (string, error){ // File file, err := os.Open(path) if err != nil { fmt.Println("打开图片出错", err) fmt.Println(err) os.Exit(-1) } defer file.Close() // FileInfo stats, err := file.Stat() if err != nil { return "", err } // []byte data := make([]byte, stats.Size()) _, err = file.Read(data) if err != nil { return "", err } base64str := base64.StdEncoding.EncodeToString(data) fmt.Println("data:image/jpg;base64,", base64str) return base64str, nil } // 尝试解开图形验证码 func (this *BaiduAI) TryGetCaptchaValue(path string) (string, error){ base64, err := this.TryGetBase64Captcha(path) if err != nil{ return "", err } access_token, err := this.GetToken() if err != nil{ return "", err } // doc page : https://ai.baidu.com/ai-doc/OCR/Ok3h7y1vo urlstr := "https://aip.baidubce.com/rest/2.0/ocr/v1/numbers?access_token=" + access_token v := url.Values{} v.Add("recognize_granularity", "big") v.Add("detect_direction", "false") v.Add("image", base64) param := v.Encode() reqest, _ := http.NewRequest("POST", urlstr, strings.NewReader(param)) reqest.Header.Add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); client := &http.Client{} response, err := client.Do(reqest) if(err != nil){ defer response.Body.Close() return "", err } body, _ := ioutil.ReadAll(response.Body) content := string(body) fmt.Println("百度AI 图片识别结果 :" , content) return this.readJson(content) } func (this *BaiduAI) readJson(jsonstr string) (string, error){ map_result := map[string]interface{}{} errx := json.Unmarshal([]byte(jsonstr), &map_result) if errx != nil { return "", errx } words_result_num, ok := map_result["words_result_num"] if !ok { return jsonstr, nil } int_nm := words_result_num.(float64) if int_nm == 0 { return "", errors.New("未能识出此验证码,请重试") } words_result := map_result["words_result"] arr_result := words_result.([]interface{}) fmt.Println(arr_result[0]); map_end := arr_result[0].(map[string]interface {}) fmt.Println("2222222222222222222222222222222222222"); fmt.Println(map_end["words"].(string)); words := map_end["words"].(string) return words, nil }