Skip to content

Instantly share code, notes, and snippets.

@sebastianvera
Created January 14, 2016 00:44
Show Gist options
  • Select an option

  • Save sebastianvera/42affe565f2c71f6c67c to your computer and use it in GitHub Desktop.

Select an option

Save sebastianvera/42affe565f2c71f6c67c to your computer and use it in GitHub Desktop.
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)
}
@berkant
Copy link
Copy Markdown

berkant commented Jan 12, 2019

Close that response body dude.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment