ブクレコの本棚のバックアップ

投稿日:

 サービス終了が判明したブクレコだが、結局のところ、書棚データのエクスポートは出来ないから、手近のマシンの「wget」あたりで丸ごとコピーしておくより他にない。

$ wget -kpr -l0  -t3 -T5 -np https://www.bookreco.jp/bookshelves/59112

 まあ、仕方がない。

 で、こうやってできた「bookshelves」ディレクトリに下りて、

$ grep -h '<p class="title">' 59112?page=* |  sed -e 's/<p class="title">//' -e 's!</p>!!' -e 's/ //g' | sort | uniq | nkf -w   >booklist20160503.txt

……なんぞとやっておけば、一応書名のリストだけは取れる。

 残念なのは、ISBNとか読書日などの情報が全く保存できないことである。これをどうにかするには、Amazonか国会図書館のAPIでも使って変換するより他になさそうである。

 しかし、ふと取得した書棚ファイルを見ると、書籍の外見イメージを取得しているところがあって、そのURLでサイトを呼ぶと、その中にISBNや出版日が書かれていることに気付いた。

 なるほど。そこで、先に得た生の書棚データテキストからこのURLを切り出す。

$ grep -h 'book_image' 59112?page=? 59112?page=1? 59112?page=2? | sed -r 's!^.+(https://www.bookreco.jp/book/[0-9]+).+!\1!' >booksurl

 ファイル名をシェルに展開させるにあたり、「59112?page=*」とせずに「59112?page=? 59112?page=1? 59112?page=2?」としているのは、1番から20番までのファイルがある場合、「59112?page=*」としてしまうと、そのままだと1,11,12,13,14,15,16,17,18,19,2,20,3,4,5,6,7,8,9、というふうに、辞書順に並んでしまうからである。「ブクレコへの登録が新しい順」を保持させるためには、こうするより他にない。

 それから、このURLリストをwget -iに食わせ、データの入った生テキストを得る。

$ wget -O - -i booksurl | sed -rn -e '/<div class="summary">/, /<!-- \/\/end summary -->/p' >booksfulltext

 更に、この生テキストのタグを除き、csvにしてしまう。これをsedなどでのみやるには、結構ゴチャゴチャ書かなければならないのだが、美しく作業する気がてんからなく、行を建て増し建て増しして書いているうちに、結局自分でもなんだかわからない、次のような謎のワンライナーになった。

$ sed -nr -e '/(<span class="bold large">)|(<ul class="floatlist_left clearfix text_thin">)/,/(<\/span>)|(<\/ul>)/p'   booksfulltext | sed -e '/<span /d' -e 's/<\/span>/,/' -e '/<ul/d' -e 's/<\/ul>/-----/' -e 's/<li>出版日://' -e 's/<li>種類://' -e 's/<li>ISBN://' -e 's/<\/li>/,/' | sed -r -e 's/ +//g' -e ':loop;N;$!b loop;s/\n/ /g' -e 's/ +/ /g' -e 's/-----/\n/g' -e 's/, /,/g' -e 's/,\n$/\n/' | nkf -w
 >bookscsv_utf

 このようにして得たCSVをスプレッドシートに読み込ませるわけである。

 残念なのは、ブクレコには読書日が入れられないので、いつ読んだ本かと言うことが消失していることだ。

メール・メーターはこのように作る。

投稿日:

 さて、一部の変わったユーザ様(ヲイ)のために、Arduinoを使用した未読メールメータの作成方法を書いておきたい。入門キットについてくるサーボを使用する。

 プログラムは次のような簡単なものでよろしい。

 まず、Arduino側には次のように書く。

//
//  シリアルからサーボを制御
//    佐藤俊夫
//    27.05.02(土) 1028~
//
#include <Servo.h>
//
Servo meter;
const int METER_0 = 5;
const int METER_100 = 175;
const int METERMIN = 0;
const int METERMAX = 100;
const int STRLEN = 10;
char buf[STRLEN];
int i = 0;
//
void setup() {
  meter.attach(9);
  meter.write(METER_0);
  Serial.begin(9600);
}

