LoginSignup
5
5

More than 1 year has passed since last update.

macOSのメニューバーにお気に入りのニュースを表示

Last updated at Posted at 2022-01-02

背景

あまりニュースを見ない(新聞もテレビも)という悪いクセがあるので、MacBook Proのメニューバーに、ニュースのヘッドラインを表示できるようにしてみました。

  • ニュース提供元ごとに記事をまとめます
  • 記事が多い場合、15記事のみ表示します
  • 記事をクリックすると、ウェブサイトにジャンプします
  • 記事タイトルの頭に「日付-時間:」を表示します

という仕様にしています。

SS 2022-01-02 8.20.28.png

環境

ここで書いていることは、下記のバージョンで実施しました。

  • 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)

手順の概要

  1. SwiftBarのインストール(メニューバーカスタマイズツール)
  2. Pythonのインストール
  3. feedparser(Pythonモジュール)のインストール
  4. Pythonスクリプトの記述

SwiftBarというメニューバーカスタマイズツールがあり、これに対して標準出力を吐けばメニューバーに表示してくれます。RSSフィードのパーサとしてfeedparserがわりとよさそうだったので、つかうことにしました。Pythonスクリプトを書くのははじめてなので、あまり流儀に沿っていないところもあるかもしれません。ご容赦ください。

手順の詳細

1. SwiftBarのインストール

Rebuild.fm第292回で、SwiftBarというツールが紹介されていました。

macOSのメニューバーのカスタマイズツールで、スクリプトの標準出力をメニューバーに出してくれます。

SwiftBarのリリースページから最新版をzipダウンロードし、任意のフォルダ(ふつうはアプリケーションフォルダ?)に置き、起動します。プラグインフォルダを指定するように言われるので、適当なフォルダを指定します。

あまり深い階層にプラグインフォルダをおくと「ダメ」と言われるっぽいので、ユーザのホーム(~/)下にフォルダをおきました。

補足

BitBarというわりと有名なアプリがあったようです。これが新しいmacOSで使えなくなって、後継版としてxBarSwiftBarの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でのニュース環境も改善したいなあ。

そのためにはまず「どのニュースサイトなら見る気になるか」の自己観察をしばらく続けてみることからでしょうか。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5