main.go 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "flag"
  6. "context"
  7. "encoding/base64"
  8. "encoding/json"
  9. "fmt"
  10. "github.com/tidwall/gjson"
  11. "io"
  12. "io/ioutil"
  13. "unicode"
  14. //"log"
  15. //"log"
  16. "math/rand"
  17. "net/http"
  18. "os"
  19. "strconv"
  20. "strings"
  21. "time"
  22. "syscall"
  23. "os/signal"
  24. "path/filepath"
  25. "github.com/spf13/viper"
  26. //"github.com/chromedp/cdproto/dom"
  27. "github.com/xuri/excelize/v2"
  28. "github.com/chromedp/cdproto/network"
  29. "github.com/chromedp/cdproto/cdp"
  30. "github.com/chromedp/chromedp"
  31. )
  32. var (
  33. configFile = flag.String("config", "./common.yaml", "config file location")
  34. version = flag.Bool("version", false, "print the version")
  35. GitCommit = "library-import"
  36. Version = "library-import"
  37. )
  38. type Configure struct {
  39. // 处理视频
  40. HandleVideo bool
  41. // 处理简单题
  42. HandleAnswer bool
  43. // 处理网页
  44. HandlePage bool
  45. // 处理材料
  46. HandleMaterial bool
  47. // 处理不计分题
  48. HandelNoPoint bool
  49. // 处理间隔
  50. HandleInterval int
  51. // 跳过已处理
  52. SkipProcessed bool
  53. //VisitCount int
  54. // 提交休眠时间
  55. CommitExamTime int
  56. //SleepTime int
  57. // 是否手动处理
  58. CodeManual bool
  59. // 超时时间
  60. Timeout int
  61. // 图片验证token
  62. VerifyToken string
  63. VerifyType string
  64. // 无答案数
  65. QuestionCount int
  66. // 最大随机出错数
  67. MaxRandomQuestion int
  68. // 最小分
  69. MinScore int
  70. // 只爬取问答题
  71. OnlyCrawlerAnswer bool
  72. }
  73. var Conf *Configure
  74. var v *viper.Viper
  75. // LoadConfig 装载配置文件
  76. func LoadConfig(filename string) error {
  77. configPath, configName := filepath.Split(filename)
  78. fileList := strings.Split(configName, ".")
  79. if len(fileList) < 2 {
  80. return fmt.Errorf("%s", "文件格式不正确")
  81. }
  82. configName = fileList[0]
  83. fileExt := fileList[1]
  84. var err error
  85. if fileExt == "json" {
  86. err = LoadConfigFromJson(configName, configPath)
  87. } else if fileExt == "yaml" || fileExt == "yml" {
  88. err = LoadConfigFromYaml(configName, configPath)
  89. } else {
  90. err = fmt.Errorf("%s", "不支持的文件格式")
  91. }
  92. // 出错直接返回
  93. if err != nil {
  94. return err
  95. }
  96. return nil
  97. }
  98. // LoadConfigFromYaml 装载yaml类型的配置文件
  99. func LoadConfigFromYaml(configName, configPath string) error {
  100. v = viper.New()
  101. v.SetConfigName(configName)
  102. v.AddConfigPath(configPath)
  103. //设置配置文件类型
  104. v.SetConfigType("yaml")
  105. if err := v.ReadInConfig(); err != nil {
  106. return err
  107. }
  108. return parseConfig()
  109. }
  110. // LoadConfigFromJson 装载json类型的配置文件
  111. func LoadConfigFromJson(configName, configPath string) error {
  112. v = viper.New()
  113. v.SetConfigName(configName)
  114. v.AddConfigPath(configPath)
  115. //设置配置文件类型
  116. v.SetConfigType("json")
  117. if err := v.ReadInConfig(); err != nil {
  118. return err
  119. }
  120. return parseConfig()
  121. }
  122. func parseConfig() error {
  123. Conf = &Configure{}
  124. if err := v.Unmarshal(Conf); err != nil {
  125. return err
  126. }
  127. return nil
  128. }
  129. func commonVerify(image string) string {
  130. config := map[string]interface{}{}
  131. config["image"] = image
  132. if Conf.VerifyType == ""{
  133. config["type"] = "88888"
  134. }else{
  135. config["type"] = Conf.VerifyType
  136. }
  137. config["token"] = Conf.VerifyToken
  138. //config["token"] = "2EEYFMjDVpMTky-Z_BCgt_I14g4qq_D63S3NsQesfMc"
  139. configData, _ := json.Marshal(config)
  140. body := bytes.NewBuffer([]byte(configData))
  141. resp, err := http.Post("http://api.jfbym.com/api/YmServer/customApi", "application/json;charset=utf-8", body)
  142. defer resp.Body.Close()
  143. if err != nil {
  144. return ""
  145. }
  146. data, _ := ioutil.ReadAll(resp.Body)
  147. //fmt.Println(string(data), err)
  148. return string(data)
  149. }
  150. func codeGetNew(data []byte, account string) string {
  151. name := account + ".png"
  152. ioutil.WriteFile(name, data, 0666)
  153. defer func() {
  154. os.Remove(name)
  155. }()
  156. file, _ := os.Open(name)
  157. defer file.Close()
  158. fileInfo, _ := file.Stat()
  159. imageSize := fileInfo.Size()
  160. imageData := make([]byte, imageSize)
  161. _, err := file.Read(imageData)
  162. if err != nil {
  163. fmt.Println("保存文件失败")
  164. return ""
  165. }
  166. base64Data := base64.StdEncoding.EncodeToString(imageData)
  167. fmt.Println("开始识别验证码")
  168. respData := commonVerify(base64Data)
  169. //fmt.Println("验证码识别结果:", respData)
  170. code := gjson.Get(respData, "code").Int()
  171. if code != 10000 {
  172. fmt.Println("验证码识别失败:", gjson.Get(respData, "msg").String())
  173. return ""
  174. }
  175. return gjson.Get(respData, "data.data").String()
  176. }
  177. type CourseModule struct {
  178. Name string
  179. ModuleId string
  180. }
  181. func getCourseModules(courceId string, cookie string, specialCourse bool) ([]CourseModule, error) {
  182. url := fmt.Sprintf("https://lms.ouchn.cn/api/courses/%s/modules", courceId)
  183. request, err := http.NewRequest("GET", url, nil)
  184. if err != nil {
  185. return nil, err
  186. }
  187. //fmt.Println("URL :",url)
  188. //fmt.Println("COOKIE :",cookie)
  189. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  190. request.Header.Set("Cookie", cookie)
  191. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
  192. //request.Header.Set("Host", "chengdu.ouchn.cn")
  193. client := http.Client{}
  194. resp, err := client.Do(request)
  195. if err != nil {
  196. return nil, err
  197. }
  198. defer resp.Body.Close()
  199. respBytes, err := ioutil.ReadAll(resp.Body)
  200. if err != nil {
  201. return nil, err
  202. }
  203. //fmt.Println("MODEL :",string(respBytes), err)
  204. ret := []CourseModule{}
  205. mos := gjson.GetBytes(respBytes, "modules").Array()
  206. for _, mo := range mos {
  207. /*if (len(mo.Get("syllabuses").Array())) == 0 && !specialCourse {
  208. continue
  209. }*/
  210. item := CourseModule{}
  211. item.ModuleId = fmt.Sprintf("%d", mo.Get("id").Int())
  212. item.Name = mo.Get("name").String()
  213. ret = append(ret, item)
  214. }
  215. return ret, nil
  216. }
  217. type Course struct {
  218. Name string
  219. Url string
  220. }
  221. func getCourceNew(ctx context.Context) []Course {
  222. // https://lms.ouchn.cn/user/courses
  223. courseList := []Course{}
  224. err := chromedp.Run(ctx,
  225. chromedp.Navigate("https://lms.ouchn.cn/user/courses"),
  226. //chromedp.Sleep(2*time.Second),
  227. )
  228. if err != nil{
  229. fmt.Println("进入学习网失败:",err)
  230. }
  231. cookie := getCookie(ctx)
  232. i := int64(1)
  233. totalPage, err := getCourceNewImpl(&courseList,cookie,i)
  234. if err != nil{
  235. fmt.Println("获取课程失败;",err,"page=",i)
  236. return courseList
  237. }
  238. //fmt.Println("totalPage:",totalPage)
  239. if i < totalPage{
  240. for i=2;i<=totalPage;i++{
  241. _,err = getCourceNewImpl(&courseList,cookie,i )
  242. if err != nil{
  243. fmt.Println("获取课程失败;",err,"page=",i)
  244. continue
  245. //return err
  246. }
  247. }
  248. }
  249. return courseList
  250. }
  251. func getCourceNewImpl(courses *[]Course,cookie string,page int64) (int64, error) {
  252. url := fmt.Sprintf(`https://lms.ouchn.cn/api/my-courses?conditions={"status":["ongoing"],"keyword":""}&fields=id,name,course_code,department(id,name),grade(id,name),klass(id,name),course_type,cover,small_cover,start_date,end_date,is_started,is_closed,academic_year_id,semester_id,credit,compulsory,second_name,display_name,created_user(id,name),org(is_enterprise_or_organization),org_id,public_scope,course_attributes(teaching_class_name,copy_status,tip,data),audit_status,audit_remark,can_withdraw_course,imported_from,allow_clone,is_instructor,is_team_teaching,academic_year(id,name),semester(id,name),instructors(id,name,email,avatar_small_url),is_master,is_child,has_synchronized,master_course(name)&page=%d&page_size=10`, page)
  253. //fmt.Println("获取课程 url:",url)
  254. request, err := http.NewRequest("GET", url, nil)
  255. if err != nil {
  256. return 0, err
  257. }
  258. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  259. request.Header.Set("Cookie", cookie)
  260. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
  261. //request.Header.Set("Host", "chengdu.ouchn.cn")
  262. client := http.Client{}
  263. resp, err := client.Do(request)
  264. if err != nil {
  265. return 0, err
  266. }
  267. if resp.StatusCode != http.StatusOK {
  268. return 0, fmt.Errorf("wrong status code: %d", resp.StatusCode)
  269. }
  270. defer resp.Body.Close()
  271. respBytes, err := ioutil.ReadAll(resp.Body)
  272. if err != nil {
  273. return 0, err
  274. }
  275. //fmt.Println("RESP:",string(respBytes))
  276. list := gjson.GetBytes(respBytes,"courses").Array()
  277. for _,course := range list{
  278. cou := Course{}
  279. cou.Name = course.Get("display_name").String()
  280. if cou.Name == ""{
  281. cou.Name = course.Get("name").String()
  282. }
  283. //fmt.Println("DIS NAME :",course.Get("display_name").String(),"NAME:",course.Get("name").String())
  284. //fmt.Println("course:",course.String())
  285. cou.Url = course.Get("id").String()
  286. if cou.Url != ""{
  287. cou.Url = fmt.Sprintf("https://lms.ouchn.cn/course/%s/ng",cou.Url)
  288. *courses = append(*courses,cou)
  289. }
  290. }
  291. total_page := gjson.GetBytes(respBytes,"pages").Int()
  292. return total_page, nil
  293. }
  294. func getCourse(courses *[]Course,cookie string,page int64) (int64, error) {
  295. url := fmt.Sprintf("https://menhu.pt.ouchn.cn/ouchnapp/wap/course/xskc-pc?page=%d", page)
  296. fmt.Println("获取课程 :",url)
  297. request, err := http.NewRequest("GET", url, nil)
  298. if err != nil {
  299. return 0, err
  300. }
  301. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  302. request.Header.Set("Cookie", cookie)
  303. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
  304. //request.Header.Set("Host", "chengdu.ouchn.cn")
  305. client := http.Client{}
  306. resp, err := client.Do(request)
  307. if err != nil {
  308. return 0, err
  309. }
  310. defer resp.Body.Close()
  311. respBytes, err := ioutil.ReadAll(resp.Body)
  312. if err != nil {
  313. return 0, err
  314. }
  315. //fmt.Println("RESP :",string(respBytes))
  316. code := gjson.GetBytes(respBytes, "e").Int()
  317. if code == 0 {
  318. data := gjson.GetBytes(respBytes, "d").String()
  319. list := gjson.Get(data,"list").Array()
  320. for _,course := range list{
  321. cou := Course{}
  322. cou.Name = course.Get("name").String()
  323. cou.Url = course.Get("url").String()
  324. *courses = append(*courses,cou)
  325. }
  326. total_page := gjson.Get(data,"total_page").Int()
  327. return total_page, nil
  328. }
  329. return 0, fmt.Errorf("code is not 0 ")
  330. }
  331. func getCourseId(src string) string {
  332. array := strings.Split(src, "/")
  333. if len(array) < 5 {
  334. return ""
  335. }
  336. return array[4]
  337. }
  338. func getCookie(ctx context.Context)string {
  339. // 执行登录操作
  340. var cookies []*network.Cookie
  341. err := chromedp.Run(ctx,
  342. chromedp.ActionFunc(func(ctx context.Context) error {
  343. // 获取页面的 cookie
  344. var err error
  345. cookies, err = network.GetCookies().Do(ctx)
  346. if err != nil {
  347. fmt.Println("获取cookie 失败:",err)
  348. return err
  349. }
  350. return nil
  351. }),
  352. )
  353. if err != nil{
  354. return ""
  355. }
  356. ret := ""
  357. for _, v := range cookies {
  358. item := fmt.Sprintf("%s=%s", v.Name, v.Value)
  359. if ret == "" {
  360. ret = item
  361. continue
  362. }
  363. ret = ret + ";"
  364. ret = ret + item
  365. }
  366. return ret
  367. }
  368. type ModuleSubInfo struct {
  369. Name string
  370. Id string
  371. Stype string
  372. SyllabusId int64
  373. Type int // 1 page,2 vedio
  374. PreUnCompelte bool
  375. }
  376. type ModuleInfo struct {
  377. TxtInfos []ModuleSubInfo
  378. ExamInfos []ModuleSubInfo
  379. VedioInfos []ModuleSubInfo
  380. }
  381. func GetModuleInfo(courseId string, moduleId string, cookie string) (*ModuleInfo, error) {
  382. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/%s"+
  383. "/all-activities?module_ids=[%s]"+
  384. "&activity_types=learning_activities,exams,classrooms,"+
  385. "live_records,rollcalls&no-loading-animation=true", courseId, moduleId)
  386. request, err := http.NewRequest("GET", url, nil)
  387. //fmt.Println("url:",url)
  388. if err != nil {
  389. return nil, err
  390. }
  391. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  392. request.Header.Set("Cookie", cookie)
  393. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  394. //request.Header.Set("Host", "chengdu.ouchn.cn")
  395. client := http.Client{Timeout: 60 * time.Second}
  396. resp, err := client.Do(request)
  397. if err != nil {
  398. return nil, err
  399. }
  400. defer resp.Body.Close()
  401. respBytes, err := ioutil.ReadAll(resp.Body)
  402. if err != nil {
  403. return nil, err
  404. }
  405. //fmt.Println("resp:",string(respBytes))
  406. ret := &ModuleInfo{}
  407. // 试题
  408. exams := []ModuleSubInfo{}
  409. examsArray := gjson.GetBytes(respBytes, "exams").Array()
  410. for _, v := range examsArray {
  411. item := ModuleSubInfo{}
  412. item.Name = v.Get("title").String()
  413. unikey := v.Get("unique_key").String()
  414. array := strings.Split(unikey, "-")
  415. if len(array) < 2 {
  416. continue
  417. }
  418. item.Id = array[1]
  419. exams = append(exams, item)
  420. }
  421. // 文档和视频
  422. texts := []ModuleSubInfo{}
  423. vedios := []ModuleSubInfo{}
  424. array := gjson.GetBytes(respBytes, "learning_activities").Array()
  425. for _, v := range array {
  426. stype := v.Get("type").String()
  427. syllabusId := v.Get("syllabus_id").Int()
  428. item := ModuleSubInfo{}
  429. item.Name = v.Get("title").String()
  430. item.SyllabusId = syllabusId
  431. item.Stype = stype
  432. unikey := v.Get("unique_key").String()
  433. array := strings.Split(unikey, "-")
  434. if len(array) < 2 {
  435. continue
  436. }
  437. prerequisitesArray := v.Get("prerequisites").Array()
  438. preUnCompletion := false
  439. if len(prerequisitesArray) == 0 || prerequisitesArray== nil{
  440. preUnCompletion = true
  441. }else{
  442. for _,pre:= range prerequisitesArray{
  443. isPreCompltetion := pre.Get("completion_criterion.completion_key").String()
  444. if isPreCompltetion == "not_completed"{
  445. preUnCompletion = true
  446. }
  447. }
  448. }
  449. item.PreUnCompelte = preUnCompletion
  450. item.Id = array[1]
  451. if stype == "page" {
  452. item.Type = 1
  453. texts = append(texts, item)
  454. vedios = append(vedios, item)
  455. } else if stype == "online_video" || (stype == "web_link" && strings.Contains(item.Name, "视频")) || stype == "material" {
  456. index := -1
  457. item.Type = 2
  458. for i := len(vedios) - 1; i >= 0; i-- {
  459. if item.SyllabusId == vedios[i].SyllabusId {
  460. index = i
  461. break
  462. }
  463. }
  464. if index == -1 || index == len(vedios)-1 {
  465. vedios = append(vedios, item)
  466. } else {
  467. tmp := []ModuleSubInfo{}
  468. tmp = append(tmp, vedios[:index+1]...)
  469. tmp = append(tmp, item)
  470. tmp = append(tmp, vedios[index+1:]...)
  471. vedios = tmp
  472. }
  473. }
  474. }
  475. ret.ExamInfos = exams
  476. ret.TxtInfos = texts
  477. ret.VedioInfos = vedios
  478. return ret, nil
  479. }
  480. func getUploadIds(moduleId string, cookie string) ([]int64, error) {
  481. url := fmt.Sprintf("https://lms.ouchn.cn/api/activities/%s", moduleId)
  482. request, err := http.NewRequest("GET", url, nil)
  483. if err != nil {
  484. return nil, err
  485. }
  486. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  487. request.Header.Set("Cookie", cookie)
  488. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  489. //request.Header.Set("Host", "chengdu.ouchn.cn")
  490. client := http.Client{}
  491. resp, err := client.Do(request)
  492. if err != nil {
  493. return nil, err
  494. }
  495. defer resp.Body.Close()
  496. respBytes, err := ioutil.ReadAll(resp.Body)
  497. if err != nil {
  498. return nil, err
  499. }
  500. ret := []int64{}
  501. uploads := gjson.GetBytes(respBytes, "uploads").Array()
  502. for _, upload := range uploads {
  503. id := upload.Get("id").Int()
  504. ret = append(ret, id)
  505. }
  506. return ret, nil
  507. }
  508. func finishUploadId(uploadId int64, moduleId string, cookie string) error {
  509. rBody := make(map[string]int64)
  510. rBody["upload_id"] = uploadId
  511. rByte, _ := json.Marshal(rBody)
  512. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/activities-read/%s", moduleId)
  513. request, err := http.NewRequest("POST", url, bytes.NewBuffer(rByte))
  514. if err != nil {
  515. return err
  516. }
  517. request.Header.Set("Content-Type", "application/json")
  518. request.Header.Set("Cookie", cookie)
  519. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  520. //request.Header.Set("Host", "chengdu.ouchn.cn")
  521. client := http.Client{}
  522. resp, err := client.Do(request)
  523. if err != nil {
  524. return err
  525. }
  526. defer resp.Body.Close()
  527. respBytes, err := ioutil.ReadAll(resp.Body)
  528. if err != nil {
  529. return err
  530. }
  531. if true {
  532. fmt.Println("do material resp:", string(respBytes))
  533. }
  534. return nil
  535. }
  536. func finishMaterial(uploadIds []int64, moduleId string, cookie string) {
  537. for _, v := range uploadIds {
  538. finishUploadId(v, moduleId, cookie)
  539. time.Sleep(2 * time.Second)
  540. }
  541. }
  542. func doMaterial(moduleId string, cookie string) {
  543. uploadIds, _ := getUploadIds(moduleId, cookie)
  544. if len(uploadIds) > 0 {
  545. finishMaterial(uploadIds, moduleId, cookie)
  546. }
  547. }
  548. func MakeTxtUrl(courseId string, moduleId string, id string) string {
  549. return fmt.Sprintf("https://lms.ouchn.cn/course/%s/learning-activity/full-screen#/%s", courseId, id)
  550. }
  551. func MakeExamUrl(courseId string, moduleId string, id string) string {
  552. return fmt.Sprintf("https://lms.ouchn.cn/course/%s/learning-activity/full-screen#/exam/%s", courseId, id)
  553. }
  554. func VedioComplete(cookie string, id string, start, end int64) (int64, error) {
  555. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/activities-read/%s", id)
  556. //fmt.Printf("get sections:%v,%v\n", str, cookie)
  557. m := map[string]interface{}{
  558. "start": start,
  559. "end": end,
  560. }
  561. content, _ := json.Marshal(m)
  562. reader := bytes.NewReader(content)
  563. //fmt.Printf("post data:%s\n", content)
  564. request, err := http.NewRequest("POST", url, reader)
  565. if err != nil {
  566. return 0, err
  567. }
  568. request.Header.Set("Content-Type", "application/json")
  569. request.Header.Set("Cookie", cookie)
  570. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  571. client := http.Client{}
  572. resp, err := client.Do(request)
  573. if err != nil {
  574. return 0, err
  575. }
  576. defer resp.Body.Close()
  577. respBytes, err := ioutil.ReadAll(resp.Body)
  578. if err != nil {
  579. return 0, err
  580. }
  581. fmt.Printf("get bytes:%s\n", respBytes)
  582. if gjson.GetBytes(respBytes, "error_msg").String() != ""{
  583. return 0,fmt.Errorf("视频处理失败(%s)",gjson.GetBytes(respBytes, "error_msg").String())
  584. }
  585. if gjson.GetBytes(respBytes, "activity_id").Int() == 0 {
  586. return 0, fmt.Errorf("视频处理失败")
  587. }
  588. str := gjson.GetBytes(respBytes, "data").Get("completeness").String()
  589. if str == "full" {
  590. return 100, nil
  591. }
  592. if str == "part" {
  593. return 10, nil
  594. }
  595. return gjson.GetBytes(respBytes, "data").Get("completeness").Int(), nil
  596. }
  597. func VedioCompleteHandle(cookie string, id string) error {
  598. count := 0
  599. start, end := int64(0), int64(1000)
  600. for count < 5 {
  601. finish, err := VedioComplete(cookie, id, start, end)
  602. if err != nil {
  603. return err
  604. }
  605. if finish < 100 {
  606. start = end
  607. end += 1000
  608. count++
  609. continue
  610. }
  611. return nil
  612. }
  613. return fmt.Errorf("视频处理失败")
  614. }
  615. func loadAnswerFromExcel() (map[string]AnswerInfo,error) {
  616. answerMap := make(map[string]AnswerInfo)
  617. entries, err := os.ReadDir("./")
  618. if err != nil {
  619. fmt.Println("Error reading directory:", err)
  620. return answerMap, err
  621. }
  622. for _,v := range entries{
  623. if !v.IsDir(){
  624. if strings.HasPrefix(v.Name(),"account") || strings.HasPrefix(v.Name(),"~") || strings.HasPrefix(v.Name(),".") {
  625. continue
  626. }
  627. if strings.HasSuffix(v.Name(),".xlsx") || strings.HasSuffix(v.Name(),".xls") {
  628. fmt.Println("读取文件:",v.Name())
  629. file, err := excelize.OpenFile(v.Name())
  630. if err != nil {
  631. fmt.Println("open file err:", err)
  632. return answerMap,err
  633. }
  634. for index, sheetName := range file.GetSheetList() {
  635. if index > 0 {
  636. break
  637. }
  638. rows, err := file.GetRows(sheetName)
  639. if err != nil{
  640. return answerMap,err
  641. }
  642. for _, row := range rows {
  643. rowLen := len(row)
  644. if rowLen <= 2 {
  645. continue
  646. }
  647. for rowIndex,_:= range row{
  648. row[rowIndex] = strings.TrimSpace(row[rowIndex])
  649. if row[rowIndex] == ""{
  650. fmt.Println("答案格式不正确,存在空列:",rowIndex,row)
  651. return answerMap,fmt.Errorf("答案格式不正确,存在空列")
  652. }
  653. }
  654. tmpAnswer := AnswerInfo{}
  655. tmpAnswer.Question = row[1]
  656. tmpAnswer.Question = ParseHan(tmpAnswer.Question)
  657. //tmpAnswer.InfoName = row[1]
  658. tmpAnswer.Type = row[0]
  659. tmpAnswer.Answer = row[2:]
  660. key := tmpAnswer.Type+":"+tmpAnswer.Question
  661. key = strings.ReplaceAll(key, " ", "")
  662. answerMap[key] = tmpAnswer
  663. }
  664. }
  665. }
  666. }
  667. //fmt.Println(v.Name(),v.IsDir())
  668. }
  669. if len(answerMap) == 0 {
  670. return answerMap,fmt.Errorf("答案为空")
  671. }
  672. return answerMap,nil
  673. }
  674. func handleVideo(ctx context.Context,moduleInfo *ModuleInfo, module CourseModule,courseId string,cookie string) error {
  675. vedioLen := len(moduleInfo.VedioInfos)
  676. for index, info := range moduleInfo.VedioInfos {
  677. if Conf.SkipProcessed{
  678. nextIndex := index+1
  679. if index+1 < vedioLen{
  680. if !moduleInfo.VedioInfos[nextIndex].PreUnCompelte{
  681. fmt.Println("已处理("+info.Name+")跳过")
  682. continue
  683. }
  684. }
  685. }
  686. if info.Type == 1{
  687. if Conf.HandlePage{
  688. fmt.Printf("------------开始处理网页数据:%s------------\n", info.Name)
  689. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  690. err := chromedp.Run(ctx,
  691. chromedp.Navigate(url),
  692. )
  693. if err != nil{
  694. fmt.Println("跳转网页:",url,"失败:",err)
  695. return err
  696. }
  697. time.Sleep(time.Duration(Conf.HandleInterval) * time.Second)
  698. }
  699. }else if info.Type == 2 {
  700. if info.Stype == "material" {
  701. if Conf.HandleMaterial{
  702. fmt.Printf("------------开始处理材料数据:%s------------\n", info.Name)
  703. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  704. err := chromedp.Run(ctx,
  705. chromedp.Navigate(url),
  706. )
  707. if err != nil{
  708. fmt.Println("跳转网页:",url,"失败:",err)
  709. return err
  710. }
  711. doMaterial(info.Id, cookie)
  712. time.Sleep(time.Duration(Conf.HandleInterval) * time.Second)
  713. }
  714. }else{
  715. if Conf.HandleVideo{
  716. fmt.Printf("------------开始处理视频数据:%s------------\n", info.Name)
  717. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  718. err := chromedp.Run(ctx,
  719. chromedp.Navigate(url),
  720. )
  721. if err != nil{
  722. fmt.Println("跳转网页:",url,"失败:",err)
  723. return err
  724. }
  725. err = VedioCompleteHandle(cookie, info.Id)
  726. if err != nil {
  727. fmt.Printf("视频%s处理失败(%s)\n", info.Name, err.Error())
  728. return err
  729. }
  730. time.Sleep(time.Duration(Conf.HandleInterval) * time.Second)
  731. }
  732. }
  733. }
  734. }
  735. return nil
  736. }
  737. func handleCrawlerChooseImpl(ctx context.Context,node *cdp.Node,answerType string )(AnswerInfo,error){
  738. answerInfo := AnswerInfo{}
  739. answerInfo.Type = answerType
  740. var childNodes []*cdp.Node
  741. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div/div/span/p", &childNodes)); err != nil {
  742. fmt.Println("获取NODE失败:",err)
  743. return answerInfo,err
  744. }
  745. var question string
  746. if err := chromedp.Run(ctx, chromedp.Text(childNodes[0].FullXPath(), &question)); err != nil {
  747. fmt.Println("获取问题失败:", err)
  748. return answerInfo,err
  749. }
  750. question = strings.TrimSpace(question)
  751. fmt.Println("问题:", question)
  752. answerInfo.Question = question
  753. var childNodes1 []*cdp.Node
  754. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div/ol/li/label/div/span", &childNodes1)); err != nil {
  755. fmt.Println("获取NODE失败:",err)
  756. return answerInfo,err
  757. }
  758. var tmpContent string
  759. if err := chromedp.Run(ctx, chromedp.Text(childNodes1[0].FullXPath(), &tmpContent)); err != nil {
  760. fmt.Println("获取答案失败:", err)
  761. return answerInfo,err
  762. }
  763. if tmpContent == ""{
  764. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div/ol/li/label/div/span/p", &childNodes1)); err != nil {
  765. fmt.Println("获取NODE失败:",err)
  766. return answerInfo,err
  767. }
  768. }
  769. for _,v := range childNodes1{
  770. pContent := ""
  771. if err := chromedp.Run(ctx, chromedp.Text(v.FullXPath(), &pContent)); err != nil {
  772. fmt.Println("获取答案失败:", err)
  773. return answerInfo,err
  774. }
  775. pContent = strings.TrimSpace(pContent)
  776. answerInfo.Answer = append(answerInfo.Answer,pContent)
  777. }
  778. return answerInfo,nil
  779. }
  780. func handleCrawlerAnswerImpl(ctx context.Context,node *cdp.Node)(AnswerInfo,error){
  781. answerInfo := AnswerInfo{}
  782. answerInfo.Type = "问答题"
  783. var childNodes []*cdp.Node
  784. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div[1]/div[1]/span/p[1]", &childNodes)); err != nil {
  785. fmt.Println("获取NODE失败:",err)
  786. return answerInfo ,err
  787. }
  788. var question string
  789. if err := chromedp.Run(ctx, chromedp.Text(childNodes[0].FullXPath(), &question)); err != nil {
  790. fmt.Println("获取问题失败:", err)
  791. return answerInfo ,err
  792. }
  793. question = strings.TrimSpace(question)
  794. fmt.Println("问题:", question)
  795. //fmt.Println(answerMap)
  796. answerInfo.Question = question
  797. return answerInfo, nil
  798. }
  799. func handleAnswerImpl(ctx context.Context,node *cdp.Node,answerMap map[string]AnswerInfo,infoName string )(int,error){
  800. isChoose := 1
  801. var childNodes []*cdp.Node
  802. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div[1]/div[1]/span/p[1]", &childNodes)); err != nil {
  803. fmt.Println("获取NODE失败:",err)
  804. return isChoose ,err
  805. }
  806. var question string
  807. if err := chromedp.Run(ctx, chromedp.Text(childNodes[0].FullXPath(), &question)); err != nil {
  808. fmt.Println("获取问题失败:", err)
  809. return isChoose ,err
  810. }
  811. question = strings.TrimSpace(question)
  812. question = ParseHan(question)
  813. fmt.Println("问题:", question)
  814. infoName = strings.TrimSpace(infoName)
  815. key := "问答题"+":"+question
  816. key = strings.ReplaceAll(key, " ", "")
  817. tmpAnswer,ok := answerMap[key]
  818. if !ok{
  819. fmt.Println("问题:",question," 未提供答案")
  820. return isChoose,fmt.Errorf("无答案")
  821. }
  822. if len(tmpAnswer.Answer) == 0 {
  823. fmt.Println("问题:",question," 未提供答案")
  824. return isChoose,fmt.Errorf("无答案")
  825. }
  826. escapedAnswer := strconv.Quote(tmpAnswer.Answer[0])
  827. if len(tmpAnswer.Answer) > 1 {
  828. escapedAnswer = strconv.Quote(tmpAnswer.Answer[random(len(tmpAnswer.Answer))])
  829. }
  830. if len(escapedAnswer) == 0 {
  831. fmt.Println("问题:",question," 无答案")
  832. return isChoose,fmt.Errorf("无答案")
  833. }
  834. jsCode := fmt.Sprintf(`
  835. var element = document.evaluate("%s", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  836. if (element) {
  837. element.textContent = %s;
  838. }
  839. `,node.FullXPath()+"/div/div/div/div/div/div/div/p",escapedAnswer)
  840. //fmt.Println("JS CODE:",jsCode)
  841. if err := chromedp.Run(ctx, chromedp.Evaluate(jsCode, nil)); err != nil {
  842. fmt.Println("设置 <p> 元素文本内容失败:", err)
  843. return isChoose, err
  844. }
  845. return 0, nil
  846. }
  847. func random(count int) int {
  848. if count == 0 {
  849. return 0
  850. }
  851. //rand.Seed(time.Now().UnixNano())
  852. return rand.Intn(count)
  853. }
  854. func handleChooseImpl(ctx context.Context,node *cdp.Node,answerMap map[string]AnswerInfo,isMulti bool,typeStr string,randomWrong bool ,infoName string)(int,error){
  855. miss := 1
  856. var childNodes []*cdp.Node
  857. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div/div/span/p", &childNodes)); err != nil {
  858. fmt.Println("获取NODE失败:",err)
  859. return miss ,err
  860. }
  861. var question string
  862. if err := chromedp.Run(ctx, chromedp.Text(childNodes[0].FullXPath(), &question)); err != nil {
  863. fmt.Println("获取问题失败:", err)
  864. return miss ,err
  865. }
  866. question = strings.TrimSpace(question)
  867. question = ParseHan(question)
  868. fmt.Println("问题:", question,"是否随机出错:",randomWrong)
  869. infoName = strings.TrimSpace(infoName)
  870. key := typeStr+":"+question
  871. key = strings.ReplaceAll(key, " ", "")
  872. fmt.Println("KEY :",key)
  873. tmpAnswer,ok := answerMap[key]
  874. if !ok{
  875. fmt.Println("问题:",question," 未提供答案")
  876. //return isChoose,nil
  877. }
  878. time.Sleep(1*time.Second)
  879. if isMulti{
  880. var nodes []*cdp.Node
  881. err := chromedp.Run(ctx,
  882. chromedp.Nodes(node.FullXPath()+"/div/div[2]/ol/li/label/span/input", &nodes, chromedp.BySearch),
  883. )
  884. if err != nil{
  885. fmt.Println("勾选组件不存在:",err)
  886. return miss,err
  887. }
  888. for index,v := range nodes{
  889. attr := v.AttributeValue("class")
  890. //fmt.Println("第",index+1," class:",attr)
  891. if strings.Contains(attr, "ng-not-empty") {
  892. fmt.Println("第",index+1,"项非空取消选择")
  893. if err := chromedp.Run(ctx, chromedp.Click(v.FullXPath())); err != nil {
  894. fmt.Println("点击取消失败:",err)
  895. return miss,err
  896. }
  897. }
  898. }
  899. }
  900. var childNodes1 []*cdp.Node
  901. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div/ol/li/label/div/span", &childNodes1)); err != nil {
  902. fmt.Println("获取NODE失败:",err)
  903. return miss ,err
  904. }
  905. var tmpContent string
  906. if err := chromedp.Run(ctx, chromedp.Text(childNodes1[0].FullXPath(), &tmpContent)); err != nil {
  907. fmt.Println("获取答案失败:", err)
  908. return miss ,err
  909. }
  910. if tmpContent == ""{
  911. if err := chromedp.Run(ctx, chromedp.Nodes(node.FullXPath()+"/div/div/ol/li/label/div/span/p", &childNodes1)); err != nil {
  912. fmt.Println("获取NODE失败:",err)
  913. return miss ,err
  914. }
  915. }
  916. ///html/body/div[2]/div[4]/div[3]/div[8]/div/div/div[5]/div/div[2]/div/ol/li[18]/div/div[2]/ol/li[1]/label/div/span/p
  917. //fmt.Println("node:",childNodes1)
  918. rightIndex := -1
  919. for nodeIndex,v := range childNodes1{
  920. pContent := ""
  921. if err := chromedp.Run(ctx, chromedp.Text(v.FullXPath(), &pContent)); err != nil {
  922. fmt.Println("获取答案失败:", err)
  923. return miss ,err
  924. }
  925. pContent = strings.TrimSpace(pContent)
  926. for _,ans := range tmpAnswer.Answer{
  927. if ans == pContent{
  928. miss = 0
  929. fmt.Println("选择答案:", pContent)
  930. if err := chromedp.Run(ctx, chromedp.Click(v.FullXPath())); err != nil {
  931. fmt.Println("点击失败:",err)
  932. return miss, err
  933. }
  934. rightIndex = nodeIndex
  935. }
  936. }
  937. }
  938. if miss == 1 {
  939. index := random(len(childNodes1))
  940. fmt.Println("未找到答案,随机选择第",index+1,"项")
  941. if err := chromedp.Run(ctx, chromedp.Click(childNodes1[index].FullXPath())); err != nil {
  942. fmt.Println("点击失败:",err)
  943. return miss,err
  944. }
  945. }else{
  946. if randomWrong{
  947. index := 0
  948. for i:=0;i<10;i++ {
  949. index = random(len(childNodes1))
  950. if rightIndex == index{
  951. continue
  952. }else{
  953. break
  954. }
  955. }
  956. fmt.Println("随机出错,随机选择第",index+1,"项")
  957. if err := chromedp.Run(ctx, chromedp.Click(childNodes1[index].FullXPath())); err != nil {
  958. fmt.Println("点击失败:",err)
  959. return miss,err
  960. }
  961. }
  962. }
  963. return miss, nil
  964. }
  965. type AnswerInfo struct{
  966. Question string
  967. Type string
  968. InfoName string
  969. Answer []string
  970. }
  971. func checkExamFinish(id string, cookie string) (finish bool, submited bool, noScore bool) {
  972. url := fmt.Sprintf("https://lms.ouchn.cn/api/exams/%s", id)
  973. request, err := http.NewRequest("GET", url, nil)
  974. if err != nil {
  975. return false, false, false
  976. }
  977. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  978. request.Header.Set("Cookie", cookie)
  979. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  980. //request.Header.Set("Host", "chengdu.ouchn.cn")
  981. client := http.Client{}
  982. resp, err := client.Do(request)
  983. if err != nil {
  984. return false, false, false
  985. }
  986. defer resp.Body.Close()
  987. respBytes, err := ioutil.ReadAll(resp.Body)
  988. if err != nil {
  989. return false, false, false
  990. }
  991. scorePercentage := gjson.GetBytes(respBytes, "score_percentage").String()
  992. if scorePercentage == "0" || scorePercentage == "0.0" || scorePercentage == "0.00" {
  993. return true, false, true
  994. }
  995. submitTimes := gjson.GetBytes(respBytes, "submit_times").Int()
  996. submittedTimes := gjson.GetBytes(respBytes, "submitted_times").Int()
  997. if submittedTimes > 0 {
  998. submited = true
  999. }
  1000. if submitTimes == submittedTimes && submitTimes > 0 {
  1001. return true, submited, false
  1002. }
  1003. return false, submited, false
  1004. }
  1005. func checkExamScore(id string, cookie string) (finish bool) {
  1006. url := fmt.Sprintf("https://lms.ouchn.cn/api/exams/%s/submissions", id)
  1007. request, err := http.NewRequest("GET", url, nil)
  1008. if err != nil {
  1009. return false
  1010. }
  1011. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  1012. request.Header.Set("Cookie", cookie)
  1013. request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36")
  1014. //request.Header.Set("Host", "chengdu.ouchn.cn")
  1015. client := http.Client{}
  1016. resp, err := client.Do(request)
  1017. if err != nil {
  1018. return false
  1019. }
  1020. defer resp.Body.Close()
  1021. respBytes, err := ioutil.ReadAll(resp.Body)
  1022. if err != nil {
  1023. return false
  1024. }
  1025. examScore := gjson.GetBytes(respBytes, "exam_score").Int()
  1026. fmt.Println("分数:", examScore, " 最低要求分数:", Conf.MinScore)
  1027. if int(examScore) >= Conf.MinScore {
  1028. return true
  1029. }
  1030. return false
  1031. }
  1032. func writeExameToExcel(infos []AnswerInfo,writer *excelize.StreamWriter,lastLine int,infoName string)(int,error){
  1033. for _,v := range infos{
  1034. lastLine++
  1035. cell, err := excelize.CoordinatesToCellName(1,lastLine)
  1036. if err != nil{
  1037. return lastLine,err
  1038. }
  1039. rowList := []interface{}{}
  1040. rowList = append(rowList,v.Type)
  1041. rowList = append(rowList,v.Question)
  1042. //rowList = append(rowList,infoName)
  1043. for _,ans := range v.Answer{
  1044. rowList = append(rowList,ans)
  1045. }
  1046. err = writer.SetRow(cell, rowList)
  1047. if err != nil{
  1048. return lastLine,err
  1049. }
  1050. }
  1051. return lastLine,nil
  1052. }
  1053. func createFile(fileName string) (*excelize.File, *excelize.StreamWriter) {
  1054. file := excelize.NewFile()
  1055. //设置表名
  1056. file.SetSheetName("Sheet1", "表1")
  1057. //创建流式写入
  1058. writer, err := file.NewStreamWriter("表1")
  1059. if err != nil {
  1060. return nil, nil
  1061. }
  1062. file.SaveAs(fileName)
  1063. return file, writer
  1064. }
  1065. func pullBottom(ctx context.Context,needPull bool ) error {
  1066. if needPull{
  1067. err := waitVisibleTimeout(ctx,"/html/body/div/div/div/div/div[2]/div[2]/div[1]")
  1068. if err != nil {
  1069. fmt.Println("获取div失败:",err)
  1070. return err
  1071. }
  1072. if err := chromedp.Run(ctx,
  1073. chromedp.Evaluate(`document.evaluate("/html/body/div/div/div/div/div[2]/div[2]/div[1]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.scrollTop = document.evaluate("/html/body/div/div/div/div/div[2]/div[2]/div[1]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.scrollHeight`, nil),
  1074. ); err != nil {
  1075. fmt.Println("拉动窗口失败:",err )
  1076. return err
  1077. }
  1078. }
  1079. err := waitVisibleAndClickTimeout(ctx,"//a[@class='button button-green take-exam ng-scope']")
  1080. if err != nil {
  1081. fmt.Println("开始答题失败,找不到开始按钮:",err)
  1082. }
  1083. err = waitVisibleAndClickTimeout(ctx,"//input[@type='checkbox' and @name='confirm']")
  1084. if err != nil {
  1085. fmt.Println("开始答题失败,找不到确认按钮:",err)
  1086. return err
  1087. }
  1088. return nil
  1089. }
  1090. // 爬取试题
  1091. func crawlerExame(ctx context.Context,moduleInfo *ModuleInfo, module CourseModule,courseId string,writer *excelize.StreamWriter,lastLine int) (int,error) {
  1092. for _, info := range moduleInfo.ExamInfos {
  1093. info.Name = strings.TrimSpace(info.Name)
  1094. url := MakeExamUrl(courseId, module.ModuleId, info.Id)
  1095. err := chromedp.Run(ctx,
  1096. chromedp.Navigate(url),
  1097. )
  1098. if err != nil{
  1099. return lastLine,err
  1100. }
  1101. fmt.Println("INFO NAME:",info.Name )
  1102. needPull := false
  1103. for i:=0 ;i<=3;i++{
  1104. err = pullBottom(ctx,needPull)
  1105. if err != nil{
  1106. needPull = true
  1107. time.Sleep(3*time.Second)
  1108. fmt.Println("第",i+1,"次失败:",err)
  1109. continue
  1110. }else{
  1111. break
  1112. }
  1113. }
  1114. if err != nil {
  1115. fmt.Println("开始答题失败:",err)
  1116. return lastLine,err
  1117. }
  1118. time.Sleep(1*time.Second)
  1119. err = waitVisibleAndClickTimeout(ctx,"//button[text()='开始答题' or text()='继续答题']")
  1120. if err != nil {
  1121. fmt.Println("开始答题失败:",err)
  1122. return lastLine,err
  1123. }
  1124. err = waitVisibleTimeout(ctx,"//li[contains(@class, 'subject ng-scope ')]")
  1125. if err != nil {
  1126. fmt.Println("等待题目出错:",err)
  1127. return lastLine,err
  1128. }
  1129. var questionClasses []*cdp.Node
  1130. if err := chromedp.Run(ctx, chromedp.Nodes("//li[contains(@class, 'subject ng-scope ')]", &questionClasses)); err != nil {
  1131. fmt.Println("获取题目出错:",err)
  1132. return lastLine,err
  1133. }
  1134. answerInfoList := []AnswerInfo{}
  1135. fmt.Println(questionClasses,"------------------题目长度(包含题目类型):",len(questionClasses))
  1136. for _, v := range questionClasses {
  1137. attr := v.AttributeValue("class")
  1138. if strings.Contains(attr, "true_or_false") {
  1139. fmt.Println("判断题")
  1140. answerInfo ,err := handleCrawlerChooseImpl(ctx,v,"判断题")
  1141. if err != nil{
  1142. return lastLine,err
  1143. }
  1144. answerInfoList = append(answerInfoList,answerInfo)
  1145. }else if strings.Contains(attr, "single_selection") {
  1146. fmt.Println("单选题")
  1147. answerInfo ,err := handleCrawlerChooseImpl(ctx,v,"单选题")
  1148. if err != nil{
  1149. return lastLine,err
  1150. }
  1151. answerInfoList = append(answerInfoList,answerInfo)
  1152. }else if strings.Contains(attr, "multiple_selection") {
  1153. fmt.Println("多选题")
  1154. answerInfo ,err := handleCrawlerChooseImpl(ctx,v,"多选题")
  1155. if err != nil{
  1156. return lastLine,err
  1157. }
  1158. answerInfoList = append(answerInfoList,answerInfo)
  1159. } else if strings.Contains(attr, "short_answer") {
  1160. fmt.Println("问答题")
  1161. answerInfo ,err := handleCrawlerAnswerImpl(ctx,v)
  1162. if err != nil{
  1163. return lastLine,err
  1164. }
  1165. answerInfoList = append(answerInfoList,answerInfo)
  1166. }else{
  1167. fmt.Println("非题目")
  1168. continue
  1169. }
  1170. }
  1171. lastLine, err = writeExameToExcel(answerInfoList,writer,lastLine,info.Name)
  1172. if err != nil{
  1173. return lastLine,err
  1174. }
  1175. time.Sleep(time.Duration(Conf.HandleInterval)*time.Second)
  1176. }
  1177. return lastLine,nil
  1178. }
  1179. func ParseHan(str string) string {
  1180. ret := ""
  1181. for _, r := range str {
  1182. if unicode.Is(unicode.Han, r) {
  1183. ret = ret + string(r)
  1184. } else if (r >= 48 && r <= 57) || (r >= 65 && r <= 90) || (r >= 97 && r <= 112) {
  1185. ret = ret + string(r)
  1186. }
  1187. }
  1188. return ret
  1189. }
  1190. func handleExame(ctx context.Context,moduleInfo *ModuleInfo, module CourseModule,courseId string,cookie string,answerMap map[string]AnswerInfo) error {
  1191. for _, info := range moduleInfo.ExamInfos {
  1192. info.Name = strings.TrimSpace(info.Name)
  1193. fmt.Println("------------开始处理试题:",info.Name,"------------")
  1194. finsh, submited, noScore := checkExamFinish(info.Id, cookie)
  1195. if !Conf.HandelNoPoint{
  1196. if noScore {
  1197. fmt.Printf("不计分,该试题不做\n")
  1198. continue
  1199. }
  1200. }
  1201. if finsh {
  1202. fmt.Printf("试题达到最大提交次数\n")
  1203. continue
  1204. }
  1205. if submited {
  1206. isFinsh := checkExamScore(info.Id, cookie)
  1207. if isFinsh {
  1208. fmt.Printf("已完成该试题,并满足分数\n")
  1209. continue
  1210. } else {
  1211. fmt.Printf("已完成该试题,不满足分数,继续答题\n")
  1212. }
  1213. }
  1214. url := MakeExamUrl(courseId, module.ModuleId, info.Id)
  1215. err := chromedp.Run(ctx,
  1216. chromedp.Navigate(url),
  1217. )
  1218. if err != nil{
  1219. return err
  1220. }
  1221. needPull := false
  1222. for i:=0 ;i<=3;i++{
  1223. err = pullBottom(ctx,needPull)
  1224. if err != nil{
  1225. needPull = true
  1226. time.Sleep(3*time.Second)
  1227. fmt.Println("第",i+1,"次失败:",err)
  1228. continue
  1229. }else{
  1230. break
  1231. }
  1232. }
  1233. if err != nil {
  1234. fmt.Println("开始答题失败:",err)
  1235. return err
  1236. }
  1237. time.Sleep(1*time.Second)
  1238. err = waitVisibleAndClickTimeout(ctx,"//button[text()='开始答题' or text()='继续答题']")
  1239. if err != nil {
  1240. fmt.Println("开始答题失败:",err)
  1241. return err
  1242. }
  1243. err = waitVisibleTimeout(ctx,"//li[contains(@class, 'subject ng-scope ')]")
  1244. if err != nil {
  1245. fmt.Println("等待题目出错:",err)
  1246. return err
  1247. }
  1248. time.Sleep(3*time.Second)
  1249. //time.Sleep(10*time.Second)
  1250. var questionClasses []*cdp.Node
  1251. if err := chromedp.Run(ctx, chromedp.Nodes("//li[contains(@class, 'subject ng-scope ')]", &questionClasses)); err != nil {
  1252. fmt.Println("获取题目出错:",err)
  1253. return err
  1254. }
  1255. total := 0
  1256. miss := 0
  1257. randomCount := random(Conf.MaxRandomQuestion)
  1258. randomHandelCount := 0
  1259. fmt.Println("可出错个数:",randomCount,Conf.MaxRandomQuestion)
  1260. fmt.Println("------------------题目长度(包含题目类型):",len(questionClasses))
  1261. for _, v := range questionClasses {
  1262. //fmt.Println("题目")
  1263. randWrong := false
  1264. if randomCount > randomHandelCount{
  1265. isWrong := random(2)
  1266. //fmt.Println("isWrong:",isWrong)
  1267. if isWrong == 1{
  1268. randomHandelCount++
  1269. fmt.Println("已随机个数:",randomHandelCount)
  1270. randWrong = true
  1271. //fmt.Println("randWrong111111111111111",randWrong)
  1272. }
  1273. }
  1274. //fmt.Println("randWrong",randWrong)
  1275. attr := v.AttributeValue("class")
  1276. if strings.Contains(attr, "true_or_false") {
  1277. //fmt.Println("判断题")
  1278. isChoose ,err := handleChooseImpl(ctx,v,answerMap,false,"判断题",randWrong,info.Name)
  1279. if err != nil{
  1280. return err
  1281. }
  1282. miss = miss + isChoose
  1283. }else if strings.Contains(attr, "single_selection") {
  1284. //fmt.Println("单选题")
  1285. isChoose ,err := handleChooseImpl(ctx,v,answerMap,false,"单选题",randWrong,info.Name)
  1286. if err != nil{
  1287. return err
  1288. }
  1289. miss = miss + isChoose
  1290. }else if strings.Contains(attr, "multiple_selection") {
  1291. //fmt.Println("多选题")
  1292. isChoose ,err := handleChooseImpl(ctx,v,answerMap,true,"多选题",randWrong,info.Name)
  1293. if err != nil{
  1294. return err
  1295. }
  1296. miss = miss + isChoose
  1297. } else if strings.Contains(attr, "short_answer") {
  1298. //fmt.Println("问答题")
  1299. isChoose ,err := handleAnswerImpl(ctx,v,answerMap,info.Name)
  1300. if err != nil{
  1301. return err
  1302. }
  1303. miss = miss + isChoose
  1304. }else{
  1305. if randWrong {
  1306. randomHandelCount--
  1307. }
  1308. //fmt.Println("非题目")
  1309. continue
  1310. }
  1311. total++
  1312. if miss > Conf.QuestionCount{
  1313. fmt.Println("无答案数(",miss,")超过设定值:",Conf.QuestionCount)
  1314. return fmt.Errorf("无答案数过多超过设定值")
  1315. }
  1316. }
  1317. if miss > Conf.QuestionCount{
  1318. fmt.Println("无答案数(",miss,")超过设定值:",Conf.QuestionCount)
  1319. return fmt.Errorf("无答案数过多超过设定值")
  1320. }
  1321. fmt.Println("等待:",Conf.CommitExamTime,"秒提交")
  1322. time.Sleep(time.Duration(Conf.CommitExamTime)*time.Second)
  1323. err = waitVisibleAndClickTimeout(ctx,"//a[@class='button button-green ng-scope' and text()='交卷']")
  1324. if err != nil {
  1325. fmt.Println("未找到交卷按钮:",err)
  1326. return err
  1327. }
  1328. time.Sleep(2*time.Second)
  1329. nodes ,err := getNodeTimeout(ctx,"#submit-exam-confirmation-popup > div > div:nth-child(3) > div > button:nth-child(1)")
  1330. if err != nil{
  1331. fmt.Println("获取确认节点失败")
  1332. nodes ,err = getNodeTimeout(ctx,`*[@id="submit-exam-confirmation-popup"]/div/div[3]/div/button[1]`)
  1333. if err != nil{
  1334. fmt.Println("获取确认节点失败")
  1335. nodes ,err = getNodeTimeout(ctx,`/html[1]/body[1]/div[10]/div[1]/div[3]/div[1]/button[1]`)
  1336. if err != nil{
  1337. fmt.Println("获取确认节点失败")
  1338. return err
  1339. }
  1340. }
  1341. }
  1342. for _, node := range nodes {
  1343. if node.FullXPath() == `/html[1]/body[1]/div[10]/div[1]/div[3]/div[1]/button[1]`{
  1344. fmt.Println("确定提交")
  1345. //time.Sleep(100*time.Hour)
  1346. err = chromedp.Run(ctx,
  1347. chromedp.Click(node.FullXPath(), chromedp.BySearch),
  1348. )
  1349. if err != nil{
  1350. fmt.Println("提交答案失败:",err)
  1351. }
  1352. }
  1353. }
  1354. fmt.Println("等待时间间隔(handleInterval):",Conf.HandleInterval)
  1355. time.Sleep(time.Duration(Conf.HandleInterval)*time.Second)
  1356. }
  1357. return nil
  1358. }
  1359. func waitVisibleTimeout(ctx context.Context,sel interface{}) error {
  1360. ctxTmp, cancleTmp := context.WithTimeout(ctx, time.Duration(Conf.Timeout)*time.Second)
  1361. defer cancleTmp()
  1362. done := make(chan struct{})
  1363. go func() {
  1364. defer close(done)
  1365. err := chromedp.Run(ctxTmp,
  1366. //chromedp.Sleep(1*time.Second),
  1367. chromedp.WaitVisible(sel),
  1368. )
  1369. if err != nil{
  1370. fmt.Println("操作:",sel,"失败 error:",err)
  1371. //fmt.Println(sel," err:",err)
  1372. }
  1373. }()
  1374. select {
  1375. case <-done:
  1376. // 操作完成,不需要做任何事情
  1377. return nil
  1378. case <-ctxTmp.Done():
  1379. // 如果 ctxTmp 超时或取消,输出相应信息
  1380. err := ctxTmp.Err()
  1381. if err != nil{
  1382. fmt.Println("操作超时,取消操作")
  1383. return err
  1384. }
  1385. }
  1386. return nil
  1387. }
  1388. func waitVisibleAndClickTimeout(ctx context.Context,sel interface{}) error {
  1389. ctxTmp, cancleTmp := context.WithTimeout(ctx, time.Duration(Conf.Timeout)*time.Second)
  1390. defer cancleTmp()
  1391. done := make(chan struct{})
  1392. go func() {
  1393. defer close(done)
  1394. err := chromedp.Run(ctxTmp,
  1395. //chromedp.Sleep(1*time.Second),
  1396. chromedp.WaitVisible(sel),
  1397. chromedp.Click(sel),
  1398. )
  1399. if err != nil{
  1400. fmt.Println("操作:",sel,"失败 error:",err)
  1401. //fmt.Println(sel," err:",err)
  1402. }
  1403. }()
  1404. select {
  1405. case <-done:
  1406. // 操作完成,不需要做任何事情
  1407. return nil
  1408. case <-ctxTmp.Done():
  1409. // 如果 ctxTmp 超时或取消,输出相应信息
  1410. err := ctxTmp.Err()
  1411. if err != nil{
  1412. fmt.Println("操作超时,取消操作")
  1413. return err
  1414. }
  1415. }
  1416. return nil
  1417. }
  1418. func getNodeTimeout(ctx context.Context,sel interface{}) ([]*cdp.Node,error) {
  1419. var nodes []*cdp.Node
  1420. ctxTmp, cancleTmp := context.WithTimeout(ctx, time.Duration(Conf.Timeout)*time.Second)
  1421. defer cancleTmp()
  1422. done := make(chan struct{})
  1423. go func() {
  1424. defer close(done)
  1425. err := chromedp.Run(ctxTmp,
  1426. chromedp.Nodes(sel, &nodes),
  1427. )
  1428. if err != nil{
  1429. fmt.Println("操作:",sel,"失败 error:",err)
  1430. //fmt.Println(sel," err:",err)
  1431. }
  1432. }()
  1433. select {
  1434. case <-done:
  1435. // 操作完成,不需要做任何事情
  1436. return nodes,nil
  1437. case <-ctxTmp.Done():
  1438. // 如果 ctxTmp 超时或取消,输出相应信息
  1439. err := ctxTmp.Err()
  1440. if err != nil{
  1441. fmt.Println("操作超时,取消操作")
  1442. return nodes,err
  1443. }
  1444. }
  1445. return nodes,nil
  1446. }
  1447. func autoLogin(ctx context.Context,userName string) error {
  1448. var buf []byte
  1449. err := chromedp.Run(ctx,
  1450. chromedp.Screenshot("body", &buf,chromedp.NodeVisible, chromedp.ByQuery),
  1451. )
  1452. if err != nil{
  1453. fmt.Println("截图失败:",err)
  1454. return err
  1455. }
  1456. code := codeGetNew(buf,userName)
  1457. //fmt.Println("识别验证码位置:", code)
  1458. codeList := strings.Split(code, "|")
  1459. if len(codeList) <=2{
  1460. return fmt.Errorf("识别异常,小于等于两个验证码")
  1461. }
  1462. fmt.Println("识别成功")
  1463. for _, v := range codeList {
  1464. codeX := strings.Split(v, ",")
  1465. if len(codeX) < 2 {
  1466. return fmt.Errorf("识别异常")
  1467. }
  1468. x, _ := strconv.Atoi(codeX[0])
  1469. y, _ := strconv.Atoi(codeX[1])
  1470. //fmt.Println("点击:",float64(x),float64(y))
  1471. err = chromedp.Run(ctx, chromedp.MouseClickXY(float64(x),float64(y),chromedp.ButtonLeft))
  1472. if err != nil {
  1473. fmt.Println("点击错误:", err)
  1474. return err
  1475. }
  1476. time.Sleep(1 * time.Second)
  1477. }
  1478. // 执行登录操作
  1479. //fmt.Println("点击确定")
  1480. err = chromedp.Run(ctx,
  1481. chromedp.Click(`/html/body/div[4]/div[1]/div[1]/div[2]/div/div/div[2]`),
  1482. )
  1483. if err != nil{
  1484. fmt.Println("点击确定按钮失败:",err )
  1485. return err
  1486. }
  1487. return nil
  1488. }
  1489. func run(name,userName ,password string,answerMap map[string]AnswerInfo,handleCourse string) error {
  1490. userName = strings.TrimSpace(userName)
  1491. password = strings.TrimSpace(password)
  1492. // 设置Chrome选项
  1493. opts := []chromedp.ExecAllocatorOption{
  1494. chromedp.Flag("disable-blink-features", "AutomationControlled"),
  1495. chromedp.Flag("exclude-switches", "enable-automation"),
  1496. chromedp.Flag("disable-popup-blocking", true),
  1497. chromedp.Flag("start-maximized", true),
  1498. chromedp.Flag("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"),
  1499. }
  1500. // 初始化Chrome实例
  1501. allocCtx, allocCancel := chromedp.NewExecAllocator(context.Background(), opts...)
  1502. defer allocCancel()
  1503. var err error
  1504. ctx, cancel := chromedp.NewContext(allocCtx)
  1505. defer cancel()
  1506. go func() {
  1507. <-ctx.Done()
  1508. err := ctx.Err()
  1509. if err == context.Canceled {
  1510. fmt.Println("浏览器已手动关闭")
  1511. } else {
  1512. fmt.Println("上下文发生错误:", err)
  1513. }
  1514. }()
  1515. // 执行登录操作
  1516. //fmt.Println(name,"登录")
  1517. err = chromedp.Run(ctx,
  1518. chromedp.Navigate("https://iam.pt.ouchn.cn/am/UI/Login?realm=%2F&service=initService&goto=https%3A%2F%2Fiam.pt.ouchn.cn%2Fam%2Foauth2%2Fauthorize%3Fservice%3DinitService%26response_type%3Dcode%26client_id%3D345fcbaf076a4f8a%26scope%3Dall%26redirect_uri%3Dhttps%253A%252F%252Fmenhu.pt.ouchn.cn%252Fouchnapp%252Fwap%252Flogin%252Findex%26decision%3DAllow"),
  1519. chromedp.SetValue(`//*[@id="loginName"]`, userName),
  1520. chromedp.SetValue(`//*[@id="password"]`, password),
  1521. chromedp.Sleep(2*time.Second),
  1522. chromedp.Click(`//*[@id="form_button"]`),
  1523. chromedp.Sleep(1*time.Second),
  1524. )
  1525. if err != nil{
  1526. fmt.Println("登录失败:",err)
  1527. return err
  1528. }
  1529. fmt.Println("获取验证码")
  1530. err = waitVisibleTimeout(ctx,"/html/body/div[4]/div[1]/div[1]/div[2]/div")
  1531. if err != nil{
  1532. fmt.Println("获取验证码登录失败:",err)
  1533. return err
  1534. }
  1535. time.Sleep(1*time.Second)
  1536. if !Conf.CodeManual{
  1537. for count := 0;count < 3 ;count++{
  1538. err = autoLogin(ctx,userName)
  1539. if err == nil {
  1540. break
  1541. }else{
  1542. time.Sleep(10*time.Second)
  1543. }
  1544. }
  1545. }
  1546. fmt.Printf("等待进入学生主页\n")
  1547. err = waitVisibleTimeout(ctx,"//a[text()='个人信息']")
  1548. if err != nil{
  1549. fmt.Println("进入学生主页失败:",err)
  1550. return err
  1551. }
  1552. cookie := getCookie(ctx)
  1553. time.Sleep(1*time.Second)
  1554. // 获取课程列表
  1555. courseList := []Course{}
  1556. for i:=0 ;i<3 ;i++{
  1557. fmt.Println("开始获取课程列表")
  1558. courseList = getCourceNew(ctx)
  1559. if len(courseList) == 0 {
  1560. fmt.Println("获取课程失败,课程为空,重新获取")
  1561. time.Sleep(10*time.Second)
  1562. continue
  1563. }else{
  1564. break
  1565. }
  1566. }
  1567. if len(courseList) == 0 {
  1568. fmt.Println("获取课程失败,课程为空")
  1569. return fmt.Errorf("获取课程失败,课程为空")
  1570. }
  1571. for _,v := range courseList{
  1572. if !strings.Contains(handleCourse,v.Name){
  1573. continue
  1574. }
  1575. fmt.Printf("*************正在处理课程(%s)***************\n",v.Name)
  1576. err = chromedp.Run(ctx,
  1577. chromedp.Navigate(v.Url),
  1578. //chromedp.WaitVisible(`//*[@id="student-module-menu"]/div[1]/div[1]/div/a`),
  1579. )
  1580. if err != nil {
  1581. fmt.Println("进入课程失败:",err)
  1582. return err
  1583. }
  1584. err = waitVisibleTimeout(ctx,`//*[@id="student-module-menu"]/div[1]/div[1]/div/a`)
  1585. if err != nil {
  1586. fmt.Println("进入课程失败:",err)
  1587. return err
  1588. }
  1589. courseId := getCourseId(v.Url)
  1590. if courseId == "" {
  1591. fmt.Println("课程id获取失败")
  1592. return fmt.Errorf("课程id获取失败")
  1593. }
  1594. cookie = getCookie(ctx)
  1595. modules, err := getCourseModules(courseId,cookie , false)
  1596. if err != nil {
  1597. fmt.Printf("获取module信息失败:%s\n", err.Error())
  1598. return err
  1599. }
  1600. lastLine := 0
  1601. var writer *excelize.StreamWriter
  1602. var file *excelize.File
  1603. if Conf.OnlyCrawlerAnswer{
  1604. fmt.Printf("*************正在爬取课程(%s)***************\n",v.Name)
  1605. file ,writer = createFile(fmt.Sprintf("%s.xlsx",v.Name))
  1606. }
  1607. for _, module := range modules {
  1608. fmt.Printf("***********课程:%s 栏目:%s***********\n", v.Name, module.Name)
  1609. moduleInfo, err := GetModuleInfo(courseId, module.ModuleId, cookie)
  1610. if err != nil {
  1611. fmt.Printf("获取module(%s)详细信息失败:%s", module.Name, err.Error())
  1612. return err
  1613. }
  1614. time.Sleep(2*time.Second)
  1615. if Conf.OnlyCrawlerAnswer{
  1616. lastLine, err = crawlerExame(ctx,moduleInfo,module,courseId,writer,lastLine)
  1617. if err != nil{
  1618. return err
  1619. }
  1620. file.Save()
  1621. continue
  1622. }
  1623. if Conf.HandleVideo {
  1624. err = handleVideo(ctx,moduleInfo,module,courseId,cookie)
  1625. if err != nil{
  1626. return err
  1627. }
  1628. }
  1629. if Conf.HandleAnswer{
  1630. err = handleExame(ctx,moduleInfo,module,courseId,cookie,answerMap)
  1631. if err != nil{
  1632. return err
  1633. }
  1634. }
  1635. }
  1636. //time.Sleep(100*time.Hour)
  1637. }
  1638. fmt.Println("学生(",name,")处理完成")
  1639. time.Sleep(2*time.Second)
  1640. return nil
  1641. }
  1642. func GetRecord(filename string) map[string]bool {
  1643. ret := map[string]bool{}
  1644. fi, err := os.Open(filename)
  1645. if err != nil {
  1646. return ret
  1647. }
  1648. defer fi.Close()
  1649. br := bufio.NewReader(fi)
  1650. for {
  1651. a, _, c := br.ReadLine()
  1652. //fmt.Printf("xx:%s\n", a)
  1653. if c == io.EOF {
  1654. break
  1655. }
  1656. array := strings.Split(string(a), " ")
  1657. if len(array) != 3 {
  1658. continue
  1659. }
  1660. ret[array[0]] = true
  1661. }
  1662. return ret
  1663. }
  1664. func GetAcounts(filename string) ([][]string, error) {
  1665. visited := GetRecord("record.txt")
  1666. fi, err := os.Open(filename)
  1667. if err != nil {
  1668. return nil, err
  1669. }
  1670. defer fi.Close()
  1671. ret := [][]string{}
  1672. br := bufio.NewReader(fi)
  1673. for {
  1674. line, err := br.ReadString('\n')
  1675. /*a, _, c := br.ReadLine()
  1676. if c == io.EOF {
  1677. break
  1678. }*/
  1679. //line := string(a)
  1680. //line = FilterANSI(line)
  1681. if err != nil{
  1682. break
  1683. }
  1684. array := strings.Split(line, ";")
  1685. fmt.Println(line)
  1686. if len(array) != 3 {
  1687. continue
  1688. }
  1689. if visited[array[0]] {
  1690. //fmt.Printf("visited:%v\n", array[2])
  1691. continue
  1692. }
  1693. item := []string{array[0], array[1], array[2]}
  1694. fmt.Println(array)
  1695. ret = append(ret, item)
  1696. }
  1697. return ret, nil
  1698. }
  1699. func columnIndexToName(index int) string {
  1700. var name string
  1701. for index > 0 {
  1702. index--
  1703. name = string('A'+index%26) + name
  1704. index /= 26
  1705. }
  1706. return name
  1707. }
  1708. func saveCell(file *excelize.File ,sheetName string,index ,line int,value string) error {
  1709. cell := columnIndexToName(index) + strconv.Itoa(line) // 新列的单元格位置
  1710. //fmt.Println("CELL VALUE :",cell,value,index,line)
  1711. if err := file.SetCellValue(sheetName, cell,value); err != nil {
  1712. fmt.Println(err)
  1713. return err
  1714. }else{
  1715. file.SetRowHeight(sheetName, line,13.5)
  1716. //file.SetColWidth(sheetName, columnIndexToName(index), columnIndexToName(index), 11.93) // 设置 A 列的宽度为 30
  1717. err = file.Save()
  1718. if err != nil{
  1719. return err
  1720. }
  1721. }
  1722. return nil
  1723. }
  1724. func main(){
  1725. err := LoadConfig(*configFile)
  1726. if err != nil {
  1727. fmt.Println("读取配置文件出错:",err)
  1728. return
  1729. }
  1730. var answerMap map[string]AnswerInfo
  1731. if Conf.HandleAnswer {
  1732. fmt.Println("开始收集答案")
  1733. answerMap ,err = loadAnswerFromExcel()
  1734. if err != nil{
  1735. fmt.Println("收集答案出错:",err)
  1736. return
  1737. }
  1738. }
  1739. //fmt.Println("答案:",answerMap)
  1740. go func(){
  1741. accountFile, err := excelize.OpenFile("account.xlsx")
  1742. if err != nil {
  1743. fmt.Println("open file err:", err)
  1744. return
  1745. }
  1746. for index, sheetName := range accountFile.GetSheetList() {
  1747. if index > 0 {
  1748. break
  1749. }
  1750. rows, err := accountFile.GetRows(sheetName)
  1751. if err != nil {
  1752. return
  1753. }
  1754. for rowLine, row := range rows {
  1755. fmt.Println("开始处理学生:",row[2])
  1756. rowLen := len(row)
  1757. if rowLen < 4 {
  1758. continue
  1759. }
  1760. if rowLen > 4{
  1761. if row[4] == "已处理"{
  1762. fmt.Println("已完成处理学生:",row[2])
  1763. continue
  1764. }
  1765. }
  1766. err = run(row[2],row[0],row[1],answerMap,row[3])
  1767. if err != nil{
  1768. fmt.Println("处理学生:",row[2],"出错:",err)
  1769. }else{
  1770. saveCell(accountFile,sheetName,5,rowLine+1,"已处理")
  1771. }
  1772. }
  1773. }
  1774. os.Exit(0)
  1775. }()
  1776. // 捕获信号
  1777. sigChan := make(chan os.Signal, 1)
  1778. signal.Notify(sigChan, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
  1779. sigValue := <-sigChan
  1780. fmt.Printf("接收到退出信号:%v", sigValue)
  1781. }