void loop() {
  int v = 0, deg = METER_0;
  if(Serial.available() > 0){  //  もし受信したデータが存在したら
    buf[i] = Serial.read();
    if(i >= STRLEN - 1 || buf[i] == '\n'){
      buf[i] = '\0';
      i = 0;
      Serial.flush();
      //  針をナニする処理
      v = atoi(buf);
      (v > METERMAX) ? v = METERMAX : v;
      (v < METERMIN) ? v = METERMIN : v;
      deg = METER_0 + (int)(v / ((float)METERMAX / (float)(METER_100 - METER_0)));
      meter.write(deg);
// Serial.println(deg, DEC);
// Serial.write('\n');
      delay(1000);
    }else{
      i++;
    }
  }    
}


 テストしてうまく動いたら、次に、POP3サーバにたまっている自分のメールの本数を知る工夫をする。

 私は次のようにした。まず、手近のLinuxマシンにexpectを入れる。

# yum -y install expect
(中略)

 そうすると、telnetなどでPOP3サーバに自動ログインできる環境が整う。

 シェルでこんなのを書く。まあ、遊びなんでrootで。

# ls -Fla pop2arduino
-rwx------ 1 root root 415 2015-05-02 13:58 pop2arduino*
# cat pop2arduino
#!/bin/sh
#  pop2arduino
#    Sat May  2 12:53:57 JST 2015
#    Sato Toshio
#
expect -c "
set timeout 5
spawn telnet pop.hogehoge.ne.jp 110
expect \"+OK POP3 ready\"
send \"USER fugafuga@hagefuge.hogehoge.ne.jp\n\"
expect \"+OK\"
send \"PASS passpass\n\"
expect \"+OK server ready\"
send \"STAT\n\"
expect -re \"\\\+OK (.+) .+$\"
send \"QUIT\n\"
" | egrep "\\+OK ([0-9]+) .+$" | sed -r "s/\\+OK ([0-9]+) .+$/\1/g" >/dev/ttyACM0

 で、このシェルは、まあ、なんだっていいんだけど、安直にcronで定期実行する。

# crontab -e
(以下crontab内)
*/5  *  *  *  *  /hoge/pop2arduino
(crontabおわり)
/etc/rc.d/init.d/crond restart
(出力略)

……で、メールが届くのを待っていると、こういうふうに動く。

IMG_2768



で、それじゃ、IoT風味で

投稿日:

 で、私がやりたかったのはコレなんである。

……つまり、このオモチャを、インターネットにつなぐ。これがやりたかった。

 あとはもう、

「メールが来たら旗があがる」

とか、

「録画が終わったら、人形が手を上げる」

とか、もう、なんぼでもアレンジ可能である。

 チナミに、Linuxだとこういうことをするのに大したことは書かなくてよく、

tail -n 0 -f /var/log/httpd/access_log | grep --line-buffered 'HTTP/1.1\" 200' | sed --unbuffered -e 's/..*/1/' >/dev/ttyACM0

……ぐらいの「ワン・ライナー」で済んでしまう。

 Arduino側のソースコードは、結局最終的には

//
//  シリアルからサーボを制御
//    佐藤俊夫
//    27.05.01(金) 0946~
//
#include <Servo.h>
//
Servo flag;
int incomingByte = 0;
const int FLAGOFF = 5;
const int FLAGON = 90;
//
void setup() {
  flag.attach(9);
  flag.write(FLAGOFF);
  Serial.begin(9600);
}

void loop() {
  if(Serial.available() > 0){  //  もし受信したデータが存在したら
    incomingByte = Serial.read();
    if(incomingByte == '1'){
      flag.write(FLAGON);
    }else if(incomingByte =='0'){
      flag.write(FLAGOFF);
    }
    Serial.flush();
    delay(1000);
  }    
}

……ぐらいの、お気楽な感じのモノになった。