Joe Conley Tagged scrape Random thoughts on technology, business, books, and everything in between jpc2.org/name/scrape Roll Your Own Notification Service <p>Have you ever wished you could receive customized updates whenever your favorite websites update their content? Most sites offer the means to get notified when a new blog post hits the wire or new products are added to their catalog (RSS, social media, e-mail, etc.). But what if the site doesn’t use any of these services? Or what if you only want specific updates (i.e. blog posts from author X, new products containing the name Y)? Then you’re left with only one course of action: build your own notification service!</p> <p>Armed with the mighty powers of HTML scraping, the Scala programming language, and a recurring scheduling mechanism (in this case Play’s Akka scheduler), you have all the tools you need to setup your custom notification.</p> <h2 id="my-new-ebook-notification-service">My New EBook Notification Service</h2> <p>Let’s create a notification service which let’s us know when new ebooks are available at my local digital library, <a href="http://digitallibrary.delcolibraries.org/">Delaware County Library System</a>. At the time of this writing, no such notification service exists. As I’d prefer not to miss any notifications, I’d like to setup an RSS feed. Specifically, we’ll write a process which periodically checks the digital library site for new ebooks and updates an RSS feed accordingly.</p> <h3 id="scalasbt">Scala/SBT</h3> <p>We’ll start out by creating a basic Scala application using SBT (you can checkout a skeleton project <a href="https://github.com/josephpconley/scala/tree/master/hello-world-sbt">here</a>). Let’s add the <a href="http://htmlunit.sourceforge.net/">HTMLUnit</a> and <a href="http://jesseeichar.github.io/scala-io-doc/0.2.0/index.html#!/overview">Scala IO</a> libraries to our project. We’ll use HTMLUnit to parse the HTML code of the library’s website, and we’ll use Scala IO to write our XML to file. Your build file should now look like this (assuming you named your project “ebook”):</p> <script src="https://gist.github.com/josephpconley/8584992.js"></script> <h3 id="scala-xml">Scala XML</h3> <p>Let’s start by building an abstraction for an RSS feed (you can read about the basics of RSS <a href="http://www.w3schools.com/rss/rss_reference.asp">here</a>). We’ll start with an Item case class which holds the basic properties of an RSS item and a method to generate xml. Similarly, we define the basic properties of a Feed using a trait. We’ll make this abstract in the anticipation of re-using this abstraction for other feeds.</p> <script src="https://gist.github.com/josephpconley/8590722.js"></script> <h3 id="screen-scraping-with-htmlunit">Screen Scraping with HTMLUnit</h3> <p>Let’s build a NewEBookFeed which implements Feed. When we implement the items method, we’ll use HTMLUnit to parse the HTML code from <a href="http://digitallibrary.delcolibraries.org/">Delaware County Library System</a> to find out the newest items. This requires digging around the source HTML a bit to understand the structure and find useful patterns. Basic knowledge of <a href="http://www.w3schools.com/xpath/">XPath</a> is required to leverage those patterns. After inspecting the source code and following the appropriate links, we can view the New Ebook page source and parse out the new titles, authors, and image URLs.</p> <script src="https://gist.github.com/josephpconley/8590984.js"></script> <p>That’s it! You can find my complete code as part of my <a href="https://github.com/josephpconley/scala/tree/master/scrape">scrape library</a>, specifically the com.josephpconley.books and com.josephpconley.rss packages. We can test the code by running the following:</p> <script src="https://gist.github.com/josephpconley/8591058.js"></script> <h2 id="deploy-using-play">Deploy using Play</h2> <p>Now that we have a way to generate an up-to-date RSS feed, we need a way to update our feed periodically and make it publically available to an RSS Reader like <a href="http://feedly.com">feedly</a> (my personal favorite). We could handle this a few different ways (i.e. schedule a CRON job to push a file to our Dropbox folder), however I’d like to demonstrate how to handle both the scheduling and file writing/serving using the <a href="http://www.playframework.com/">Play Framework</a>.</p> <p>Start a new Play Scala project, and either package our ebook project as a jar and copy to the lib folder, or just copy and paste the source code into the new Play project (I’ve done the former).</p> <h3 id="akka-scheduler">Akka Scheduler</h3> <p>To hook into Play’s Akka scheduler, we create a Global object in the app folder and override the onStart method, which allows us to run code once the application starts. The Akka system scheduler allows you to schedule a recurring process for a given Duration. In our case, since the site doesn’t update that frequently and we want to be respectful by not overloading the site with requests, we’ll set the duration to 12 hours.</p> <script src="https://gist.github.com/josephpconley/8605053.js"></script> <p>From there, it’s simply a matter of building out a controller with some routes to host the updated file (a straightforward exercise I’d leave to the reader). I personally included this code and hosted the RSS feeds in <a href="http://app.josephpconley.com/rss">my own Play app</a> running on Heroku.</p> <h2 id="drawbacks">Drawbacks</h2> <p>One drawback you might have noticed from this specific example is the possibility of the target site’s source code changing. We relied on very specific HTML tags, text and class attributes to query the information we needed, and should the site be re-written significantly, it’s possible that we would have to re-write our scraping code to accommodate.</p> <h2 id="conclusion">Conclusion</h2> <p>Managing the daily flow of information can be a challenge. With a little bit of coding, however, we can gain finer control over the information we consume, helping us be more productive in our everyday life.</p> Mon, 27 Jan 2014 00:00:00 +0000 jpc2.org/2014/01/27/roll-your-own-notification-service.html jpc2.org/2014/01/27/roll-your-own-notification-service.html