Grab Packt With Racket
Automatically claim Packt's free e-books with a Racket program.
A nice fellow, wrote some Javascript code (nodejs) to be able to automatically claim a book, for example by calling the code from a cron job. He named the code "grab_packt" and has made it available on github.
To me it is not fair to have only a Javascript version! So I decided to give it a go with different languages. This time I'll present the version written with Racket.
For this to work you need to replace
Introduction
Packt Publishing has launched a new programme called "free learning offer"; each day they put an e-book for free on their website to download: one needs to login to Packt's website and click on the "claim your free book" link.A nice fellow, wrote some Javascript code (nodejs) to be able to automatically claim a book, for example by calling the code from a cron job. He named the code "grab_packt" and has made it available on github.
To me it is not fair to have only a Javascript version! So I decided to give it a go with different languages. This time I'll present the version written with Racket.
The Code
The code is in public domain. You can get it directly from is.gd/iRF96N or from my github gist.For this to work you need to replace
YOUR-EMAIL-ADDRESS
(line 24) and YOUR-PASSWORD
(line 25) with reasonable values. If you wish to run this from shell/cron remember to add #! /usr/bin/env racket
to the top of the file. #lang racket
;;;
;;; Author: Bahman Movaqar
;;;
(require net/url)
(require sxml)
(require net/uri-codec)
(require net/http-client)
(require (planet neil/html-parsing))
(require net/cookies)
;;;
(define LOGIN-ERR-MSG
"you entered an invalid email address and password combination")
(define XPATH-LOGIN-ERR "//div[contains(@class, 'error')][contains(., '~a')]")
(define XPATH-FORM-BUILD-ID
"//input[@type='hidden'][substring(@id, 1, 4)='form'][substring(@value, 1, 4)='form']//@value")
(define XPATH-BOOK-URL "//a[contains(@class, 'twelve-days-claim')]//@href")
(current-cookie-jar (new list-cookie-jar%))
;;;
(define (login-params form-build-id)
(alist->form-urlencoded
(list (cons 'email "YOUR-EMAIL-ADDRESS")
(cons 'password "YOUR-PASSWORD")
(cons 'op "Login")
(cons 'form_id "packt_user_login_form")
(cons 'form_build_id form-build-id))))
;;;
(define (url->xexp a-url)
(html->xexp (call/input-url
a-url
get-pure-port
(λ (in-port) (port->string in-port)))))
;;;
(define (req-with-cookies conn uri method data headers url)
(http-conn-send! conn uri #:method method #:data data #:headers headers)
(let-values ([(status-line header-list inport) (http-conn-recv! conn)])
(extract-and-save-cookies! header-list url)
inport))
;;;
(define (login-to-packt form-build-id fl-url)
(let* [(in-port (req-with-cookies
(http-conn-open "www.packtpub.com" #:ssl? #t)
"/packt/offers/free-learning"
#"POST"
(login-params form-build-id)
`("Content-Type: application/x-www-form-urlencoded")
fl-url))
(html-doc (html->xexp (port->string in-port)))
(error-xpath (format XPATH-LOGIN-ERR LOGIN-ERR-MSG))
(login-success? (equal? '() ((txpath error-xpath) html-doc)))]
(if login-success? #t (error "Login failed"))))
;;;
(define (claim-the-book book-uri fl-url)
(req-with-cookies (http-conn-open "www.packtpub.com" #:ssl? #t)
book-uri
#"GET"
#""
`(,(format "Cookie: ~a" (cookie-header fl-url)))
fl-url))
;;;
(let* [(fl-url (string->url "https://www.packtpub.com/packt/offers/free-learning"))
(html-doc (url->xexp fl-url))
(form-build-id (second (car ((txpath XPATH-FORM-BUILD-ID) html-doc))))
(book-uri (second (car ((txpath XPATH-BOOK-URL) html-doc))))]
(login-to-packt form-build-id fl-url)
(claim-the-book book-uri fl-url)
(println "Book is claimed."))
How To Run
Provided that you have already downloaded and installed Racket and have the installation'sbin/
directory on your PATH
, run this in a terminal to install dependencies: $ raco pkg install sxml
$ raco pkg install net-cookies
Get the code (either by copy-pasting or directly from is.gd/iRF96N), and simply run it: $ racket grab-packt.rkt
Comments
Post a Comment