Scalaで集合知プログラミング その7
Scala de 集合知プログラミングの第2章の続きをやっていきますよ(`・ω・´)
今回はdel.icio.usのAPIを使って集合知的ゴニョゴニョをしていきますよ
del.icio.usのリンクを推薦するシステムを作る
前回までにやったアイテムの推薦、という内容を実際のサービスを利用したものとして発展させていきたいと思います。
今回サービスとして利用するのはオンラインブックマークのdel.icio.usデス
...と、本が出版されてからサービス名が地味に変わっている気がするのですが、まあ(゚ε゚)キニシナイ!!方向で
del.icio.usのAPI
とりあえずDeli.cio.usをPythonで利用しやすくするためにpydeliciousを導入してやります
pydelicious google code
導入はeasy installであっさりと
$sudo easy_install pydelicious
それでは使ってみますかね(`・ω・´)
>>> import pydelicious # 注目されているprogrammingタグのついたブックマーク一覧を取得しました >>> pydelicious.get_popular(tag='programming') [{'extended': '', 'description': u'Using the Cython Compiler to write fast Python code', 'tags': u'python programming c compiler performance c++ optimization software presentation cython', 'url': u'http://www.behnel.de/cython200910/talk.html', 'user': u'remosu', 'dt': u'2009-10-30T13:32:03Z'}, {'extended': '', 'description': u'ontwik | Lectures, Screencasts and conferences for real web developers & designers', 'tags': u'screencast conferences webdev webdesign video resources programming education learning development', 'url': u'http://ontwik.com/', 'user': u'certainly', 'dt': u'2010-10-16T14:47:50Z'}, {'extended': '', 'description': u'Free ebook: Programming Windows Phone 7, by Charles Petzold', 'tags': u'free silverlight programming phone windows windowsphone7 microsoft development mobile pdf', 'url': u'http://blogs.msdn.com/b/microsoft_press/archive/2010/10/28/free-ebook-programming-windows-phone-7-by-charles-petzold.aspx', 'user': u'derek.lakin', 'dt': u'2010-10-28T16:43:38Z'}, {'extended': '', 'description': u'What Every Computer Scientist Should Know About Floating-Point Arithmetic', 'tags': u'programming math', 'url': u'http://docs.sun.com/source/806-3568/ncg_goldberg.html', 'user': u'agles', 'dt': u'2004-03-25T14:44:49Z'}, {'extended': '', 'description': u"Making Asynchronous Programming Easy - Somasegar's WebLog - Site Home - MSDN Blogs", 'tags': u'programming asynchronous c# vb.net', 'url': u'http://blogs.msdn.com/b/somasegar/archive/2010/10/28/making-asynchronous-programming-easy.aspx', 'user': u'andyparkhill', 'dt': u'2010-10-28T22:35:48Z'}, {'extended': '', 'description': u'Ksplice \xbb Hosting backdoors in hardware - System administration and software blog', 'tags': u'security kernel hardware linux hacking programming drivers unix', 'url': u'http://blog.ksplice.com/2010/10/hosting-backdoors-in-hardware/', 'user': u'handshandy', 'dt': u'2010-10-27T17:35:55Z'}, {'extended': '', 'description': u'IMPURE', 'tags': u'data information interaction interface programming software technology visualization tool', 'url': u'http://www.impure.com/', 'user': u'creoquecreo', 'dt': u'2010-10-26T10:02:32Z'}, {'extended': '', 'description': u'Phono - jQuery Phone Plugin', 'tags': u'jquery phone programming webdesign', 'url': u'http://www.phono.com/', 'user': u'socialpest', 'dt': u'2005-03-03T10:23:43Z'}]
出版後しばらく立っているのでAPI廃止されてたらどうしよう…と思ってたのですが、何とか使えるみたいですね(´・ω・`)
ただし、仕様変更はあったらしくどうやら取得件数制限がかかるようになってますね(´・ω・`)デフォルトだと15件までみたい…って、負荷を考えればそりゃそうだわな
ちなみに取得用urlにパラメータをcount=<件数>で指定すると最大100件までは取れるみたいですが…pydeliciousで今回使用するメソッドにはそういうオプションがない…だ..と...orz
うーん、大規模なデータが取得できないとなると余り分析に意味は出なそうなのですが…まあ、今回はコードを書くのが第一目的なのでとりあえずやってみマス
Scalaにはモジュールがない…よ..な、そりゃ
…と、Pythonでは本書の指示通りにAPIを使える見込みが立ったのですが、本特集の本題であるScalaでやるという目的を達成できるような、Scala de deliciousなモジュールは提供されていなさそう(´・ω・`)
いや、あるのかも知れないけど正直探すのが大変すぐるので、今回のサンプルの中で利用しているpydeliciousのメソッドを車輪の再発明してみますよ
- get_popular
- get_userposts
- geturlposts
get_popularメソッド
まずは最新のpopularな投稿リストを取得するメソッドです
呼び出しはこんな感じですね
pydelicious.get_popular(tag='programming')
タグを引数として渡すことで、指定タグの付けられたもののみを取得します
pydelicious.get_popularで実際に取得できるデータはこんな感じですね
# 全部表示するとあれなので2件だけ表示します >>> pydelicious.get_popular(tag='programming')[0:2] # 出力データです >>> pydelicious.get_popular(tag='programming')[0:2] [{'extended': '', 'description': u'Using the Cython Compiler to write fast Python code', 'tags': u'python programming c compiler performance c++ optimization software presentation cython', 'url': u'http://www.behnel.de/cython200910/talk.html', 'user': u'remosu', 'dt': u'2009-10-30T13:32:03Z'}, {'extended': '', 'description': u'ontwik | Lectures, Screencasts and conferences for real web developers & designers', 'tags': u'screencast conferences webdev webdesign video resources programming education learning development', 'url': u'http://ontwik.com/', 'user': u'certainly', 'dt': u'2010-10-16T14:47:50Z'}]
上記の結果を見ると、戻りはこんな感じのキーで構成されたMapデータで返ってくれば良いみたいですね
- extended
- description
- tags
- url
- user
- dt
また、pydeliciousのコードを眺めてみると、内部的には以下のURLに接続してRSS情報を引っ張るような処理をしているみたいデス
http://delicious.com/rss/popular/<タグ名>
programmingタグがつくとたとえばこんな感じ
http://delicious.com/rss/popular/programming
そんなわけでURLからフィードを取得して、適当に要素を再構成するコードを書いてみました(´・ω・`)
import scala.io.Source import scala.xml.{XML, NodeSeq} import scala.xml.parsing.XhtmlParser // 取得用のURLを定義します val rss_url = "http://delicious.com/rss/" // popularなフィードを取得するメソッドです def get_popular(tag:String = ""):List[Map[String, String]] = { // URLから提供フィードを取得してXMLの"item"要素を取得します val source = Source.fromURL(rss_url + "popular/"+ tag) val feeds = XhtmlParser(source) \\ "item" // 取得した各フィードの内容を再構成します feeds.map(feed => // extendedについては該当のものが見当たらないので // 適当に空白扱ってます(´・ω・`)まあ、今回は使わないので… // 残りの要素は多分これでいいはずデス Map("extended" -> (feed \\ "extended").text, "description" -> (feed \\ "title").text, "tags" -> (feed \\ "subject").text, "url" -> (feed \\ "link").text, "user" -> (feed \\ "creator").text, "dt" -> (feed \\ "date").text ) ).toList }
参考にしたのはこちら
get_userpostsメソッド
これは与えられたユーザのポストを取得するメソッドです
呼び出しはこんな感じですね
pydelicious.get_userposts(user='tsegaran')
ユーザ名を引数として渡してやります(`・ω・´)ちなみにサンプルの"tsegaran"は集合知プログラミングの筆者さんデス
pydelicious.get_userpostsで実際に取得できるデータはこんな感じですね
# 全部表示するとあれなので2件だけ表示します >>> pydelicious.get_userposts(user='tsegaran')[0:2] # 出力データです [{'extended': '', 'description': u'How to Build a 8\xd78 RGB LED Matrix with PWM using an Arduino | Francis ...', 'tags': u'arduino shiftregister', 'url': u'http://francisshanahan.com/index.php/2009/how-to-build-a-8x8x3-led-matrix-with-pwm-using-an-arduino/', 'user': u'tsegaran', 'dt': u'2010-09-09T00:19:54Z'}, {'extended': '', 'description': u'shogun | A Large Scale Machine Learning Toolbox', 'tags': u'python svm machinelearning', 'url': u'http://www.shogun-toolbox.org/', 'user': u'tsegaran', 'dt': u'2010-07-15T23:09:37Z'}]
こちらのほうも戻りはこんな感じのキーで構成されてればいいみたいデス(´・ω・`)
- extended
- description
- tags
- url
- user
- dt
pydeliciousのコードからでは、以下のURLに接続してRSS情報を引っ張ってますね
http://delicious.com/rss/<ユーザ名>
tsegaranのフィードを引っ張る場合はこんな感じ
http://delicious.com/rss/popular/tsegaran
以上の情報からこちらの方もScala版を作成しますデス
// 取得用のURLを定義します val rss_url = "http://delicious.com/rss/" // userのフィードを取得するメソッドです def get_userposts(user:String):List[Map[String, String]] = { // URLから提供フィードを取得してXMLの"item"要素を取得します val source = Source.fromURL(rss_url + user) val feeds = XhtmlParser(source) \\ "item" // 取得した各フィードの内容を再構成します feeds.map(feed => Map("extended" -> (feed \\ "extended").text, "description" -> (feed \\ "title").text, "tags" -> (feed \\ "subject").text, "url" -> (feed \\ "link").text, "user" -> (feed \\ "creator").text, "dt" -> (feed \\ "date").text ) ).toList }
get_urlpostsメソッド
最後に与えられたURLに関する投稿を取得するget_urlpostsメソッドです
呼び出しはこんな感じですね
pydelicious.get_urlposts(url='http://google.co.jp/')
urlを引数として渡してやります
pydelicious.get_urlpostsで実際に取得できるデータはこんな感じですね
# 全部表示するとあれなので2件だけ表示します >>> pydelicious.get_urlposts(url='http://google.co.jp/')[0:2] # 出力データです [{'extended': '', 'description': u'[from jayce_fun] Google', 'tags': u'google', 'url': u'http://www.google.co.jp/', 'user': u'jayce_fun', 'dt': u'2011-01-16T06:03:38Z'}, {'extended': '', 'description': u'[from largeleft] Google', 'tags': u'google', 'url': u'http://www.google.co.jp/', 'user': u'largeleft', 'dt': u'2011-01-14T13:56:33Z'}]
こちらのほうも戻りはこんな感じのキーで構成されてればいいみたいデス(´・ω・`)
- extended
- description
- tags
- url
- user
- dt
pydeliciousのコードからでは、以下のURLに接続してRSS情報を引っ張ってますね
http://google.co.jp/のフィードを引っ張る場合はこんな感じ
http://delicious.com/rss/url/ec16dada27c84a7c23fff9c27265355b
以上の情報からこちらの方もScala版を作成しますデス
import java.security.MessageDigest // 取得用のURLを定義します val rss_url = "http://delicious.com/rss/" // urlのフィードを取得するメソッドです def get_urlposts(url:String):List[Map[String, String]] = { // 引数として渡されたurlをハッシュ化します val hash_url = MessageDigest.getInstance("MD5"). digest(url.getBytes()).map((n : Byte) => "%02x".format(n & 0xff)).mkString // URLから提供フィードを取得してXMLの"item"要素を取得します val source = Source.fromURL(rss_url + "url/"+ hash_url) val feeds = XhtmlParser(source) \\ "item" // 取得した各フィードの内容を再構成します feeds.map(feed => Map("extended" -> (feed \\ "extended").text, "description" -> (feed \\ "title").text, "tags" -> (feed \\ "subject").text, "url" -> (feed \\ "link").text, "user" -> (feed \\ "creator").text, "dt" -> (feed \\ "date").text ) ).toList }
オブジェクト化
3つの各処理とも結構同じような処理をしているので共通化して、かつpydeliciousと同じように使用できるようにオブジェクトとしてまとめてしまいます
// パッケージ化します package org.plasticscafe.collective.recommend import scala.io.Source import scala.xml.{XML, NodeSeq} import scala.xml.parsing.XhtmlParser object ScalaDelicious { // 取得用のURLを定義します val rss_url = "http://delicious.com/rss/" // popularなフィードを取得するメソッドです def get_popular(tag:String = ""):List[Map[String, String]] = { // urlからフィードを取得します get_feed(rss_url + "popular/"+ tag) } // userのフィードを取得するメソッドです def get_userposts(user:String):List[Map[String, String]] = { get_feed(rss_url + user) } // urlのフィードを取得するメソッドです def get_urlposts(url:String):List[Map[String, String]] = { // 引数として渡されたurlをハッシュ化します val hash_url = MessageDigest.getInstance("MD5"). digest(url.getBytes()).map((n : Byte) => "%02x".format(n & 0xff)).mkString get_feed(rss_url + "url/"+ hash_url) } // フィードを取得する共通処理です private def get_feed(url:String):List[Map[String, String]] = { // URLから提供フィードを取得してXMLの"item"要素を取得します val source = Source.fromURL(url) val feeds = XhtmlParser(source) \\ "item" // 取得した各フィードの内容を再構成します feeds.map(feed => Map("extended" -> (feed \\ "extended").text, "description" -> (feed \\ "title").text, "tags" -> (feed \\ "subject").text, "url" -> (feed \\ "link").text, "user" -> (feed \\ "creator").text, "dt" -> (feed \\ "date").text ) ).toList } }
うん、なんとかまとまったみたいなので試しに動かしてみますよ(`・ω・´)
// インポートします scala> import org.plasticscafe.collective.recommend.ScalaDelicious import org.plasticscafe.collective.recommend.ScalaDelicious // get_popularを使ってみます scala> ScalaDelicious.get_popular("programming") res2: List[Map[String,String]] = List(Map((extended,), (dt,2009-10-30T13:32:03Z), (url,http://www.behnel.de/cython200910/talk.html), (description,Using the Cython Compiler to write fast Python code), (tags,python programming c compiler performance c++ optimization software presentation cython), (user,remosu)), Map((extended,), (dt,2010-10-16T14:47:50Z), (url,http://ontwik.com/), (description,ontwik | Lectures, Screencasts and conferences for real web developers & designers), (tags,screencast conferences webdev webdesign video resources programming education learning development), (user,certainly)), Map((extended,), (dt,2010-10-28T16:43:38Z), (url,http://blogs.msdn.com/b/microsoft_press/archive/2010/10/28/free-ebook-programming-windows-phone-7-by-charles-petzold.aspx), (description,Free ... // get_userpostsを使ってみます scala> ScalaDelicious.get_userposts("tsegaran") res3: List[Map[String,String]] = List(Map((extended,), (dt,2010-09-09T00:19:54Z), (url,http://francisshanahan.com/index.php/2009/how-to-build-a-8x8x3-led-matrix-with-pwm-using-an-arduino/), (description,How to Build a 8×8 RGB LED Matrix with PWM using an Arduino | Francis ...), (tags,arduino shiftregister), (user,tsegaran)), Map((extended,), (dt,2010-07-15T23:09:37Z), (url,http://www.shogun-toolbox.org/), (description,shogun | A Large Scale Machine Learning Toolbox), (tags,python svm machinelearning), (user,tsegaran)), Map((extended,), (dt,2010-07-15T23:08:55Z), (url,http://mypoyozo.com/#tour), (description,Poyozo | Make Life Make Sense), (tags,data personal visualization), (user,tsegaran)), Map((extended,), (dt,2010-07-08T21:43:23Z), (url,http://www.sagenb.org/), (description,Sign in -... // get_urlpostsを使ってみます scala> ScalaDelicious.get_urlposts("http://google.co.jp/") res4: List[Map[String,String]] = List(Map((extended,), (dt,2011-01-16T06:03:38Z), (url,http://www.google.co.jp/), (description,[from jayce_fun] Google), (tags,google), (user,jayce_fun)), Map((extended,), (dt,2011-01-14T13:56:33Z), (url,http://www.google.co.jp/), (description,[from largeleft] Google), (tags,google), (user,largeleft)), Map((extended,), (dt,2011-01-14T02:51:19Z), (url,http://www.google.co.jp/), (description,[from ttake2000] Google), (tags,google), (user,ttake2000)), Map((extended,), (dt,2011-01-12T00:08:33Z), (url,http://google.co.jp/), (description,[from nakapond] Google), (tags,), (user,nakapond)), Map((extended,), (dt,2011-01-10T22:20:55Z), (url,http://www.google.co.jp/), (description,[from jospefois_2sc372] Google), (tags,), (user,jospefois_2sc372)), Map((extended,), (...
おお!できました(`・ω・´)