├── README.md └── bjeea-bulk-query.go /README.md: -------------------------------------------------------------------------------- 1 | # 北京高考录取结果批量查询工具 2 | 3 | 本程序可以通过北京教育考试院网站批量查询考生成绩,只需要知道准考证号和考生号。 4 | 5 | 6 | ## 查询单个考生录取结果 7 | 8 | ```bash 9 | bjeea-bulk-query -i 123456789:12345678901234 10 | ``` 11 | 12 | 成功返回结果样例 13 | 14 | ``` 15 | 姓名: 张三 16 | 准考证号: 123456789 17 | 考生号: 12345678901234 18 | 本科一批: 北京工业大学 (1049) 19 | 专业: 软件工程(实验班) (67) 20 | ``` 21 | 22 | 失败返回结果样例 (考生号错误、尚未录取、过期查询或者考试 ID 不匹配) 23 | 24 | ``` 25 | 查询失败,请检查准考证号和考生号 26 | 准考证号: 111111111 27 | 考生号: 22222222222222 28 | ``` 29 | 30 | ## 批量查询 31 | 32 | ```bash 33 | bjeea-bulk-query file.csv 34 | ``` 35 | 36 | 本程序会多线程的查询成绩,输出结果顺序可能会有所改变 37 | 38 | ### `file.csv` 样例 39 | 40 | 请按照准考证号、考生号创建文件。不符合规则的行会自动跳过。 41 | 42 | ```CSV 43 | 123456789,12345678901234 44 | 987654321,43210987654321 45 | 111111111,22222222222222 46 | ``` 47 | 48 | 返回结果样例 49 | 50 | ``` 51 | 姓名: 张三 52 | 准考证号: 123456789 53 | 考生号: 12345678901234 54 | 本科一批: 北京工业大学 (1049) 55 | 专业: 软件工程(实验班) (67) 56 | 57 | ------ 58 | 59 | 姓名: 李四 60 | 准考证号: 987654321 61 | 考生号: 43210987654321 62 | 本科一批: 清华大学 (1023) 63 | 专业: 理科试验班类(数理) (10) 64 | 65 | ------ 66 | 67 | 查询失败,请检查准考证号和考生号 68 | 准考证号: 111111111 69 | 考生号: 22222222222222 70 | ``` 71 | 72 | 73 | ## 批量查询 (以 CSV 格式返回) 74 | 75 | ```bash 76 | bjeea-bulk-query file.csv --csv 77 | ``` 78 | 79 | 加入 `--csv` 参数后可以返回 CSV 格式, 成功返回结果样例 80 | 81 | ``` 82 | 姓名,准考证号,考生号,大学类型,大学名称,大学代码,专业名称,专业代码,查询状态 83 | 张三,12345678,12345678901234,本科一批,北京工业大学,1049,软件工程(实验班),67,成功 84 | 李四,87654321,43210987654321,本科一批,清华大学,1023,理科试验班类(数理),10,成功 85 | ,111111111,22222222222222,,,0,,0,失败 86 | ``` 87 | 88 | ## 免责声明 89 | 90 | 仅限于于学术交流,请于下载24小时后删除。产生的一切后果与本人无关,一切责任由您自己承担。 91 | -------------------------------------------------------------------------------- /bjeea-bulk-query.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "gopkg.in/alecthomas/kingpin.v2" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "os" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | type Examinee struct { 16 | name string 17 | examNo int 18 | examineeNo int64 19 | university string 20 | universityNo int 21 | major string 22 | majorNo int 23 | universityType string 24 | failed bool 25 | } 26 | 27 | var ( 28 | inputExaminee = kingpin.Flag("examinee", "考生信息, 格式 [准考证号,考生号]").Short('i').String() 29 | inputId = kingpin.Flag("examid", "考试 ID (表单中的 examID, 默认为 2018 年北京市高招录取结果)").Default("4865").Short('t').Int() 30 | filename = kingpin.Arg("file.csv", "准考证号与考生号的 CSV 文件").String() 31 | outCSV = kingpin.Flag("csv", "以 CSV 格式输出").Bool() 32 | ) 33 | 34 | func main() { 35 | kingpin.Parse() 36 | kingpin.CommandLine.HelpFlag.Short('h') 37 | 38 | if *inputExaminee == "" { 39 | if *filename == "" || !strings.HasSuffix(*filename, ".csv") { 40 | kingpin.FatalUsage("参数错误, 请提供文件名或考生信息, 文件必须是 .csv 后缀\n") 41 | } 42 | if rawBytes, err := ioutil.ReadFile(os.Args[1]); err != nil { 43 | log.Fatal(err) 44 | } else { 45 | examinees := parseExaminees(string(rawBytes)) 46 | if *outCSV { 47 | fmt.Println("姓名,准考证号,考生号,大学类型,大学名称,大学代码,专业名称,专业代码,查询状态") 48 | for _, examinee := range getExamineesDetail(examinees) { 49 | fmt.Println(examinee.List()) 50 | } 51 | } else { 52 | for i, examinee := range getExamineesDetail(examinees) { 53 | fmt.Println(examinee) 54 | if i != len(examinees)-1 { 55 | fmt.Print("------\n\n") 56 | } 57 | } 58 | } 59 | 60 | } 61 | } else if inputExamineeSlice := strings.Split(*inputExaminee, ","); len(inputExamineeSlice) == 2 { 62 | var examinee Examinee 63 | examinee.examNo, _ = strconv.Atoi(strings.TrimSpace(inputExamineeSlice[0])) 64 | examinee.examineeNo, _ = strconv.ParseInt(strings.TrimSpace(inputExamineeSlice[1]), 10, 64) 65 | fmt.Print(getExamineeDetail(examinee)) 66 | } else { 67 | kingpin.FatalUsage("参数错误, 请提供文件名或考生信息. 准考证号应为 9 位, 考生号应为 14 位\n") 68 | } 69 | } 70 | 71 | func parseExaminees(data string) (Examinees []Examinee) { 72 | var ( 73 | Examinee Examinee 74 | err error 75 | ) 76 | for _, line := range strings.Split(data, "\n") { 77 | lineSlice := strings.Split(line, ",") 78 | if line == "" || len(lineSlice) < 2 { 79 | continue 80 | } else if examNo, err := strconv.Atoi(strings.TrimSpace(lineSlice[0])); err == nil { 81 | Examinee.examNo = examNo 82 | if examineeNo, err := strconv.ParseInt(strings.TrimSpace(lineSlice[1]), 10, 64); err == nil { 83 | Examinee.examineeNo = examineeNo 84 | Examinees = append(Examinees, Examinee) 85 | } 86 | } 87 | if err != nil { 88 | fmt.Println(err) 89 | continue 90 | } 91 | } 92 | return Examinees 93 | } 94 | 95 | func getExamineesDetail(examinees []Examinee) (newExaminees []Examinee) { 96 | examineesChannel := make(chan Examinee) 97 | for _, examinee := range examinees { 98 | go func(examinee Examinee) { 99 | examineeDetail := getExamineeDetail(examinee) 100 | if examineeDetail.name == "" { 101 | examinee.failed = true 102 | } 103 | examineesChannel <- examineeDetail 104 | }(examinee) 105 | } 106 | 107 | for len(newExaminees) < len(examinees) { 108 | examinee := <-examineesChannel 109 | newExaminees = append(newExaminees, examinee) 110 | } 111 | 112 | return newExaminees 113 | } 114 | 115 | func getExamineeDetail(examinee Examinee) Examinee { 116 | var urlQuery string 117 | urlQuery = "examNo=" + fmt.Sprintf("%09d", examinee.examNo) + "&examinneNo=" + fmt.Sprintf("%014d", examinee.examineeNo) + "&examId=" + fmt.Sprint(*inputId) 118 | resp, err := http.Post("http://query.bjeea.cn/queryService/rest/admission/110", 119 | "application/x-www-form-urlencoded; charset=utf-8", 120 | strings.NewReader(urlQuery)) 121 | if err != nil { 122 | log.Fatal(err) 123 | } 124 | 125 | body, err := ioutil.ReadAll(resp.Body) 126 | if err != nil { 127 | log.Fatal(err) 128 | } 129 | 130 | jsonMap := make(map[string]interface{}) 131 | err = json.Unmarshal(body, &jsonMap) 132 | if err != nil { 133 | panic(err) 134 | } 135 | if jsonMap["enrollList"] != nil { 136 | jsonMap, _ = jsonMap["enrollList"].([]interface{})[0].(map[string]interface{}) 137 | examinee.university, _ = jsonMap["GRADE11"].(string) 138 | examinee.universityNo, _ = strconv.Atoi(jsonMap["GRADE10"].(string)) 139 | examinee.major, _ = jsonMap["GRADE13"].(string) 140 | examinee.majorNo, _ = strconv.Atoi(jsonMap["GRADE12"].(string)) 141 | examinee.name, _ = jsonMap["NAME"].(string) 142 | examinee.universityType = jsonMap["GRADE8"].(string) 143 | } else { 144 | examinee.failed = true 145 | } 146 | 147 | return examinee 148 | } 149 | 150 | func (examinee Examinee) String() (formatted string) { 151 | if examinee.failed { 152 | formatted = "查询失败, 请检查准考证号和考生号\n" 153 | } 154 | if examinee.name != "" { 155 | formatted = "姓名: " + examinee.name + "\n" 156 | } 157 | if examinee.examNo > 0 { 158 | formatted += "准考证号: " + fmt.Sprintf("%09d", examinee.examNo) + "\n" 159 | } 160 | if examinee.examineeNo > 0 { 161 | formatted += "考生号: " + fmt.Sprintf("%014d", examinee.examineeNo) + "\n" 162 | } 163 | if examinee.universityNo > 0 && examinee.universityType != "" && examinee.university != "" { 164 | formatted += examinee.universityType + ": " + examinee.university + " (" + fmt.Sprint(examinee.universityNo) + ")\n" 165 | } 166 | if examinee.majorNo > 0 && examinee.major != "" { 167 | formatted += "专业: " + examinee.major + " (" + fmt.Sprint(examinee.majorNo) + ")\n" 168 | } 169 | return formatted 170 | } 171 | 172 | func (examinee Examinee) List() (formatted string) { 173 | formatted = examinee.name 174 | formatted += "," + fmt.Sprintf("%09d", examinee.examNo) 175 | formatted += "," + fmt.Sprintf("%014d", examinee.examineeNo) 176 | formatted += "," + examinee.universityType + "," + examinee.university + "," + fmt.Sprint(examinee.universityNo) 177 | formatted += "," + examinee.major + "," + fmt.Sprint(examinee.majorNo) 178 | if examinee.failed { 179 | formatted += ",失败" 180 | } else { 181 | formatted += ",成功" 182 | } 183 | return formatted 184 | } 185 | --------------------------------------------------------------------------------