Yahoo Search APIとBeautiful Soupを使ったサンプル

ヒゲソリ前にちゃんとプレシェーブローション塗ると、明らかによく剃れることに今更気付いたbonlifeです。数年間、肌にも電気カミソリにも悪いことをしていました…。
id:kadoppeさんのこの記事Pythonネタをいつも読ませていただいているid:aodagさんのこの記事を読んで、Yahoo Search APIを使ったサンプル作りに便乗してみました。というよりid:aodagさんのスクリプトをほぼほぼ流用して、一部改変。(改悪になっていませんように。)
Yahoo Search APIに検索用の文字列を渡して、検索結果を格納した辞書のリストを返すサンプルです。

  • yahoo_search_api_sample.py
# -*- coding: utf-8 -*-

import urllib
import urllib2
from BeautifulSoup import BeautifulStoneSoup

class YahooSearchRequest (object):
    methodurls = dict(web='WebSearchService/V1/webSearch',
                   image='ImageSearchService/V1/imageSearch', 
                   video='VideoSearchService/V1/videoSearch', 
                   assist='AssistSearchService/V1/assistSearch')
    baseurl = 'http://api.search.yahoo.co.jp/'
    def __init__(self, appid, query='', mode='web'):
        self.url = '%s%s' % (self.baseurl, self.methodurls[mode])
        self.params = dict(appid=appid, query=query)
        self.result = None

    def addQuery(self, query):
        self.params['query'] = self.params['query'] + ' ' + query
        self.result = None
        return self
    
    def __getitem__(self, key):
        return self.params[key]

    def __setitem__(self, key, value):
        self.params[key] = value

    def search(self, force=False):
        if self.result is None or force:
            x = urllib2.urlopen(self.requestUrl)
            self.result = x.read()
        return self.result

    def asBeautifulSoup(self):
        result = self.search()
        return BeautifulStoneSoup(result)

    def asDictList(self):
        def xmlsoup2dic(soup):
            dic = {}
            for k, v in [(tag.name, tag.contents) for tag in soup()]:
               if len(v) == 1:
                    dic[k] = v[0]
               else:
                    dic[k] = xmlsoup2dic(soup(str(k))[0])
#                    dic[k] = [xmlsoup2dic(x) for x in soup(str(k))]
            return dic

        b = self.asBeautifulSoup()
        return [xmlsoup2dic(r) for r in b.findAll('result')]

    @property
    def queryString(self):
        return urllib.urlencode(self.params)
    
    @property
    def requestUrl(self):
        return '%s?%s' % (self.url, self.queryString)

    @property
    def query(self):
        return self.params['query']

appid = 'hogehoge' # よきにはからってください

for x in YahooSearchRequest(appid, mode='web').addQuery('中島みゆき').addQuery('生きて泳げ').addQuery('手嶌葵').asDictList():
    print '\n'.join(['%-15s : %s' % (k, v) for k, v in x.iteritems()])
    print '-----'

えぇえぇ、asDictListと最後の実行部分しか変えてませんよ。せっかくなので、全要素を取得できた方が良いかな、と思いまして、そのあたりのみ変更してみました。cacheだけがurl、sizeって2つのお子様を持っていて、そのままだと上手く扱えないので、再帰的に処理。XMLの構造があらかじめ分かってる場合はこんなことせずに個別に要素を取り出して、よりフラットな形で取り出した方が扱いやすいかもしれないですね。(上記のスクリプトだとcacheは辞書をそのまま表示してしまいます…。手抜き!)
Beautiful Soupは使い方はシンプルでありながら、結構奥が深そうでした。使いこなせると便利かもかも。XMLよりもちょっとダーティーなHTMLを扱うのに向いてるかもしれないですね。
まぁ、そんなこんなしていて思ったのは、PHPのSimpleXml関数は素人用によく出来てるな、ってことです。あれはすごく楽。楽過ぎです。それっぽいことをやってそうなサンプルを見つけたので、以下にメモしておきます。(試してません。)