前些日子,收到一个做网约车的朋友的需求,他手下目前有几百号司机和车子。每隔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
}