例の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では取れない)