0
0

More than 1 year has passed since last update.

bookmeterのAPIをつくってみた。 #1 (データの取得)

Last updated at Posted at 2021-10-25

はじめに

昔は図書館の本すら、自由に読めなかったらしいが、今は自由に読めるし、ブックオフもあるし、更に bookmter もある。恵まれた時代である。僕も bookmeter を愛用させていただいてる。美しいデザイン、知的なユーザー、痒いところに手の届く機能。人類の幸福に多大な貢献をしている bookmeter だが、だからこそ、なぜこれが出来ないのかと思うことがある。例えば、「読みたい本」というブックマークのような機能があって、素晴らしいのだが、検索ができないのである。VIM はヘルプすら検索できるのに。同様に「読んだ本」や「積読」も検索できない。また、タグがないので、バラバラに本がある印象で、どうにもまとまらない。しかし不満ばかり言っても仕方ない。使う側が工夫すれば良いのである。モンスターカスタマーでは、素晴らしい運営者も疲れて辞めてしまうかもしれない。

したいこと

  • 「読みたい本」などの検索
  • 本のタグ付け
  • ...

まずは、上の2つをやりたい。他にも色々できたら。

本のデータの取得

まずは本のデータを取る。本のデータの一覧は同じ形式なので同じように取れる。

bookmeter.py
import requests
from bs4 import BeautifulSoup as bs
import time
import urllib.parse as up
import json

HEADERS={
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36"
}

class Selector:
        BOOK="li.group__book"
        THUMB=".book__thumbnail"
        TITLE=".detail__title"
        DATE=".detail__date"
        AUTHOR=".detail__authors"
        PAGE=".detail__page"

def toData(book):
        thumb=book.select_one(Selector.THUMB)
        if thumb:
                img=thumb.find("img")
                imgUrl=img.get("src") if img else ""
        else:
                imgUrl=""
        title_e=book.select_one(Selector.TITLE)
        title=title_e.text if title_e else ""
        href=title_e.find("a").get("href") if title_e else ""
        date=book.select_one(Selector.DATE)
        date=date.text if date else ""
        author=book.select_one(Selector.AUTHOR)
        author=author.text if author else ""
        page=book.select_one(Selector.PAGE)
        page=page.text if page else ""
        return {
                "imgUrl":imgUrl,
                "title":title,
                "href":href,
                "date":date,
                "author":author,
                "page":page
        }


def __getBooks(soup):
        return map(toData,soup.select(Selector.BOOK))

def _getBooks(url,page=1,session=requests):
        params={
                "page":page
        }
        res=session.get(url,headers=HEADERS,params=params)
        soup=bs(res.text,"html.parser")
        return __getBooks(soup)
def getBooks(url,start=1,n=5):
        session=requests.Session()
        for page in range(start,start+n):
                data=list(_getBooks(url,page,session))
                if not data:
                        return
                for dat in data:
                        dat["href"]=up.urljoin(url,dat["href"])
                        yield dat
                time.sleep(1)

同じような関数ばかり並んでるのはご愛敬。肝心なのは getBooks だけだが、一応説明。

toData

引数 意味
book 本のデータのスープ

toDataの返り値

キー
imgUrl 画像のURL
title タイトル
href 本のURL
date 日付(追加日)
author 著者名
page 本のページ数

__getBooks

引数 意味
soup スープ

返り値は、データのリスト。

_getBooks

引数 意味
url URL
page サイトのページ数
session セッション

返り値は、データのリスト。sessionは内部のもの。

getBooks

引数 意味
url URL
start 最初のページ
n データを取るページの数

返り値は、データのリスト。

要するに、toData はスープをデータに変換する、__getBooksはスープから本のデータを返す、_getBooks は __getBooks のラッパーのようなもので、getBooks は _getBooks のラッパーのようなもの。

pageというのはページ数で、例えば、ホームの検索窓で、「数学」と入れて調べると、
https://bookmeter.com/search?keyword=%E6%95%B0%E5%AD%A6
に飛ぶ。これに page=3 を足して
https://bookmeter.com/search?keyword=%E6%95%B0%E5%AD%A6&page=3
とすると3ページ目に行く。

試す

main.py
import bookmeter
import time
import json

url="https://bookmeter.com/users/1035737/books/wish"
for data in bookmeter.getBooks(url,start=1,n=10**32):
        print(json.dumps(data,ensure_ascii=False,indent=4))
実行結果
$ python main.py

{
    "imgUrl": "https://m.media-amazon.com/images/I/41OCTQEQrbL._SL500_.jpg",
    "title": "安部公房とわたし",
    "href": "https://bookmeter.com/books/7020962",
    "date": "",
    "author": "山口 果林",
    "page": ""
}
{
    "imgUrl": "https://m.media-amazon.com/images/I/510TTCKFQNL._SL500_.jpg",
    "title": "イスラーム文化−その根柢にあるもの (岩波文…",
    "href": "https://bookmeter.com/books/579483",
    "date": "",
    "author": "井筒 俊彦",
    "page": ""
}
{
    "imgUrl": "https://m.media-amazon.com/images/I/51vdg2TkNfL._SL500_.jpg",
    "title": "日本史有名人の身体測定",
    "href": "https://bookmeter.com/books/10276713",
    "date": "",
    "author": "篠田 達明",
    "page": ""
}


~~ 以下略 ~~

簡単な検索

データを取れたので、簡単な検索をしてみる。

main.py
import time
import json
import docopt
import re
import docopt

__doc__="""
Usage:
        search <s> <url>
"""

args=docopt.docopt(__doc__)

s=args["<s>"]
url=args["<url>"]
for data in bookmeter.getBooks(url,start=1,n=10**32):
        if re.search(s,data["title"]):
                print(json.dumps(data,ensure_ascii=False,indent=4))
実行結果
$ python main.py  数学 https://bookmeter.com/users/1035737/books/wish

{
    "imgUrl": "https://m.media-amazon.com/images/I/51xDl2z9K+L._SL500_.jpg",
    "title": "高校数学でわかる流体力学 (ブルーバックス)",
    "href": "https://bookmeter.com/books/8095831",
    "date": "",
    "author": "竹内 淳",
    "page": ""
}
{
    "imgUrl": "https://m.media-amazon.com/images/I/51G4jU5hGhL._SL500_.jpg",
    "title": "高校数学でわかるフーリエ変換―フーリエ級数か…",
    "href": "https://bookmeter.com/books/424303",
    "date": "",
    "author": "竹内 淳",
    "page": ""
}
{
    "imgUrl": "https://m.media-amazon.com/images/I/416Yus64pNL._SL500_.jpg",
    "title": "物語 数学の歴史―正しさへの挑戦 (中公新書)",
    "href": "https://bookmeter.com/books/491408",
    "date": "",
    "author": "加藤 文元",
    "page": ""
}

~~ 以下略 ~~           

終わりに

次回はデータベースを作って、もう少し本格的にできるようにしたい。

コードはこちら

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0