やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

はてなブログ記事DBを作る

SQLite3で。

成果物

GitHubHatena.Blog.API.Database.Create.Blog.Entries.201703011629

開発環境

はてなブログAtomPub - Hatena Developer Center

前回まで

http://ytyaru.hatenablog.com/entry/2017/06/26
http://ytyaru.hatenablog.com/entry/2017/06/29
http://ytyaru.hatenablog.com/entry/2017/06/30

データベース

  • Hatena.Hatena.Blog.Entries.{BlogId}.sqlite3

テーブル

Accounts

create table Entries(
    Id                  integer primary key,
    EntryId             text unique not null,
    Url                 text unique not null,
    Title               text,
    Summary             text,
    ContentType         text,
    Content             text,
    HtmlContent         text,
    Categories          text,
    IsDraft             integer default 0 check(IsDraft = 0 or IsDraft = 1),
    Edited              text,
    Published           text,
    Updated             text
);

EntryId

サービス文書内では<id>tag:blog.hatena.ne.jp,2013:blog-{HatenaId}-{HatenaBlogId}-{EntryId}</id>という書式。このうち{EntryId}の部分をDBのEntryId列値として保存する。

値はすべて数値である。はてなの仕様書によるとepoch値らしい。たぶん1970/01/01 00:00:00からの経過秒数。にもかかわらずDBを数値でなくテキスト型にしたのはオーバーフロー対策。

Python3は上限がなくなった。SQLite3は最大で符号付き8byte(64bit)まで。しかし、PythonはDBに数値を挿入するとき、64bit数値をサポートしていないらしい。32bit値までしか正常に扱えない。以下のようなエラーが出る。

OverflowError: Python int too large to convert to SQLite INTEGER

OverflowError: Python int too large to convert to SQLite INTEGER · Issue #191 · volatilityfoundation/volatility · GitHub

DBにはテキストとして保存し、Pythonで数値として復元して利用するしかない。 そうなると、DB値だけで大小比較すると文字列としての大小比較になってしまう。 数値としての比較とはまったく結果が異なるため、注意が必要である。

EditURL

DB列にはない。各種IDから作成できる

<link rel="edit" href="https://blog.hatena.ne.jp/{HatenaId}/{BlogId}/atom/entry/{EntryId}"/>

Url

はてなブログの設定によって違うと思われる。

予約投稿したときURLは予約投稿されるURLになっていない。保存したときの日時URLになっているのか。よくわからない。そのURLを指定しても存在しないからアクセスできない。

<link rel="alternate" type="text/html" ...>href属性値をDBのUrlに保存する。

ContentType

記事を編集するときのテキストデータ形式。「見たまま」「Markdown」「はてな記法」の3種類のどれかと思われる。

編集形式 Content要素のtype属性値
見たまま ?
Markdown text/x-markdown
はてな記法 text/x-hatena-syntax

<content type="text/x-markdown">,<content type="text/x-hatena-syntax">。type属性値をContentType列に保存する。「見たまま」だと何の値なのか不明。

Content

記事を編集するときのテキストデータ。たとえばMarkdownならそのデータ。

<content>のテキストノード値をContent列に保存する。

HtmlContent

<content>をHTMLに変換したときのテキストデータ。

<hatena:formatted-content type="text/html" ...>のテキストノード値をHtmlContent列に保存する。

Category

記事のカテゴリ。

<category term="">のterm属性値をCategory列に保存する。

ただし、複数の<category term="">が存在しうる。すべての値を取得し、それらをカンマ区切りにして保存する。

IsDraft

<app:control><app:draft>no</app:draft></app:control>noまたはyesのどちらか。

状態 draft
下書き yes
公開 no

ちなみに予約投稿の場合、draft=yesかつ<updated>が未来の日時になっている。

日時

日時はyyyy-MM-ddTHH:mm:ss+09:00形式。

悩んだこと

HTMLテキストは必要か

Markdownデータさえあればあとはツールで変換できると思う。

でもツールによって結果が変わりうるし、毎回変換せずに済むなら保存したほうが早い。

Draft

はてな内部での扱い方に従った。

実際は、公開、下書き、予約投稿、の3種類ある気がする。でも、下書きと予約投稿の区別は<updated>が未来の日付かどうかで判断すると思われる。いまいちそのへんの仕様を理解していない。

EditURL

将来変更されてキーだけでは自動生成できなくなる懸念もある。途中からベースURLが変更されることも考えられる。でも、ほとんど重複しているから省いた。Contentさえ保存できれば大体OKだし。

URL

URL形式

URL形式が日時なら、日時さえあればURLを作成できる気がする。でも、カスタムURLもできるのでそのままURL値を保存するほうが安全か。

予約投稿

予約投稿で下書き中のURLは公開予定のURLではない。作成時か最終更新日かわからないが、そのへんのURLになっている。そのURLは存在しない。はてなブログ内部で、予約投稿の日時がくると、その日時でURLを作成すると思われる。それ以降では公開URLが取得できると思われる。

つまり、予約投稿記事のURLは存在しないものになってしまう。公開後にURLを取得し直すべきだが、今はその問題は放置しておく。とにかくContentさえ保存できればOKとする。

所感

予約投稿まわりが不自然に思える。振り回されないよう、Contentの保存にだけ集中する。とりあえずXML値をそのまま保存するくらいの軽い気持ちで。