やってみる

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

複数フィードから重複なく全件を高速に取得する方法の考察

 前回のつづき。

方法

  1. 同一ソースの別カテゴリRSSを統合する
  2. 重複チェックとHTML取得最少化

1. 同一ソースの別カテゴリRSSを統合する。

  1. stdinRSSリストを渡す
  2. 1を指定したDBへ統合する
echo -e "RSS1\nRSS2\nRSS3" | ./run.sh /任意パス/news.db

 RSSの統合はSQLite3ファイルを分けることで対応する。テーブルにsource列を増やすと前回のように面倒になるため避ける。

 もしRSSリストをファイルに保存しているなら、以下のようにする。

cat rss_list.txt | ./run.sh /任意パス/news.db

 ./run.sh./get_news.pyで動くようにしたほうが早いかもしれない。その場合はPythonstdinを取得する方法を調べる。

echo -e "RSS1\nRSS2\nRSS3" | python3 get_news.py /任意パス/news.db
cat rss_list.txt | python3 get_news.py /任意パス/news.db

2. 重複チェックとHTML取得最少化

 メモリを使って高速化やフラグメンテーション回避を狙う。

  • :memory:→RAMディスクDB→ファイルDB

 それぞれを以下のように別DBとしてattachして名付けたものとする。

attach ':memory:' as m;
attach '/RAMディスク/news.db' as r;
attach '/永続ディスク/news.db' as f;

2-1. インメモリDB

 :memory:でインメモリDBを作り、そこで別カテゴリRSSを統合させる方法。

  1. 同一ソース源の別カテゴリRSSから取得したニュースを統合する: :memory: insert or ignore m.news ...
  2. DB内最新を取得する(統合ニュースから重複を削除): ./news.db select max(published) from f.news;
  3. URLからHTML取得し本文抽出する
  4. DBへマージする: :memory: update m.news set body=? where published=? and url=?
  5. DBへマージする: ./news.db insert or fail into f.news(...) select ... from m.news;

2-2. RAMディスクDB

 さらにRAMディスクDBファイルを作る方法。ニュース取得のプロセス終了後でも、電源ON〜OFFまでの間に得たニュースだけを見れる。場合によっては、そこから選択したニュースだけをファイルDBへ登録させても良い。

  1. 同一ソース源の別カテゴリRSSから取得したニュースを統合する: :memory: insert or ignore m.news ...
  2. DB内最新を取得する(統合ニュースから重複を削除): RAMディスク/news.db select max(published) from r.news;
  3. URLからHTML取得し本文抽出する
  4. DBへマージする: :memory: update m.news set body=? where published=? and url=?
  5. DBへマージする: RAMディスク/news.db insert or fail into r.news(...) select ... from m.news;
  6. DBへマージする: ./news.db insert or fail into f.news(...) select ... from m.news;

2-3. RSS用DB(RAMディスク)

 上記までだと、本文抽出が完了するまでRSSが見れない。10分後かもしれない。もっと早く応答が欲しい。RSSだけ別DBにする。それをRAMディスクに配置する。

create table rss(
  published   text not null, 
  url         text not null,
  title       text not null,
  description text not null default '',
  UNIQUE(published,url) -- 記事の一意確認
);

2-4. 通知

 本文が抽出されるには時間がかかる。完了したら通知して欲しい。

zenity --notification ...

フローを細かく

  • インメモリDB
    • ニュースが重複しうる複数のRSSをマージする
      • published, url, titleを取得する
      • それらが重複するものははじく
    • ソートする: order by published desc, url asc, id desc
  • ファイルDB
    • インメモリDBのうちファイルDBとの重複がないか探す
      • 見つかれば、それ以前までのニュースのみ取り込む
        • [(published, url, title), ...]のうち重複ニュースを切り捨てる
      • RSS用RAMディスクDBに挿入する
        • [(published, url, title), ...]
    • 本文を抽出する
      • [(published, url, title, body), ...]を生成する
      • ファイルDBに挿入する

 insertには時間がかかるためexecutemany()で一括挿入したい。だが、そのために全URLのHTML取得を完了させねばならない。そこに最も時間がかかる。

 最初の1件目だけを、できるだけ早く見ることができれば嬉しい。だが、それだと挿入が遅くなる。RAMディスクに一時DBを作って、そこに1件ずつ挿入すればいいかもしれない。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux