例のKindle無料検索サイトを作成していて、APIで取ってこれる情報は一条件につき10ページまでとなっている。だから、、商品番号(ASIN)は最大100件までしか収集できない(もちろん検索条件を変えればいいのだけど、、無料Kindleコミックの情報をAPIで取得するのに、他に使える条件があるのか分からず)。
そこで、、クローリングの勉強を兼ねて、利用者と同じ無料ページをクローラがクローリングするソフトをPythonで試作した。
Kindleコミックを安いもの順で表示した場合、以下のURL(下記の例は安いもの順、3ページ目を表示した場合の例)
ページが変わると、page=NNNの部分が変わる。逆に言うと、page=NNN(とsr_pg_NN)を指定すれば好きなページに移ることができる。そんな特性を利用してクローラを作ってみた。HTMLを本気でパースするのは大変だけど、商品番号(ASIN)の入っている文字列はパターンが限られるので、パターンに合致するように正規表現で引っこ抜く。
ポイントとしては、、普通にurllib2でアクセスしていると、cookieを食わないおまえはロボットか?とAmazonサイトから応答されるので、cookieを有効にしている。もっと正しい実装もあると思うけど、現状はこれで動いています。ご参考にどうぞ。
#!/usr/bin/python # # crawling Amazon Kindle Page and get ASIN Code # import sys import time import re import urllib2 import cookielib URL_TMPL="https://www.amazon.co.jp/s/ref=sr_pg_{:d}?rh=n%3A2250738051%2Cn%3A%212250739051%2Cn%3A2275256051%2Cn%3A2293143051&page={:d}&sort=price-asc-rank&ie=UTF8&qid=1493625872&lo=digital-text" UERAGENT = 'Mozilla/5.0' SUSPICIOUS_ROBOT = 'automated access to Amazon data' #MAXCRAWLINGPAGE = 1 # crawling max page MAXCRAWLINGPAGE = 35 # crawling max page cj = cookielib.CookieJar() cjhdr = urllib2.HTTPCookieProcessor(cj) opener = urllib2.build_opener(cjhdr) opener.addheaders = [('User-Agent', UERAGENT)] asinList = [] for pageNo in range(1, MAXCRAWLINGPAGE + 1): print "crawling {:d}th Page\n".format(pageNo) print "zzzzz....\n" time.sleep(3) waitTimeInRetry = 0 for trial in range(10): responseCode=None time.sleep(waitTimeInRetry) try: url = URL_TMPL.format(pageNo, pageNo) print "access" + url fp = opener.open(url) html = fp.read() responseCode = fp.getcode() fp.close() if SUSPICIOUS_ROBOT in html: print 'Warning!! access is recognized as ROBOT' print "retry" waitTimeInRetry = 3 else: break except urllib2.URLError as e: print "raised Exception" print "urllib2.URLError" print 'failed to reach a server.' print 'Reason: ', e.reason print "retry" waitTimeInRetry = 3 except urllib2.HTTPError as e: print "raised Exception" print "urllib2.HTTPError" print "The server couldn't response the request." print 'Error code: ', e.code print "retry" waitTimeInRetry = 3 except: print "raised Exception" print("Unexpected error:", sys.exc_info()[0]) print "retry" waitTimeInRetry = 3 if responseCode != 200: print "access failed" sys.exit() else: print "OK[{:d}]".format(responseCode) # # save crawling taget page for debug # file = 'crawling_{:d}.html'.format(pageNo) f = open(file, 'w') f.write(html) f.close() # # pickup ASIN (kindle free contents) # # find href="xxxxxxx" itemList = re.findall('href="([^\"]*)"', html) for url in itemList: if 'ebook' in url: print "-------------------------------\n" print url print "-------------------------------\n" matchAsin = re.match('.*ebook/dp/([A-Z0-9]*)', url) if matchAsin: print "find ASIN\n" asin = matchAsin.groups()[0] if not asin in asinList: # add to ASIN_LIST only if new one asinList.append(asin) else: print "Error!! not found\n" for asin in asinList: print asin f = open('crawl_out.txt', 'w') for asin in asinList: output = "{:s}\n".format(asin) f.write(output) f.close()
■ご参考
(今回は参考にしていませんが、まっとうにクローリングを実装しようとしたらいろいろ考慮必要ということで)
https://blog.hartleybrody.com/scrape-amazon/
Hartley Brody氏
How to Scrape Amazon.com: 19 Lessons I Learned While Crawling 1MM+ Product Listings
■補足
Amazonサイトではクローリングを禁止しているとのとで、このようなプログラムによるWebサイトアクセスは使用許諾違反になるようです。あまり悪質だとIP制限がかかるらしい。ですので、、もしそういう行為をされる場合はご注意ください。
「プログラムによるアクセスはAPIを使うこと」というのが正しい答えです。ただ、、APIでも取れない情報があるのでそれは困った点です。(Kindle版コンテンツの価格、レビューアによるコメント(星いくつ)といった情報はAPIでは取れない)