#はじめに
何事も手動より自動の方が良いに決まっている。勿論本の検索も然り。すでに十分自動化されているが、もっと楽を追求するのが人間の業であると思う。今回は表題の通り、bookoffオンラインのAPIを少し作ってみた。もっと作る予定だが、とりあえず公開。^-^
#何をしたい
主にしたいのは、
- データの取得
データベースの作成- 手元のリストから、bookoffオンラインのカートに追加
などなど。まずは上の3つを作りたい。「手元のリスト」をbookmeterやブクログから作れたら欲しい本をワンクリックで購入!なんてことも出来そうである。
#データの取得
手始めにデータを取得する。サイトを触ると、一見バリバリのJavascriptなので、seleniumを使わないと厳しそうだが、ソースコードを見ると、普通のGETで取れるようである。
##テキストの取得
まず、リクエストを送ってテキストを取得する。
import requests
from bs4 import BeautifulSoup as bs
import urllib.parse as up
import sys
import re
import time
SEARCH_URL="https://www.bookoffonline.co.jp/disp/CSfSearch.jsp"
class SearchType:
ALL=""
BOOK=12
PRATICAL_BOOK=1201
MAGAZINE="13"
CD=31
DVD=71
GAME=51
ELSE=81
SET="set"
def _search(query,page=1,type_=SearchType.BOOK,session=requests):
query=query
encoding="sjis"
query=query.encode(encoding)
params={
"name":"search_form",
"q":query,
"st":"",
"p":page,
"bg":type_
}
headers={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
"
}
res=session.get(SEARCH_URL,headers=headers,params=params)
res.encoding=encoding
return res.text
引数 | 意味 |
---|---|
query | 検索する文字 |
page | 何ページ目 |
type_ | データの種類 |
session | セッション |
sessionは内部のもの。type_の初期値はとりあえず本にしておく。
少し工夫したのは、
query=query.encode(encoding)
のところで、これがないと文字コードの問題で、日本語のクエリがうまくいかない。
SearchTypeは本当はもっとあるのだが、とりあえず使いそうなものだけ。以下が全て。 以下は本の全てとその他。
###ジャンルの一覧
ジャンル名 | 値 |
---|---|
すべて | |
本・書籍 | 12 |
実用書 | 1201 |
健康・家庭医学 | 1202 |
旅行・レジャー・スポーツ | 1203 |
趣味・就職ガイド・資格 | 1204 |
絵本・児童書 | 1205 |
社会・文化 | 1206 |
政治 | 1207 |
法律・コンプライアンス | 1208 |
ビジネス・経済 | 1209 |
産業・労働 | 1210 |
情報・通信・コンピュータ | 1211 |
テクノロジー・環境 | 1212 |
サイエンス | 1213 |
メディカル | 1214 |
哲学・心理学・宗教 | 1215 |
歴史・地理 | 1216 |
語学・会話 | 1217 |
教育 | 1218 |
芸術・芸能・エンタメ・アート | 1219 |
文学・エッセイ・詩集 | 1220 |
小説 | 1221 |
外国の小説 | 1222 |
ポルノグラフィティ | 1223 |
教養・雑学(文庫) | 1224 |
小説・エッセイ・ノンフィクション(新書) | 1225 |
教養・雑学(文庫) | 1226 |
小説・エッセイ・ノンフィクション(新書) | 1227 |
グラビアアイドル・タレント写真集(女性) | 1228 |
プライベートブランド | 1299 |
漫画・コミック | 11 |
雑誌 | 13 |
CD | 31 |
DVD | 71 |
ゲーム | 51 |
その他 | 31 |
オトナ買い | "set" |
##データに変換
_search はテキストを返すだけなので、それをデータに変換する。
def toData(l):
ttl=l.select_one(".itemttl")
title=ttl.text
a=ttl.find("a")
url=a.get("href")
stocked=not l.select_one(".nostockbtn")
mainprice=l.select_one(".mainprice")
price=mainprice.text
price=re.search("[\d,]+",price)
price=price.group()
price=int(price.replace(",",""))
author=l.select_one(".author")
author=author.text
return {
"title":title,
"url":url,
"author":author,
"price":price,
"stocked":stocked
}
def getData(soup):
for l in soup.select(".list_group"):
yield toData(l)
####toDataの引数
引数 | 意味 |
---|---|
l | データのエレメント |
####toDataの返り値
引数 | 意味 |
---|---|
title | タイトル |
url | url |
author | 著者名 |
price | 価格 |
stocked | 在庫の有無 |
####getDataの引数
引数 | 意味 |
---|---|
soup | スープ |
スープからデータを取って変換するだけ。toDataのときに、もっと色々なデータを取れそうだが、とりあえず欲しいものだけ取る。
そして、組み合わせて実用的にする。
def search(query,page=1,type_=SearchType.BOOK,session=requests):
text=_search(query,page,type_,session)
soup=bs(text,"html.parser")
for data in getData(soup):
data["url"]=up.urljoin(SEARCH_URL,data["url"])
yield data
###使ってみる
import api
import json
import sys
query=sys.argv[1]
for data in api.search(query):
print(json.dumps(data,indent=4,ensure_ascii=False))
$ python main.py 安部公房
{
"title": " 砂の女(新潮文庫)",
"url": "https://www.bookoffonline.co.jp/old/0015580570",
"author": "安部公房",
"price": 200,
"stocked": true
}
{
"title": " 壁(新潮文庫)",
"url": "https://www.bookoffonline.co.jp/old/0015580566",
"author": "安部公房",
"price": 200,
"stocked": true
}
{
"title": " 笑う月(新潮文庫)",
"url": "https://www.bookoffonline.co.jp/old/0015580572",
"author": "安部公房",
"price": 200,
"stocked": false
}
~~ 以下略 ~~
##何ページも取る
search は或る1ページしか取れないので、何ページも取れるようにする。
def searchN(query,start=1,n=1,type_=SearchType.BOOK):
session=requests.Session()
for page in range(start,start+n):
dataa=list(search(query,page,type_,session))
if not dataa:
return
for data in dataa:
yield data
time.sleep(1)
####searchNの引数
引数 | 意味 |
---|---|
query | 検索する文字 |
start | 最初のページ |
n | ページ数 |
type_ | データの種類 |
只何ページも取る。一応 search の引数のsesison はこのためにつくった。
#試す
import api
import json
import sys
query=sys.argv[1]
for data in api.searchN(query,start=2,n=2):
print(json.dumps(data,indent=4,ensure_ascii=False))
$ python main.py 安部公房
{
"title": " カンガルー・ノート(新潮文庫)",
"url": "https://www.bookoffonline.co.jp/old/0012309902",
"author": "安部公房",
"price": 300,
"stocked": true
}
{
"title": " けものたちは故郷をめざす(新潮文庫)",
"url": "https://www.bookoffonline.co.jp/old/0016232640",
"author": "安部公房",
"price": 600,
"stocked": false
}
~~ 略 ~~
{
"title": " 安部公房全集-1970.02‐1973.03(23)(贋月報(本文挟み込み小冊子)付)",
"url": "https://www.bookoffonline.co.jp/old/0012889510",
"author": "安部公房",
"price": 3350,
"stocked": true
}
{
"title": " 安部公房全集-1973.03‐1974.02(24)(贋月報(本文挟み込み小冊子)付)",
"url": "https://www.bookoffonline.co.jp/new/0012893518",
"author": "安部公房",
"price": 6270,
"stocked": true
}
以上!!