Created
January 14, 2016 00:44
-
-
Save sebastianvera/42affe565f2c71f6c67c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "io/ioutil" | |
| "log" | |
| "net/http" | |
| "net/http/cookiejar" | |
| "net/url" | |
| "os" | |
| "strconv" | |
| "strings" | |
| "github.com/PuerkitoBio/goquery" | |
| flag "github.com/ogier/pflag" | |
| ) | |
| const ( | |
| loginURL string = "http://alumno.udp.cl/Login.aspx" | |
| gradesURL = "http://alumno.udp.cl/Pages/MiRegistroAcademico.aspx" | |
| version = "0.0.1" | |
| ) | |
| type User struct { | |
| Username string | |
| Password string | |
| } | |
| type Scraper struct { | |
| user User | |
| HttpClient *http.Client | |
| } | |
| func main() { | |
| flag.Usage = func() { | |
| printUsage() | |
| os.Exit(2) | |
| } | |
| var versionCommand bool | |
| flag.BoolVarP(&versionCommand, "version", "v", false, "") | |
| flag.Parse() | |
| if flag.Arg(0) == "help" { | |
| fmt.Fprintf(os.Stdout, usageMessage()) | |
| os.Exit(0) | |
| } | |
| if versionCommand || flag.Arg(0) == "version" { | |
| printVersion() | |
| os.Exit(0) | |
| } | |
| if len(flag.Args()) < 2 { | |
| fmt.Println("Error: You need to suply a username and password.") | |
| printUsage() | |
| os.Exit(1) | |
| } | |
| args := flag.Args() | |
| username := args[0] | |
| password := args[1] | |
| user := &User{Username: username, Password: password} | |
| scraper := getScraper(user) | |
| res, err := scraper.getExamResult() | |
| if err != nil { | |
| log.Panic(err) | |
| } | |
| fmt.Println("Nota:", res) | |
| } | |
| func (s *Scraper) login() error { | |
| viewState := getViewState() | |
| data := url.Values{ | |
| "__VIEWSTATE": {viewState}, | |
| "ctl00$cphBody$txtUserCodi": {s.user.Username}, | |
| "ctl00$cphBody$txtUserPass": {s.user.Password}, | |
| "ctl00$cphBody$btnLogin": {"Entrar"}, | |
| } | |
| req, _ := http.NewRequest("POST", loginURL, bytes.NewBufferString(data.Encode())) | |
| req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | |
| req.Header.Add("Accept", "*/*") | |
| req.Header.Add("Cookie", "1=1") | |
| req.Header.Add("User-Agent", "NewID") | |
| req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) | |
| response, err := s.HttpClient.Do(req) | |
| defer response.Body.Close() | |
| if err != nil { | |
| return err | |
| } | |
| body, err := ioutil.ReadAll(response.Body) | |
| if err != nil { | |
| return err | |
| } | |
| err = checkLogin(body) | |
| if err != nil { | |
| return err | |
| } | |
| return nil | |
| } | |
| func (s *Scraper) getExamResult() (result string, err error) { | |
| err = s.login() | |
| if err != nil { | |
| return "", err | |
| } | |
| pageURL, err := url.Parse(loginURL) | |
| if err != nil { | |
| return "", err | |
| } | |
| client := s.HttpClient | |
| req, _ := http.NewRequest("GET", gradesURL, nil) | |
| req.Header.Add("Cookie", cookiesToString(client, pageURL)) | |
| response, err := client.Do(req) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| doc, err := goquery.NewDocumentFromResponse(response) | |
| defer response.Body.Close() | |
| if err != nil { | |
| log.Fatal(err) | |
| return "", err | |
| } | |
| doc.Find("table#ctl00_cphBody_tblRegistroAcademico tr").Each(func(i int, s *goquery.Selection) { | |
| nodes := s.Find("td:contains('EXAMEN')") | |
| if nodes.Size() > 0 { | |
| gradeNode := s.Find("td:nth-child(4)") | |
| if gradeNode.Size() > 0 { | |
| result = gradeNode.Text() | |
| } else { | |
| err = fmt.Errorf("Scraping error, HTML has changed, grade not found") | |
| } | |
| } | |
| }) | |
| return result, err | |
| } | |
| func getScraper(u *User) *Scraper { | |
| s := &Scraper{} | |
| s.user = *u | |
| cookieJar, _ := cookiejar.New(nil) | |
| s.HttpClient = &http.Client{Jar: cookieJar} | |
| return s | |
| } | |
| func getViewState() string { | |
| response, err := http.Get(loginURL) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| doc, err := goquery.NewDocumentFromResponse(response) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| s := doc.Find("input[name=__VIEWSTATE]") | |
| val, exists := s.Attr("value") | |
| if !exists { | |
| log.Fatal(fmt.Errorf("VIEW STATE NOT FOUND")) | |
| } | |
| return val | |
| } | |
| func cookiesToString(c *http.Client, url *url.URL) string { | |
| var result []string | |
| for _, cookie := range c.Jar.Cookies(url) { | |
| result = append(result, cookie.String()) | |
| } | |
| return strings.Join(result, "; ") | |
| } | |
| func emptyUsernameBytes() []byte { | |
| return []byte("Ingrese su identificador de usuario") | |
| } | |
| func emptyPasswordBytes() []byte { | |
| return []byte("Ingrese su contraseña") | |
| } | |
| func wrongUsernameBytes() []byte { | |
| return []byte("USUARIO INGRESADO NO CORRESPONDE, POR FAVOR REINGRESE NUEVAMENTE") | |
| } | |
| func wrongPasswordBytes() []byte { | |
| return []byte("PASSWORD INCORRECTO, POR FAVOR INTENTE NUEVAMENTE") | |
| } | |
| func checkLogin(body []byte) error { | |
| if bytes.Contains(body, emptyUsernameBytes()) { | |
| return fmt.Errorf("username can't be blank") | |
| } | |
| if bytes.Contains(body, emptyPasswordBytes()) { | |
| return fmt.Errorf("password can't be blank") | |
| } | |
| if bytes.Contains(body, wrongUsernameBytes()) { | |
| return fmt.Errorf("wrong username") | |
| } | |
| if bytes.Contains(body, wrongPasswordBytes()) { | |
| return fmt.Errorf("wrong password") | |
| } | |
| return nil | |
| } | |
| func printUsage() { | |
| fmt.Fprintf(os.Stdout, usageMessage()) | |
| } | |
| func usageMessage() string { | |
| appName := os.Args[0] | |
| usage := `usage: %s [--version] [--help] username password | |
| Example: | |
| $ %s 17942471-1 mysecretpassword | |
| Additional Information: | |
| - The same username format as alumno.udp.cl is required | |
| - No, is not my password. | |
| ` | |
| return fmt.Sprintf(usage, appName, appName) | |
| } | |
| func printVersion() { | |
| fmt.Printf("%s version %s\n", os.Args[0], version) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Close that response body dude.