背景
あまりニュースを見ない(新聞もテレビも)という悪いクセがあるので、MacBook Proのメニューバーに、ニュースのヘッドラインを表示できるようにしてみました。
- ニュース提供元ごとに記事をまとめます
- 記事が多い場合、15記事のみ表示します
- 記事をクリックすると、ウェブサイトにジャンプします
- 記事タイトルの頭に「日付-時間:」を表示します
という仕様にしています。
環境
ここで書いていることは、下記のバージョンで実施しました。
- feedparser 6.0.8
- SwiftBar 1.4.2 (386)
- VS Code 1.63.2
- macOS Monterey 12.1
- MacBook Pro (13-inch, 2020, Four Thunderbolt 3 Ports)
手順の概要
- SwiftBarのインストール(メニューバーカスタマイズツール)
- Pythonのインストール
- feedparser(Pythonモジュール)のインストール
- Pythonスクリプトの記述
SwiftBarというメニューバーカスタマイズツールがあり、これに対して標準出力を吐けばメニューバーに表示してくれます。RSSフィードのパーサとしてfeedparser
がわりとよさそうだったので、つかうことにしました。Pythonスクリプトを書くのははじめてなので、あまり流儀に沿っていないところもあるかもしれません。ご容赦ください。
手順の詳細
1. SwiftBarのインストール
Rebuild.fmの第292回で、SwiftBarというツールが紹介されていました。
macOSのメニューバーのカスタマイズツールで、スクリプトの標準出力をメニューバーに出してくれます。
SwiftBarのリリースページから最新版をzipダウンロードし、任意のフォルダ(ふつうはアプリケーションフォルダ?)に置き、起動します。プラグインフォルダを指定するように言われるので、適当なフォルダを指定します。
あまり深い階層にプラグインフォルダをおくと「ダメ」と言われるっぽいので、ユーザのホーム(
~/
)下にフォルダをおきました。
補足
BitBarというわりと有名なアプリがあったようです。これが新しいmacOSで使えなくなって、後継版としてxBarとSwiftBarの2つに進化したようです。いずれもBitBar時代のプラグイン資産を使えます。xBarのほうがGitHubのスターが多くて人気があるようです。しかしながら、
- わたしはうまくxBarを動かせなかった(原因は不明)
- SwitBarは
SF Symbols
がつかえる
ので、SwiftBarをつかっています。
2. Pythonのインストール
以前にmacOSでpythonを使えるようにしたはずなんですけれども、python3を使おうとするとまたクラッシュするようになっていました。Montereyにアップデートしたからですかねえ(謎)。
$ python3 --version
dyld[1993]: dyld cache '/System/Library/dyld/dyld_shared_cache_x86_64h' not loaded: syscall to map cache into shared region failed
dyld[1993]: Library not loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
Referenced from: /Library/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python
Reason: tried: '/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation' (no such file), '/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation' (no such file)
Abort trap: 6
$ python3 -m pip install requests
dyld[6053]: dyld cache '/System/Library/dyld/dyld_shared_cache_x86_64h' not loaded: syscall to map cache into shared region failed
dyld[6053]: Library not loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
Referenced from: /Library/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python
Reason: tried: '/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation' (no such file), '/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation' (no such file)
Abort trap: 6
しかたがないので、再度インストール。
$ brew install python3
Error: python@3.9: the bottle needs the Apple Command Line Tools to be installed.
You can install them, if desired, with:
xcode-select --install
You can try to install from source with:
brew install --build-from-source python@3.9
Please note building from source is unsupported. You will encounter build
failures with some formulae. If you experience any issues please create pull
requests instead of asking for help on Homebrew's GitHub, Twitter or any other
official channels.
と言われたので(入れてあるはずなんだけどなあとおもいながら)
$ xcode-select --install
xcode-select: note: install requested for command line developer tools
そしてふたたび
$brew install python3
無事に完了しました。
補足
macOSでのPythonについては、macOS環境のPythonをお手本にしました。
ほんとうはvenv
などの仮想環境下でモジュールをインストールしたほうがいいのですけれども、今回はSwiftBarのためにPythonスクリプトを動かしたいので、仮想環境をつかいませんでした。
SwiftBarから動かすスクリプトを、仮想環境で動かしたほうがいいんだろうなとはおもっています。
3. feedparserのインストール
ニュースサイトのRSSフィードを解釈するツールとして、feedparserを選びました。
$ python3.9 -m pip install feedparser
補足
GitHubでrss parser
を調べて、rss-parserも使おうとしてみましたが、なぜだかうまく動かなかったので、最初にうまく動いたfeedparserに。
つかいかたの入門には、Using Feedparser in Pythonがとても参考になりました。
だいたいわかってきてからは、feedparserの公式ドキュメントが使いやすかったです。
4. Pythonスクリプトの記述
いまのところ下記のスクリプトで落ち着いています。
- ニュース提供元ごとに記事をまとめます
- 記事が多い場合、15記事のみ表示します
- 記事をクリックすると、ウェブサイトにジャンプします
- 記事タイトルの頭に「日付-時間:」を表示します
ファイル名をfeedparser.60m.py
として、SwiftBarのプラグインフォルダに置きます。
60m
は、60分ごとに実行する、という意味です。
#!/usr/bin/env python3.9
# -*- coding: utf-8 -*-
#
# sources - source - parsed(d) + feed
# + entries[i]
import feedparser
MAX_ENTRIES = 15
ENTRY_LENGTH = 30
SUBMENU = '--'
SUBSUBMENU = '----'
IDX_DAY = 2
IDX_HOUR = 3
DAY_DELIM = '-'
HOUR_DELIM = ':'
MISS_MONTH = 'xx'
MISS_HOURS = 'xx'
sources = [
# {'cite':'Apple', 'url':'https://www.apple.com/jp/newsroom/rss-feed.rss'},
{'cite':'京都新聞', 'url':'https://www.kyoto-np.co.jp/list/feed/rss'},
{'cite':'NHK', 'url':'https://www.nhk.or.jp/rss/news/cat0.xml'},
{'cite':'ハフポス', 'url':'https://www.huffingtonpost.jp/feeds/index.xml'},
{'cite':'CNET', 'url':'http://feed.japan.cnet.com/rss/index.rdf'},
{'cite':'ギズモ', 'url':'https://www.gizmodo.jp/index.xml'},
{'cite':'ParsToday','url':'https://parstoday.com/ja/rss'},
{'cite':'ロイター', 'url':'https://assets.wor.jp/rss/rdf/reuters/top.rdf'}
]
# generate parameter strings
entry_param = ' | length=' + str(ENTRY_LENGTH) + ' href='
# menubar header
print(':newspaper.fill:')
print('---')
# bodies
for source in sources:
parsed = feedparser.parse(source['url'])
# show source
print (SUBMENU + parsed.feed.title)
# debug: to see keys list of entry
# print (parsed.entries[0].keys())
# show entries
no_display = min(len(parsed['entries']), MAX_ENTRIES)
for i in range(no_display):
e = parsed.entries[i]
t = ' '.join(e.title.split())
l = e.link
# set date and hour
if ('published_parsed' in e):
d = '{:02}'.format(e.published_parsed[IDX_DAY])
h = '{:02}'.format(e.published_parsed[IDX_HOUR])
elif ('date' in e):
# note: avoid date format hell...
d = e.date[8:10]
h = e.date[11:13]
else:
d = MISS_MONTH
h = MISS_HOURS
d = d + DAY_DELIM
h = h + HOUR_DELIM
print (SUBSUBMENU + d + h + t + entry_param + l)
スクリプトに対して、
$ chmod +x feedparser.60m.py
$ ll
-rwxr-xr-x@ 1 masatora staff 2369 1 1 17:35 feedparser.60m.py*
として、実行権限を与えます。
補足1. 実行環境
VS Codeだと、▶️ボタンを押すとTerminal
で実行してくれるんですね。これを発見してから、スクリプトを書くのがはかどりました。
補足2. ハマりどころ
feedparser
のドキュメントやイシューをみているとd.entries[0].published_parsed
で日付情報が取れるハズなんですけれども、実際には取れないサイトがあります。
print (parsed.entries[0].keys())
で、どういうキーを取得しているのかサイトごとに調べておいて、
if ('published_parsed' in e):
xxxx
elif ('date' in e):
xxxx
などと、存在するキーを確かめてから日付情報を取得するようにしました。
なお、日付情報が格納されているフォーマットは、
- standard Python 9-tuple in UTC
time.struct_time(tm_year=2021, tm_mon=12, tm_mday=2, tm_hour=8, tm_min=50, tm_sec=17, tm_wday=3, tm_yday=336, tm_isdst=0)
- ISO8601 format
2021-12-02T08:50:17.489Z
2022-01-01T08:51:00+09:00
の2種類でした(RSS提供元によっては他にもあるかも)。
とくにISO8601をきちんと日付オブジェクトに変換するのはハマりそうだったので深入りせず、文字列から日付と時刻だけを切り出すという簡易的な方法をとりました。
おわりに
さて、これでニュースを多少は見るようになるか!?
元ネタは、iOSや(さいきんはmacOSでもつかえるようになった)ショートカットアプリの「トップニュースをブラウズ」です。これをカスタマイズして、iPhoneでは便利につかっていました。ニュースを見るかどうかはムラがあるんですけども。
ショートカットも改良して、iPhoneでのニュース環境も改善したいなあ。
そのためにはまず「どのニュースサイトなら見る気になるか」の自己観察をしばらく続けてみることからでしょうか。