student.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. package student
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "net/http"
  11. "os"
  12. "ouchn/config"
  13. "runtime"
  14. "strings"
  15. "time"
  16. "github.com/tebeka/selenium"
  17. "github.com/tebeka/selenium/chrome"
  18. "github.com/tidwall/gjson"
  19. )
  20. func GetRecord(filename string) map[string]bool {
  21. ret := map[string]bool{}
  22. fi, err := os.Open(filename)
  23. if err != nil {
  24. return ret
  25. }
  26. defer fi.Close()
  27. br := bufio.NewReader(fi)
  28. for {
  29. a, _, c := br.ReadLine()
  30. //fmt.Printf("xx:%s\n", a)
  31. if c == io.EOF {
  32. break
  33. }
  34. array := strings.Split(string(a), " ")
  35. if len(array) != 3 {
  36. continue
  37. }
  38. ret[array[0]] = true
  39. }
  40. return ret
  41. }
  42. func GetAcounts(filename string) ([][]string, error) {
  43. visited := GetRecord("record.txt")
  44. fi, err := os.Open(filename)
  45. if err != nil {
  46. return nil, err
  47. }
  48. defer fi.Close()
  49. ret := [][]string{}
  50. br := bufio.NewReader(fi)
  51. for {
  52. a, _, c := br.ReadLine()
  53. if c == io.EOF {
  54. break
  55. }
  56. array := strings.Split(string(a), ";")
  57. if len(array) != 3 {
  58. continue
  59. }
  60. if visited[array[0]] {
  61. //fmt.Printf("visited:%v\n", array[2])
  62. continue
  63. }
  64. item := []string{array[0], array[1], array[2]}
  65. ret = append(ret, item)
  66. }
  67. return ret, nil
  68. }
  69. func Displayed(by, elementName string) func(selenium.WebDriver) (bool, error) {
  70. return func(wd selenium.WebDriver) (ok bool, err error) {
  71. var el selenium.WebElement
  72. el, err = wd.FindElement(by, elementName)
  73. if err != nil {
  74. ok = false
  75. err = nil
  76. return
  77. }
  78. ok, err = el.IsDisplayed()
  79. return
  80. }
  81. }
  82. func Check415Error(wd selenium.WebDriver) error {
  83. e, err := wd.FindElement(selenium.ByTagName, "body")
  84. if err != nil {
  85. return err
  86. }
  87. text, _ := e.Text()
  88. str := "错误代码:415"
  89. if len(text) <= len(str) {
  90. return nil
  91. }
  92. if text[:len(str)] == str {
  93. return errors.New("访问受限")
  94. }
  95. return nil
  96. }
  97. func StartOneStudent(usernameText string, passwordText string, realName string) (*selenium.Service, selenium.WebDriver, error) {
  98. var (
  99. //driverPath = `C:\Users\lqzlt\AppData\Local\Google\Chrome\Application\chromedriver.exe`
  100. driverPath = `chromedriver.exe`
  101. port = 60002
  102. )
  103. // chrome服务
  104. service, err := selenium.NewChromeDriverService(driverPath, port, []selenium.ServiceOption{}...)
  105. if nil != err {
  106. return nil, nil, err
  107. }
  108. defer func() {
  109. //_ = service.Stop()
  110. }()
  111. // Connect to the WebDriver instance running locally.
  112. caps := selenium.Capabilities{"browserName": "chrome"}
  113. imagCaps := map[string]interface{}{
  114. //"profile.managed_default_content_settings.images": 2,
  115. //"profile.default_content_setting_values":2,
  116. "same-site-by-default-cookies": 2,
  117. "cookies-without-same-site-must-be-secure": 2,
  118. }
  119. chromeCaps := chrome.Capabilities{
  120. Prefs: imagCaps,
  121. Path: "",
  122. ExcludeSwitches: []string{"enable-automation"},
  123. //Extensions:[]string{"D:\\sql2struct"},
  124. Args: []string{
  125. //"--headless",
  126. //"--disable-javascript",
  127. "--disable-blink-features=AutomationControlled",
  128. "--no-sandbox",
  129. "--disable-gpu-sandbox",
  130. "--accept-Encoding=gzip, deflate",
  131. "--accept-language=zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6",
  132. "--cache-control=max-age=0",
  133. "--abc=d",
  134. "--load-extention=D:\\sql2struct",
  135. //"--cookie=MoodleSession=66925citrm0o6b68gsfn9quaof;username=2051101450211;UserName=2051101450211",
  136. "--moodleSession=66925citrm0o6b68gsfn9quaof;username=2051101450211",
  137. "--upgrade-insecure-requests=1",
  138. //"--authorization=Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkYyREVCM0ZGMURBMUFEODNFOTA5QjIxMjM4QTZCMjU5RjAyQjNGMDEiLCJ0eXAiOiJKV1QiLCJ4NXQiOiI4dDZ6X3gyaHJZUHBDYklTT0theVdmQXJQd0UifQ.eyJuYmYiOjE2MzYwMTAzNTMsImV4cCI6MTYzNjAxMzk1MywiaXNzIjoiaHR0cDovL3Bhc3Nwb3J0Lm91Y2huLmNuIiwiYXVkIjpbImh0dHA6Ly9wYXNzcG9ydC5vdWNobi5jbi9yZXNvdXJjZXMiLCJvdWNobnN0dWRlbnRzcGFjZWFwaSJdLCJjbGllbnRfaWQiOiJzdHVkZW50c3BhY2UiLCJzdWIiOiJlZjkzZTdlYi0zYjhlLTQ2ZTUtYjA1ZC1mYTJmYjAwYjA2ZmUiLCJhdXRoX3RpbWUiOjE2MzYwMTAyMDMsImlkcCI6ImxvY2FsIiwibmFtZSI6IjIwNTExMDE0NTAyMTEiLCJVc2VySWQiOiJlZjkzZTdlYi0zYjhlLTQ2ZTUtYjA1ZC1mYTJmYjAwYjA2ZmUiLCJVc2VyTmFtZSI6IjIwNTExMDE0NTAyMTEiLCJuaWNrbmFtZSI6IueOi-aVjyIsIkF2YXRhciI6IiIsIlVzZXJUeXBlIjoiMiIsIlByb3ZpbmNlU2Nob29sQ29kZSI6IjUxMSIsIk9yZ2FuaXphdGlvbkNvZGUiOiI1MTE1NDAwIiwiTW9iaWxlQ29uZmlybWVkIjoiRmFsc2UiLCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwib3VjaG51c2VyIiwib3VjaG5zdHVkZW50c3BhY2VhcGkiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsicHdkIl19.ACwdaY50NnuXiQso4T6dS1PoQc7K2rTBRKWz0VmocC40xL4PlPy3Usf3kAZf-1dNFnh8PTBUWc9s_F6eTp_Rcemu5IBGL-iKs9I-QMeG3u7TnaNbCL4XwUt8vZoOPHaXzor3gN7WEqs01Qj11nTbERZ2ixEMhk2Ov_lGqCS8me67pyvd2HWCP7yUcizfEk4_5FQBZZ12daRpXrBksCC27I8BcpXQLrogWewa1W4x5uhLFeW7BKhBGxYDT4yM-b0RNM2jNTxn-ToqoOgBIsLsnU8e0eOKi6WBuhbDYK8psiCPt1hT_3BumeuJ-P36iuaPLcNv7D2T1WlCMHJe1Fv92Q",
  139. "--connection=keep-alive",
  140. //"--host=chengdu.ouchn.cn",
  141. "--accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
  142. "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36",
  143. },
  144. }
  145. //以上是设置浏览器参数
  146. caps.AddChrome(chromeCaps)
  147. wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
  148. if err != nil {
  149. return service, nil, errors.New(fmt.Sprintf("connect to the webDriver failed:%v\n", err))
  150. }
  151. defer func() {
  152. //_ = wd.Close()
  153. }()
  154. //wd.Get("file:///C:/Users/lqzlt/Desktop/1.html")
  155. //time.Sleep(1*time.Second)
  156. //handleQuestionWebTest(wd)
  157. //time.Sleep(1000*time.Second)
  158. //return service, wd, nil
  159. err = wd.Get("https://menhu.pt.ouchn.cn/")
  160. if err != nil {
  161. return service, wd, err
  162. }
  163. // 进入登录页面
  164. err = wd.WaitWithTimeout(Displayed(selenium.ByName, "button"), 5*time.Second)
  165. if err != nil {
  166. return service, wd, errors.New(fmt.Sprintf("获取登录页面失败:%v\n", err))
  167. }
  168. username, err := wd.FindElement(selenium.ByID, "loginName")
  169. if err != nil {
  170. return service, wd, errors.New(fmt.Sprintf("获取用户名按钮:%v\n", err))
  171. }
  172. password, err := wd.FindElement(selenium.ByID, "password")
  173. if err != nil {
  174. return service, wd, errors.New(fmt.Sprintf("获取密码按钮:%v\n", err))
  175. }
  176. submit, err := wd.FindElement(selenium.ByName, "button")
  177. if err != nil {
  178. return service, wd, errors.New(fmt.Sprintf("获取登录按钮:%v\n", err))
  179. }
  180. err = username.SendKeys(usernameText)
  181. if err != nil {
  182. return service, wd, errors.New(fmt.Sprintf("填充用户名:%v\n", err))
  183. }
  184. err = password.SendKeys(passwordText)
  185. if err != nil {
  186. return service, wd, errors.New(fmt.Sprintf("填充密码:%v\n", err))
  187. }
  188. verifyCode, err := wd.FindElement(selenium.ByID, "validateCode")
  189. if err != nil {
  190. return service, wd, errors.New(fmt.Sprintf("获取验证码按钮失败:%v\n", err))
  191. }
  192. count := 0
  193. for count < 60 {
  194. time.Sleep(1*time.Second)
  195. value, _ := verifyCode.GetAttribute("value")
  196. if len(value) < 4 {
  197. continue
  198. }
  199. break
  200. }
  201. if count >= 60 {
  202. return service, wd, errors.New("未输入验证码")
  203. }
  204. err = submit.Click()
  205. if err != nil {
  206. return service, wd, errors.New(fmt.Sprintf("点击登录:%v\n", err))
  207. }
  208. //time.Sleep(1200 *time.Second)
  209. //return service, wd, errors.New("登录测试")
  210. // 进入学生主页
  211. err = wd.WaitWithTimeout(Displayed(selenium.ByXPATH, "//a[text()='去学习']"), 5*time.Second)
  212. if err != nil {
  213. return service, wd, errors.New(fmt.Sprintf("进入学生主页失败:%v\n", err))
  214. }
  215. buttons, _ := wd.FindElements(selenium.ByXPATH, "//a[text()='去学习']")
  216. currentWindow, err := wd.CurrentWindowHandle()
  217. if err != nil {
  218. return service, wd, errors.New(fmt.Sprintf("获取当前窗口失败:%v\n", err))
  219. }
  220. for _, button := range buttons {
  221. text, _ := button.Text()
  222. if text != "去学习" {
  223. continue
  224. }
  225. button.Click()
  226. //time.Sleep(60*time.Second)
  227. count := 0
  228. handled := ""
  229. // 等待新页面弹出
  230. for count < 5 {
  231. time.Sleep(1 * time.Second)
  232. handles, _ := wd.WindowHandles()
  233. if len(handles) > 1 {
  234. handled = handles[len(handles)-1]
  235. //
  236. wd.SwitchWindow(handled)
  237. break
  238. }
  239. count++
  240. }
  241. if count > 5 {
  242. continue
  243. }
  244. if err := Check415Error(wd); err != nil {
  245. return service, wd, err
  246. }
  247. // 处理课程
  248. err = CourseHandle(wd, realName)
  249. fmt.Printf("xxxxerr:%v\n", err)
  250. // 关闭当前课程窗口
  251. wd.CloseWindow(handled)
  252. // 切换到学生主页窗口(有进入学习按钮的窗口)
  253. wd.SwitchWindow(currentWindow)
  254. runtime.GC()
  255. }
  256. return service, wd, err
  257. }
  258. // 等待url变化
  259. func waitUrl(wd selenium.WebDriver, url1 string, url2 string, count int) string {
  260. for count >= 0 {
  261. time.Sleep(100 * time.Millisecond)
  262. url, _ := wd.CurrentURL()
  263. if url != url1 && url != url2 {
  264. return url
  265. }
  266. count--
  267. }
  268. return ""
  269. }
  270. func getCookies(wd selenium.WebDriver) (string, error) {
  271. cookies, err := wd.GetCookies()
  272. if err != nil {
  273. return "", errors.New(fmt.Sprintf("获取cookie 失败,%v\n", err))
  274. }
  275. if len(cookies) == 0 {
  276. return "", errors.New(fmt.Sprintf("获取cookie 失败,%v\n", "cookie为空"))
  277. }
  278. ret := ""
  279. for _, v := range cookies {
  280. item := fmt.Sprintf("%s=%s", v.Name, v.Value)
  281. if ret == "" {
  282. ret = item
  283. continue
  284. }
  285. ret = ret + ";"
  286. ret = ret + item
  287. }
  288. return ret, nil
  289. }
  290. func checkCourseExist(courses []string, course string) bool {
  291. for _, v := range courses {
  292. if v == course {
  293. return true
  294. }
  295. }
  296. return false
  297. }
  298. type RadioInfo struct {
  299. Radio selenium.WebElement
  300. }
  301. type AnswerInfo struct {
  302. Text string
  303. Radio *RadioInfo
  304. }
  305. type QuestionInfo struct {
  306. Text string
  307. Tag int
  308. Number string
  309. AnswerArray []*AnswerInfo
  310. ChoosedAnswer *AnswerInfo
  311. }
  312. func chooseAnswer(questions []QuestionInfo, questionM map[string][]string) error {
  313. for i, _ := range questions {
  314. if len(questions[i].AnswerArray) == 0 {
  315. return errors.New(fmt.Sprintf("%s 选项为空", questions[i].Text))
  316. }
  317. str := TrimSpace(questions[i].Text)
  318. str = ParseHan(str)
  319. str = fmt.Sprintf("%d-", questions[i].Tag) + str
  320. answerstrs, _ := questionM[str]
  321. if len(answerstrs) == 0 || answerstrs[0] == "" {
  322. return errors.New(fmt.Sprintf("%s 题库答案为空", questions[i].Text))
  323. }
  324. find := false
  325. answerstr := answerstrs[0]
  326. for _, v := range questions[i].AnswerArray {
  327. if strings.Contains(answerstr, TrimSpace(v.Text)) {
  328. v.Radio.Radio.Click()
  329. find = true
  330. break
  331. }
  332. }
  333. if !find {
  334. fmt.Printf("题库中的答案:%s\n", answerstr)
  335. for _, v := range questions[i].AnswerArray {
  336. fmt.Printf("选项:%v,%v\n", v.Text, strings.Contains(answerstr, v.Text))
  337. }
  338. return errors.New(fmt.Sprintf("%s 未找到匹配答案", questions[i].Text))
  339. }
  340. }
  341. return nil
  342. }
  343. func getCourseId(src string) string {
  344. array := strings.Split(src, "/")
  345. if len(array) < 5 {
  346. return ""
  347. }
  348. return array[4]
  349. }
  350. type CourseModule struct {
  351. Name string
  352. ModuleId string
  353. }
  354. func getCourseModules(courceId string, cookie string) ([]CourseModule, error) {
  355. url := fmt.Sprintf("https://lms.ouchn.cn/api/courses/%s/modules", courceId)
  356. request, err := http.NewRequest("GET", url, nil)
  357. if err != nil {
  358. return nil, err
  359. }
  360. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  361. request.Header.Set("Cookie", cookie)
  362. 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")
  363. //request.Header.Set("Host", "chengdu.ouchn.cn")
  364. client := http.Client{}
  365. resp, err := client.Do(request)
  366. if err != nil {
  367. return nil, err
  368. }
  369. defer resp.Body.Close()
  370. respBytes, err := ioutil.ReadAll(resp.Body)
  371. if err != nil {
  372. return nil, err
  373. }
  374. ret := []CourseModule{}
  375. mos := gjson.GetBytes(respBytes, "modules").Array()
  376. for _, mo := range mos {
  377. if (len(mo.Get("syllabuses").Array())) == 0 {
  378. continue
  379. }
  380. item := CourseModule{}
  381. item.ModuleId = fmt.Sprintf("%d",mo.Get("id").Int())
  382. item.Name = mo.Get("name").String()
  383. ret = append(ret, item)
  384. }
  385. return ret, nil
  386. }
  387. type ModuleSubInfo struct {
  388. Name string
  389. Id string
  390. }
  391. type ModuleInfo struct {
  392. TxtInfos []ModuleSubInfo
  393. ExamInfos []ModuleSubInfo
  394. VedioInfos []ModuleSubInfo
  395. }
  396. func GetModuleInfo(courseId string, moduleId string, cookie string) (*ModuleInfo, error) {
  397. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/%s"+
  398. "/all-activities?module_ids=[%s]"+
  399. "&activity_types=learning_activities,exams,classrooms,"+
  400. "live_records,rollcalls&no-loading-animation=true", courseId, moduleId)
  401. request, err := http.NewRequest("GET", url, nil)
  402. if err != nil {
  403. return nil, err
  404. }
  405. request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  406. request.Header.Set("Cookie", cookie)
  407. 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")
  408. //request.Header.Set("Host", "chengdu.ouchn.cn")
  409. client := http.Client{}
  410. resp, err := client.Do(request)
  411. if err != nil {
  412. return nil, err
  413. }
  414. defer resp.Body.Close()
  415. respBytes, err := ioutil.ReadAll(resp.Body)
  416. if err != nil {
  417. return nil, err
  418. }
  419. ret := &ModuleInfo{}
  420. // 试题
  421. exams := []ModuleSubInfo{}
  422. examsArray := gjson.GetBytes(respBytes, "exams").Array()
  423. for _, v := range examsArray {
  424. item := ModuleSubInfo{}
  425. item.Name = v.Get("title").String()
  426. unikey := v.Get("unique_key").String()
  427. array := strings.Split(unikey, "-")
  428. if len(array) < 2 {
  429. continue
  430. }
  431. item.Id = array[1]
  432. exams = append(exams, item)
  433. }
  434. // 文档和视频
  435. texts := []ModuleSubInfo{}
  436. vedios := []ModuleSubInfo{}
  437. array := gjson.GetBytes(respBytes, "learning_activities").Array()
  438. for _, v := range array {
  439. stype := v.Get("type").String()
  440. item := ModuleSubInfo{}
  441. item.Name = v.Get("title").String()
  442. unikey := v.Get("unique_key").String()
  443. array := strings.Split(unikey, "-")
  444. if len(array) < 2 {
  445. continue
  446. }
  447. item.Id = array[1]
  448. if stype == "page" {
  449. texts = append(texts, item)
  450. } else if stype == "online_video" {
  451. vedios = append(vedios, item)
  452. }
  453. }
  454. ret.ExamInfos = exams
  455. ret.TxtInfos = texts
  456. ret.VedioInfos = vedios
  457. return ret, nil
  458. }
  459. func MakeTxtUrl(courseId string, moduleId string, id string) string {
  460. return fmt.Sprintf("https://lms.ouchn.cn/course/%s/learning-activity/full-screen#/%s", courseId, id)
  461. }
  462. func MakeExamUrl(courseId string, moduleId string, id string) string {
  463. return fmt.Sprintf("https://lms.ouchn.cn/course/%s/learning-activity/full-screen#/exam/%s", courseId, id)
  464. }
  465. func VedioCompleteHandle(cookie string, id string)(error) {
  466. count := 0
  467. start, end := int64(0), int64(1000)
  468. for count < 5 {
  469. finish, err := VedioComplete(cookie, id, start, end)
  470. if err != nil {
  471. return err
  472. }
  473. if finish < 100 {
  474. start = end
  475. end += 1000
  476. count++
  477. continue
  478. }
  479. return nil
  480. }
  481. return errors.New("视频处理失败")
  482. }
  483. func VedioComplete(cookie string, id string, start, end int64) (int64, error) {
  484. url := fmt.Sprintf("https://lms.ouchn.cn/api/course/activities-read/%s", id)
  485. //fmt.Printf("get sections:%v,%v\n", str, cookie)
  486. m := map[string]interface{}{
  487. "start":start,
  488. "end":end,
  489. }
  490. content, _ := json.Marshal(m)
  491. reader := bytes.NewReader(content)
  492. fmt.Printf("post data:%s\n", content)
  493. request, err := http.NewRequest("POST", url, reader)
  494. if err != nil {
  495. return 0, err
  496. }
  497. request.Header.Set("Content-Type", "application/json")
  498. request.Header.Set("Cookie", cookie)
  499. 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")
  500. client := http.Client{}
  501. resp, err := client.Do(request)
  502. if err != nil {
  503. return 0, err
  504. }
  505. defer resp.Body.Close()
  506. respBytes, err := ioutil.ReadAll(resp.Body)
  507. if err != nil {
  508. return 0, err
  509. }
  510. //fmt.Printf("get bytes:%s\n", respBytes)
  511. if gjson.GetBytes(respBytes, "activity_id").Int() == 0 {
  512. return 0, errors.New("视频处理失败")
  513. }
  514. return gjson.GetBytes(respBytes, "data").Get("completeness").Int(), nil
  515. }
  516. func examHandleJump(wd selenium.WebDriver, courseId, moduleId, id string) error {
  517. url := MakeExamUrl(courseId, moduleId, id)
  518. wd.Get(url)
  519. time.Sleep(2*time.Second)
  520. return nil
  521. }
  522. func examHandleStart(wd selenium.WebDriver) error {
  523. // 点击答题按钮
  524. button, err := wd.FindElement(selenium.ByXPATH, "//a[@class='button button-green take-exam ng-scope']")
  525. if err != nil {
  526. return err
  527. }
  528. button.Click()
  529. time.Sleep(1*time.Second)
  530. active, _ := wd.ActiveElement()
  531. // 勾选同意框
  532. chbox, _ := active.FindElement(selenium.ByXPATH, "//input[@type='checkbox' and @name='confirm']")
  533. chbox.Click()
  534. time.Sleep(1*time.Second)
  535. // 点击答题
  536. button, _ = active.FindElement(selenium.ByXPATH, "//button[text()='开始答题' or text()='继续答题']")
  537. button.Click()
  538. time.Sleep(5*time.Second)
  539. return nil
  540. }
  541. func examHandleQuestion(wd selenium.WebDriver) ([]QuestionInfo, error) {
  542. questionClasses,_ := wd.FindElements(selenium.ByXPATH, "//li[@class='subject ng-scope text']")
  543. ret := []QuestionInfo{}
  544. for _, v := range questionClasses {
  545. p, err := v.FindElement(selenium.ByXPATH, "./div/div/div/span/p[@class='ng-scope']")
  546. if err != nil {
  547. return nil, err
  548. }
  549. txt, _ := p.Text()
  550. fmt.Printf("题类%s\n", txt)
  551. className := ""
  552. tag := 0
  553. // 题目类型
  554. if strings.Contains(txt, "选择") {
  555. className = "subject ng-scope single_selection"
  556. tag = 1
  557. } else if strings.Contains(txt, "判断") {
  558. className = "subject ng-scope true_or_false"
  559. tag = 2
  560. }
  561. if className == "" {
  562. continue
  563. }
  564. p = v
  565. path := fmt.Sprintf("./following-sibling::li[@class='%s']", className)
  566. for {
  567. item := QuestionInfo{Tag:tag}
  568. // 题目
  569. p, err = p.FindElement(selenium.ByXPATH, path)
  570. if err != nil {
  571. fmt.Printf("err:%v\n", err)
  572. break
  573. }
  574. x, err := p.FindElement(selenium.ByXPATH, "./div/div/div/span/p[@class='ng-scope']")
  575. if err != nil {
  576. break
  577. }
  578. txt, _ := x.Text()
  579. // 题目描述
  580. item.Text = txt
  581. // 题目选项
  582. ol, _ := p.FindElement(selenium.ByXPATH, "./div/div/ol")
  583. lis, _ := ol.FindElements(selenium.ByXPATH, "./li")
  584. for _, li := range lis {
  585. radio, err := li.FindElement(selenium.ByXPATH, "./label/span/input")
  586. if err != nil {
  587. break
  588. }
  589. answerIterm := AnswerInfo{Radio:&RadioInfo{Radio:radio}}
  590. if className == "subject ng-scope true_or_false" {
  591. answer, err := li.FindElement(selenium.ByXPATH, "./label/div/span")
  592. if err != nil {
  593. break
  594. }
  595. txt, _ := answer.Text()
  596. answerIterm.Text = txt
  597. } else {
  598. answer,err := li.FindElement(selenium.ByXPATH, "./label/div/span/p")
  599. if err != nil {
  600. break
  601. }
  602. txt, _ := answer.Text()
  603. answerIterm.Text = txt
  604. }
  605. item.AnswerArray = append(item.AnswerArray, &answerIterm)
  606. }
  607. ret = append(ret, item)
  608. }
  609. }
  610. return ret, nil
  611. }
  612. func examHandleMatch(questions []QuestionInfo, m map[string][]string) error {
  613. return chooseAnswer(questions, m)
  614. }
  615. func examHandleSubmit(wd selenium.WebDriver) error {
  616. // 交卷
  617. button1,err := wd.FindElement(selenium.ByXPATH, "//a[@class='button button-green ng-scope' and text()='交卷']")
  618. if err != nil {
  619. return err
  620. }
  621. button1.Click()
  622. time.Sleep(2*time.Second)
  623. // 弹窗中的确定
  624. active, _ := wd.ActiveElement()
  625. button2s, err := active.FindElements(selenium.ByXPATH, "//button[text()='确定']")
  626. if err != nil {
  627. return err
  628. }
  629. for _, button2 := range button2s {
  630. txt, _ := button2.Text()
  631. if txt == "确定" {
  632. button2.Click()
  633. break
  634. }
  635. }
  636. return errors.New("交卷未找到确定按钮")
  637. }
  638. func examHandle(wd selenium.WebDriver, courseId, moduleId, id string, qustionM map[string][]string) error {
  639. // 跳转页面
  640. if err := examHandleJump(wd, courseId, moduleId, id); err != nil {
  641. return err
  642. }
  643. // 点击答题按钮
  644. if err := examHandleStart(wd); err != nil {
  645. return err
  646. }
  647. // 收集题目和答案选项
  648. questions, err := examHandleQuestion(wd)
  649. if err != nil {
  650. return err
  651. }
  652. // 匹配题库并选择答案
  653. if err := examHandleMatch(questions, qustionM); err != nil {
  654. return err
  655. }
  656. // 提交试卷
  657. return examHandleSubmit(wd)
  658. }
  659. func CourseHandle(wd selenium.WebDriver, username string) error {
  660. title, err := wd.Title()
  661. if title == "" {
  662. return nil
  663. }
  664. array := strings.Split(title, "-")
  665. title = array[0]
  666. title = strings.TrimSpace(title)
  667. if !checkCourseExist(config.Conf.Courses, title) {
  668. fmt.Println("不执行课程:", title)
  669. time.Sleep(3)
  670. return nil
  671. }
  672. cookie, err := getCookies(wd)
  673. if err != nil {
  674. return err
  675. }
  676. defer func() {
  677. if strings.Contains(title, "习近平新时代中国特色社会主义思想") {
  678. panic("over")
  679. }
  680. }()
  681. fmt.Printf("***************正在处理%s课程%s\n", username, title)
  682. questionM, err := LoadXlsx(fmt.Sprintf("%s.xlsx", title))
  683. if err != nil {
  684. return err
  685. }
  686. // 获取课程id
  687. src, _ := wd.CurrentURL()
  688. courseId := getCourseId(src)
  689. if courseId == "" {
  690. return errors.New("课程id获取失败")
  691. }
  692. fmt.Printf("课程id:%s\n", courseId)
  693. // 获取课程所有栏目
  694. modules, err := getCourseModules(courseId, cookie)
  695. if err != nil {
  696. return errors.New(fmt.Sprintf("获取module信息失败:%s", err.Error()))
  697. }
  698. for _, module := range modules {
  699. fmt.Printf("***课程:%s 栏目:%s\n", title, module.Name)
  700. // 获取某栏目下的试题和视频数据
  701. moduleInfo, err := GetModuleInfo(courseId, module.ModuleId, cookie)
  702. if err != nil {
  703. return errors.New(fmt.Sprintf("获取module(%s)详细信息失败:%s", module.Name, err.Error()))
  704. }
  705. // 处理网页数据
  706. if true {
  707. for _, info := range moduleInfo.TxtInfos {
  708. fmt.Printf("*****正在处理网页数据:%s\n", info.Name)
  709. url := MakeTxtUrl(courseId, module.ModuleId, info.Id)
  710. wd.Get(url)
  711. time.Sleep(1*time.Second)
  712. }
  713. }
  714. if true {
  715. // 处理视频数据
  716. for _, info := range moduleInfo.VedioInfos {
  717. fmt.Printf("*****正在处理视频数据:%s\n", info.Name)
  718. err := VedioCompleteHandle(cookie, info.Id)
  719. if err != nil {
  720. fmt.Printf(" 视频%s处理失败(%s)\n", info.Name, err.Error())
  721. }
  722. time.Sleep(1*time.Second)
  723. }
  724. }
  725. if true {
  726. // 处理试题数据
  727. for _, info := range moduleInfo.ExamInfos {
  728. fmt.Printf("*****正在处理试题数据:%s\n", info.Name)
  729. err := examHandle(wd, courseId, module.ModuleId, info.Id, questionM)
  730. if err != nil {
  731. fmt.Printf(" 试题%s处理失败(%s)\n", info.Name, err.Error())
  732. }
  733. time.Sleep(1*time.Second)
  734. }
  735. }
  736. }
  737. return nil
  738. }