package parser import ( "strings" "schedule_converter/models" "github.com/PuerkitoBio/goquery" ) // ParseGradesHTML 解析成绩页面 HTML,返回成绩条目列表。 func ParseGradesHTML(html string) ([]models.GradeEntry, error) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) if err != nil { return nil, err } var grades []models.GradeEntry doc.Find("table.bot_line").Each(func(_ int, table *goquery.Selection) { rows := table.Find("tr") if rows.Length() < 2 { return } rows.Each(func(idx int, row *goquery.Selection) { if idx == 0 { return } cells := row.Find("td") if cells.Length() < 8 { return } texts := getCellTexts(cells) if len(texts) < 9 { return } isMidterm := len(texts) == 10 entry := models.GradeEntry{ Term: safeGet(texts, 1), Code: safeGet(texts, 3), Name: safeGet(texts, 4), Credit: safeGet(texts, 7), } if isMidterm { entry.Score = safeGet(texts, 8) if len(texts) > 9 { entry.GradePoint = safeGet(texts, 9) } } else { if len(texts) >= 13 { entry.Nature = safeGet(texts, 3) entry.Category = safeGet(texts, 4) entry.Score = safeGet(texts, 12) entry.GradePoint = safeGet(texts, 9) entry.IsExam = safeGet(texts, 6) } else if len(texts) >= 10 { entry.Score = safeGet(texts, 8) entry.GradePoint = safeGet(texts, 9) } } if entry.Name != "" && len(entry.Name) >= 2 { if entry.Score == "" { entry.Score = "-" } grades = append(grades, entry) } }) }) return grades, nil } // ParseGPAHTML 解析学分绩页面 HTML,返回 GPA 概要与成绩条目列表。 func ParseGPAHTML(html string) (*models.GPASummary, []models.GradeEntry, error) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) if err != nil { return nil, nil, err } summary := &models.GPASummary{} doc.Find("span").Each(func(_ int, span *goquery.Selection) { id, _ := span.Attr("id") text := strings.TrimSpace(span.Text()) switch id { case "pjxfj": summary.AverageGPA = text case "zrs": summary.Rank = text case "kskxfj": summary.ExamTotalGPA = text case "kskxfs": summary.ExamTotalCredits = text case "kckxfj": summary.FailCredits = text case "xqs": summary.Semesters = text } }) var grades []models.GradeEntry doc.Find("table.bot_line").Each(func(_ int, table *goquery.Selection) { rows := table.Find("tr") if rows.Length() < 2 { return } rows.Each(func(idx int, row *goquery.Selection) { if idx == 0 { return } cells := row.Find("td") if cells.Length() < 10 { return } texts := getCellTexts(cells) entry := models.GradeEntry{ Term: safeGet(texts, 0), Code: safeGet(texts, 1), Name: safeGet(texts, 2), Nature: safeGet(texts, 3), Category: safeGet(texts, 4), IsExam: safeGet(texts, 6), Credit: safeGet(texts, 7), Score: safeGet(texts, 8), GradePoint: safeGet(texts, 9), } if entry.Name != "" && len(entry.Name) >= 2 { grades = append(grades, entry) } }) }) return summary, grades, nil } func getCellTexts(cells *goquery.Selection) []string { var texts []string cells.Each(func(_ int, cell *goquery.Selection) { text := strings.TrimSpace(cell.Text()) texts = append(texts, text) }) return texts } func safeGet(arr []string, idx int) string { if idx < len(arr) { return arr[idx] } return "" }