edo1z blog

プログラミングなどに関するブログです

BeautifulSoupの挙動が謎(GAE上でPythonでAmazonAPIを使う)

GAEでPythonとDjangoでAmazonAPI使ったWEBアプリを作成中。
AmazonAPIはPyzonを使っていて、返ってきたxmlはBeautifulSoupを使って読んでいる。
が、BeautifulSoupがよく分からず、どんなコードならいいのか全然わからない。
あれはうまくいったのに、これはうまくいかないなんて全然分からなかったので、あれとこれをメモっておく。

うまくいったコード

from pyzon import Pyzon
from BeautifulSoup import BeautifulStoneSoup
import re

access_key_id = ****
secret_access_key = ****
associate_tag = ****

#本を検索する
def book_search(bookname):
    pyzon = Pyzon(access_key_id, secret_access_key, associate_tag)
    xml = pyzon.ItemSearch(search_index='Books', Keywords=bookname,ResponseGroup='Small,Images')
    soup = BeautifulStoneSoup(xml)
    #asin,smallimage,title,author,manufacturerのリスト作る
    bookinfo_list = [[item.asin.contents[0],item.smallimage,item.title.contents[0],item.author,item.manufacturer.contents[0]] for item in soup.findAll('item')]
    #smallimage,authorはNONEの可能性あり、チェックしてNONEの対応して、余計なものを省く
    idx = 0
    for bookinfo in bookinfo_list:
        if not bookinfo[1] == 'NONE':
            p = re.compile('^(?P.+?)')
            m = p.match(str(bookinfo[1]))
            if m: bookinfo_list[idx][1] = m.group('url')
        if not bookinfo[3] == 'NONE':
            p = re.compile('^(?P.+?)')
            m = p.match(str(bookinfo[3]))
            if m: bookinfo_list[idx][3] = m.group('author')
        idx += 1
    return bookinfo_list


うまくいかなかったコード

from pyzon import Pyzon
from BeautifulSoup import BeautifulStoneSoup

access_key_id = ****
secret_access_key = ****
associate_tag = ****

#1冊の本の情報を取り出す(IdTypeはASIN)※amazonAPIのitemLookup
def book_lookup(asin):
    pyzon = Pyzon(access_key_id, secret_access_key, associate_tag)
    xml = pyzon.ItemLookup(item_id=str(asin),ResponseGroup='Small,Images')
    soup = BeautifulStoneSoup(xml)
    #asin,smallimage,mediumimage,largeimage,title,author,manufacturerのリスト作る
    asin = soup.asin.contents[0]
    title = soup.title.contents[0]
    manufacturer = soup.manufacturer.contents[0]
    #imageとauthorはNONEの可能性があるので、チェックした後、値を格納。
    smallimage_url = ''
    mediumimage_url = ''
    largeimage_url = ''
    author = ''
    if not soup.smallimage == 'NONE': smallimage_url = soup.smallimage.url.contents[0]
    if not soup.mediumimage == 'NONE': mediumimage_url = soup.mediumimage.url.contents[0]
    if not soup.largeimage == 'NONE': largeimage_url = soup.largeimage.url.contents[0]
    if not soup.author == 'NONE': author = soup.author.contents[0]
    return [int(asin),str(smallimage_url),str(mediumimage_url),str(largeimage_url),title,author,manufacturer]

うまくいかなかった方は、soup.smallimageとか、soup.authorとかの値が無いときに、最後の数行のif分のところで、無い値の部分がエラーになる。

AmazonAPIがJSONで返してくれたらどんだけ楽なことか。PythonでXMLをJSONに直すのも簡単ではなさそうなので、XMLをBeautifulSoup読む方法で粘ろう。
ちなみに、うまくいかなかったコードの修正版がこれ。

from pyzon import Pyzon
from BeautifulSoup import BeautifulStoneSoup
import re

access_key_id = ****
secret_access_key = ****
associate_tag = ****

#1冊の本の情報を取り出す(IdTypeはASIN)※amazonAPIのitemLookup
def book_lookup(asin):
    pyzon = Pyzon(access_key_id, secret_access_key, associate_tag)
    xml = pyzon.ItemLookup(item_id=str(asin),ResponseGroup='Small,Images')
    soup = BeautifulStoneSoup(xml)
    #asin,smallimage,mediumimage,largeimage,title,author,manufacturerのリスト作る
    bookinfo_list = [[item.asin.contents[0],item.smallimage,item.mediumimage,item.largeimage,item.title.contents[0],item.author,item.manufacturer.contents[0]] for item in soup.findAll('item')]
    #imageとauthorはNONEの可能性があるので、チェックした後、値を格納。
    idx = 0
    for bookinfo in bookinfo_list:
        if not bookinfo[1] == 'NONE':
            p = re.compile('^(?P.+?)')
            m = p.match(str(bookinfo[1]))
            if m: bookinfo_list[idx][1] = m.group('url')
        if not bookinfo[2] == 'NONE':
            p = re.compile('^(?P.+?)')
            m = p.match(str(bookinfo[2]))
            if m: bookinfo_list[idx][2] = m.group('url')
        if not bookinfo[3] == 'NONE':
            p = re.compile('^(?P.+?)')
            m = p.match(str(bookinfo[3]))
            if m: bookinfo_list[idx][3] = m.group('url')
        if not bookinfo[5] == 'NONE':
            p = re.compile('^(?P.+?)')
            m = p.match(str(bookinfo[5]))
            if m: bookinfo_list[idx][5] = unicode(m.group('author'),'utf-8')
        idx += 1
    return bookinfo_list[0]