はてなブログAPIを使った自動投稿プログラムの改良
これを改良しました。
温湿度・写真の自動取得
ベースは以前述べたexample/simple_test.py ですが、それにカメラモジュールでの撮影を含めたプログラムを作りました。
#!/home/pi/.pyenv/shims/python # log_condition_v3.py # -*- coding: utf-8 -*- import os import Adafruit_DHT import datetime now = datetime.datetime.now() # 現在の日時を取得 sensor = Adafruit_DHT.DHT22 pin = 4 degC = '℃' #def condition(humidity, temperature, discomfort): def condition(): humidity, temperature = Adafruit_DHT.read_retry(sensor, pin) discomfort = 0.81*temperature + 0.01*humidity*(0.99*temperature - 14.3) + 46.3 return humidity, temperature, discomfort def takepic(): # cmd = 'raspistill -n -o /home/pi/log_condition/log_pic/{0:%Y%m%d%H%M}.png -t 500 -q 5 -e'.format(now) cmd = 'raspistill -n -o /home/pi/log_condition/log_pic/{0:%Y%m%d%H}.png -t 500 -q 5 -e'.format(now) cmdact = os.popen(cmd) def main(): # log current condition file = open('/home/pi/log_condition/log_TH/{0:%Y%m%d}.txt'.format(now), 'a+') #書き込みモードでオープン humidity, temperature, discomfort = condition() # Markdown記法(半角スペース2個で改行) file.write('時刻:{0:%Y/%m/%d %H:%M:%S} \n'.format(now)) file.write('湿度:{0:3.1f}% \n'.format(humidity)) file.write('温度:{0:3.1f} {1} \n'.format(temperature,degC)) file.write('不快指数:{0:3.1f} \n\n'.format(discomfort)) # take a picture takepic() if __name__ == '__main__': main()
cronに書き込んで、1日3回情報を取得するようにしておきます。
$ crontab -e
0 7,12,22 * * * sudo python /home/pi/log_condition/log_condition_v3.py
7:00、12:00、22:00に温湿度のログと写真撮影を行うようにしました。
はてなブログへの自動投稿
自動投稿は次のような手順で行うようにしました。
1. はてなフォトライフAPIを使って、写真をはてなフォトライフへアップロードする
2. はてなフォトライフAPIを使って、アップロードした写真の情報を取得する
3. 取得した情報(アップロードされたときの名称)と一緒に、はてなブログAPIを使ってはてなブログへとアップロードする
#!/home/pi/.pyenv/shims/python # hatenablog_post_v7.py #coding=utf-8 import os import sys import random import requests from base64 import b64encode from datetime import datetime from hashlib import sha1 from pathlib import Path from chardet.universaldetector import UniversalDetector import re from time import sleep import shutil import glob now = datetime.now() dtime = str(now.year)+"""-"""+str(now.month)+"""-"""+str(now.day)+"""T"""+str(now.hour)+""":"""+str(now.minute)+""":"""+str(now.second) print(dtime) # setting ----------------------------------------------------------- username = 'はてなブログに登録している名前(ここではshut9)' api_key = '********APIキーをここに書く*******' blogname = 'ブログの名前を書く(ここではsaibaimen.hatenadiary.jp)' draft = 'yes' # yes or no 下書きとして投稿する場合はyes。本投稿はno。 # WSSE def wsse(username, api_key): created = datetime.now().isoformat() + "Z" b_nonce = sha1(str(random.random()).encode()).digest() b_digest = sha1(b_nonce + created.encode() + api_key.encode()).digest() c = 'UsernameToken Username="{0}", PasswordDigest="{1}", Nonce="{2}", Created="{3}"' return c.format(username, b64encode(b_digest).decode(), b64encode(b_nonce).decode(), created) # hatena blog def create_data_blog(title, body, fotoname): if fotoname == None: text = body else: text = body + """ [f:id:shut9:{0}p:plain] """.format(fotoname) template = """<?xml version="1.0" encoding="utf-8"?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app"> <title>{0}</title> <author><name>{1}</name></author> <content type="text/x-markdown"> {2} </content> <updated>{3}</updated> <category term="" /> <app:control> <app:draft>{4}</app:draft> </app:control> </entry> """ data = template.format(title, username, text, dtime, draft).encode() return data def parse_text(file, charset): with open(file, encoding=charset) as f: obj = f.readlines() body = "" for i, line in enumerate(obj): body = body + line return body def check_encoding(file): detector = UniversalDetector() with open(file, mode='rb') as f: for binary in f: detector.feed(binary) if detector.done: break detector.close() charset = detector.result['encoding'] return charset def post_hatena(data, headers): url = 'http://blog.hatena.ne.jp/{0}/{1}/atom/entry'.format(username, blogname) r = requests.post(url, data=data, headers=headers) if r.status_code != 201: sys.stderr.write('Error!\n' + 'status_code: ' + str(r.status_code) + '\n' + 'message: ' + r.text) # hatena foto life def create_data_foto(filename): files = Path(filename).read_bytes() root,ext=os.path.splitext(filename) ext = ext[1:] uploadData = b64encode(files) template=""" <entry xmlns="http://purl.org/atom/ns#"> <title>{0}</title> <content mode="base64" type="image/{1}">{2}</content> </entry> """ return template.format(filename,ext,uploadData.decode()) def upload_foto(data, headers): url = 'http://f.hatena.ne.jp/atom/post/' r = requests.post(url, data=data, headers=headers) if r.status_code != 201: sys.stderr.write('Error!\n' + 'status_code: ' + str(r.status_code) + '\n' + 'message: ' + r.text) def foto_info(headers): url = 'http://f.hatena.ne.jp/atom/feed/' r = requests.post(url, headers=headers) return re.search('[0-9]{14}', r.text).group() # main ----------------------------------------------------------- def main(): # define WSSE header headers = {'X-WSSE': wsse(username, api_key)} # upload photo to HatenaFotoLife filelist = glob.glob('/home/pi/log_condition/log_pic/*') filename = '/home/pi/log_condition/log_pic/{0:%Y%m%d}12.png'.format(now) print(filename) fotoflag = 0 if filename in filelist: print('Photo uploading...') data_foto = create_data_foto(filename) upload_foto(data_foto, headers) fotoflag = 1 sleep(10) else: print('Photo data is nothing') fotoflag = 0 # confirm infomation of uploaded pyhoto if fotoflag == 1: print('---- Uploaded foto info ----') fotoname = foto_info(headers) print(fotoname) else: fotoname = None # post blog to HatenaBlog filelist_log = glob.glob('/home/pi/log_condition/log_TH/*') filename_log = '/home/pi/log_condition/log_TH/{0:%Y%m%d}.txt'.format(now) print(filename_log) if filename_log in filelist_log: print('blog uploading...') charset = check_encoding(filename_log) title = '今日の観察' body = parse_text(filename_log, charset) data_blog = create_data_blog(title, body, fotoname) else: print("log file is nothing") return 0 post_hatena(data_blog, headers) print('Done') if __name__ == '__main__': main()
main関数を読むと、先程上で記述した順番で動かそうとしていることがわかるかと思います。 APIキーは、はてなブログの[設定]→[詳細設定]から見ることができます。
このプログラムを使えば、こんな感じでブログにアップロードされます。
あとはcronに書き込んで、定期投稿させるようにして完成です。
1日のおわり(23:50にしました)に記事を投稿させるようにしています。
$ crontab -e
50 23 * * * /home/pi/.pyenv/shims/python /home/pi/posthatena_python/hatenablog_post_v7.py
pythonは絶対パスで/home/pi/.pyenv/shims/pythonと書いておかないと動きません。
実際には/home/pi/.pyenv/shims/pythonの部分には
$ which python
としたときに書かれている文字列を書く必要がありますが、pyenv環境下で普段実行していれば多分これでよいと思います。
その他
自動投稿の準備まではすでに出来ているのですが、部屋が狭いので置く場所がいまいち決まらなかったり、Raspberry Piケースを削ったりなど、細かい部分の調整があってまだ観察日記が始まっていません。
今のところ、一番致命的な問題点は、部屋が暗いと写真を撮影しても見えづらいことです。(写真撮影のフラッシュ用にPower LEDを付けたいと思っています。)
早く完成させないとアボカドが少しずつ成長しています………