Windows10にUnityインストールしてみる【2017.10.20現在】

Windows10にUnityインストールしてみる【2017.10.20現在】

名古屋でUnity初心者向けのハンズオンを行うことになったので、念のため自分も現在の段階でUnityをインストールしてみます。2017.10.20現在でのインストール情報となりますので、今後画面などは変わっていくと思いますが、ご了承ください。

ハンズオンイベントはこちらです。MashupAwardの一貫としてのハンズオンとなります。(MAの名古屋予選はもう終わっていたりしますが)大阪、福井、名古屋で開催し、今後は大垣でも開催が決定しています。

mashup-nagoya.connpass.com

 インストール作業

では、インストールを行いましょう。

ダウンロード

まずは公式サイトでインストーラーダウンロードします。

unity3d.com

画面内にある【プラン選択&ダウンロード】ボタンをクリックします。

f:id:ueponx:20171020171517j:plain

画面が遷移したら左側のPersonalの欄の【Persoinalを試す】をクリックします。

f:id:ueponx:20171020171529j:plain

更に画面が遷移して…

f:id:ueponx:20171020171545j:plain

画面を下にスクロールさせて、【ダウンロードするバージョンを選択してください】の下にある【Windows】ボタンをクリックします。

f:id:ueponx:20171020171554j:plain

少しするとインストーラのファイルのダウンロードが開始されますのでしばらく待ちます。

f:id:ueponx:20171020171605j:plain

指定した、フォルダにUnityDownloadAssistant-2017.2.0f3.exeが作成されていれば成功です。

f:id:ueponx:20171020171615j:plain

インストール作業

インストーラの実行ファイルが入手できたのでインストールを開始します。インストーラをダブルクリックするとインストーラのダイアログが開きます。【Next>】ボタンをクリックします。

f:id:ueponx:20171020171621j:plain

次はライセンスのダイアログ画面に遷移します。ダイアログ下にある【I accept the terms of the License Agreement】のチェックボックスにチェックを入れて、【Next>】ボタンをクリックします。(ライセンス文書は読んだ読んだほうがいいと思います。)

f:id:ueponx:20171020171629j:plain

次はインストールするコンポーネントの選択のダイアログが表示されます。 デフォルトのチェックリストで基本は大丈夫ですが、ちょっと注意なのは【Microsoft Visual Studio Community 2017】です。基本的にはインストールされるのですが、ここでインストールされるコンポーネントUnity開発用のコンポーネントのみなので、通常のデスクトップ開発を行う場合には、デスクトップ開発環境なども別途入れる必要があります。(VisualStudioのインストーラを起動して変更を行えば追加出来ます。)詳しくはページ下部の追記を参照。

f:id:ueponx:20171020171639j:plain

選択が終わったら、【Next>】ボタンをクリックします。続いて表示されるのはインストール先のフォルダ指定のダイアログになります。特に問題が内容であれば、【Next>】ボタンをクリックします。

f:id:ueponx:20171020171647j:plain

次は、Visual Studio 2017のライセンスのダイアログ画面です。ダイアログ下にある【I accept the terms of the License Agreement】のチェックボックスにチェックを入れて、【Next>】ボタンをクリックします。 これでインストールが開始されます。

f:id:ueponx:20171020171657j:plain

かなり時間がかかるので、他の作業を行ったほうがいいと思います。

f:id:ueponx:20171020171706j:plain

インストール作業も後半にくるとVisualStudioのインストールダイアログが表示されます。全く操作はいらないので待機します。 これが終わるとほぼインストール作業もほぼ終わりです。

f:id:ueponx:20171020171737j:plain

VisualStudioのインストールが終わってすぐ、Unityのインストール完了ダイアログが表示されます。ダイアログに【Launch Unity】のチェックボックスがあるのでチェックを入れて【Finish】ボタンをクリックします。これでインストーラが終了し、Unityが起動します。

初回起動時の作業

f:id:ueponx:20171020171726j:plain

Unityのスプラッシュスクリーンが表示されます。自分が以前インストールした時にはこんなにおしゃれだった記憶がありません。

f:id:ueponx:20171020171749j:plain

初回の起動ではUnityのアカウントのサインアップ画面が表示されます。以前はメールアドレスからの登録しかできなかったと思いますが、今ではGoogleアカウント、Facebookアカウントでもサインアップできるようです。すでに登録済みの方は、ログインしてください。新たに登録する方はメール受信することもありますので受信に気をつけてください。(少しかかるようです。)

f:id:ueponx:20171020171759j:plain

ログインができるとライセンスの画面になります。どちらかというと、ProやPlusのバージョンを使用するか、Personalバージョンを使うかの選択になります。ここでは【Personal】にチェックをいれて【Next】ボタンをクリックします。

f:id:ueponx:20171020171807j:plain

次の画面でも【I don't use Unity in a Professional capacity】を選択しておきます。会社や組織などの収入によって使用できるバージョンがあるためです。【Next】ボタンをクリックします。

f:id:ueponx:20171020171826j:plain

これでインストールと初回設定が完了しました。お疲れ様です。【Start Using Unity】ボタンをクリックして次に向かいましょう。

f:id:ueponx:20171020171836j:plain

プロジェクトを作成してみる

2回目以降にUnityを起動するとこのようなダイアログ画面になります。中央の【New Project】ボタンをクリックすると

f:id:ueponx:20171020171844j:plain

プロジェクト設定のダイアログが表示されます。作成するパスや3Dと2Dの選択などを入力して【Create project】ボタンをクリックすると、プロジェクトの生成が行われます。

f:id:ueponx:20171020171852j:plain

そして、以下のような画面が表示されます。あとはハンズオンのお楽しみというところかと思います。

f:id:ueponx:20171020171904j:plain

おわりに

ハンズオン準備のためのインストールということでエントリーを書いてみました。これからがスタートです!楽しいUnityライフをお迎えください!

追記

VisualStudioの設定は【設定】→【アプリと設定】を選択すると参照出来ます。

f:id:ueponx:20171020204132j:plain

検索ボックスでVisual Studioを入力

f:id:ueponx:20171020204146j:plain

Microsoft Visual Studio 2017】の【変更】ボタンをクリック

f:id:ueponx:20171020204645j:plain

インストール済みのVisual Studio Community 2017の【変更】ボタンをクリック

f:id:ueponx:20171020204656j:plain

ワークロードタブを下にスクロールすると

f:id:ueponx:20171020203946j:plain

【Unityによるゲーム開発】”のみ”にチェックがついていることがわかるかと思います。

UnityからVisualStudioのインストールを選択すると、【Unityによるゲーム開発】のモジュールがインストールされますが、デスクトップアプリなどの開発ができる【.NET デスクトップ開発】や【ユニバーサルWindowsプラットフォーム開発】はインストールされません。もし追加したければ、ここでチェックをつけることで追加インストールも出来ます。

「IoT×API de 街ハック!#MA_2017 名古屋ハッカソン予選」に参加してきました

「IoT×API de 街ハック!#MA_2017 名古屋ハッカソン予選」に参加してきました

f:id:ueponx:20171020001426p:plain

mashupawards.connpass.com

10月14日・15日に名古屋工業大学さんで行われたMashupAward名古屋予選に参加してきました。 今回は、一般参加でもあり、運営のお手伝いもさせていただきました。

f:id:ueponx:20171019144024j:plain

f:id:ueponx:20171019144038j:plain

会場に到着して会場準備作業へ、一番の難関はPepper2台の運搬。天気は微妙でしたがなんとか雨は保ってくれました。

f:id:ueponx:20171019233814j:plain

当日は欠席者もほとんど出ず、逆にお手伝いに駆けつけてくれる方も来てくれてかなり助かりました。

今年のMA_2017の初期のカレンダーをみると、実は名古屋の開催はスケジュールに含まれていなかったのですが、 名古屋で開催してほしいと要望を出したところ、うまくことが運び、開催に至ることができました。 ただ、この日は色々な行事との重なりなどがあり、事前ではあまり参加者の伸びが芳しくなかったです。 ただ、告知やLTなどにも行くようにしたこともあり、初参加の方とベテランの方のバランスがいい感じになったような気がします。(直前のIoTLT、GeekBar、Code for Nagoyaが効いたかなと思います)

f:id:ueponx:20171019144504j:plain

今回のイベントは鈴木まなみさんがファシリテーションを行ってもらっていたのですが、自分はまなみさんの ファシリテーションは初めてだったのでかなり新鮮でした。

f:id:ueponx:20171019144636j:plain

ハッカソンのテーマは「IoT×APIで街ハック」の3つのキーワードを絡めてでした。「街」というと町おこしというイメージがありますが、生活するフィールドとして捉えて、それを楽しく!尖った感じ!にアイデアを考えようと捉えていました。

f:id:ueponx:20171019144610j:plain

自分としては街にある既存のものを面白くするのもどうかなと思っていたのですが、それだとアイデアとしては「ありきたり」なのでもっとぶっ飛んだ何かを作らないとだめかなと思いました(MAは一応コンテストなので評価されるものなので)

念のため審査基準

  • チャレンジ度(ぶっ飛び度、変化球、新規性、アイデア
  • ギーク度(突き抜け度、こだわり度、完成度)
  • ワクワク感(表現手法、体験、人を楽しませる仕組み、デザイン)

後は、ハッカソンのテーマ性がこれに加わる感じです。(今回であれば「街」というキーワードとなります。)

一日目の午前中は、参加者みんなでアイデアソンと企業の皆さんからのインプットと続き、その後チームビルディングへと進みます。冒頭で自己紹介があったのは初めて体験したような気がします。

f:id:ueponx:20171019233400j:plain

私は一日目のこの時間が一番楽しい時間だと思っているので、まだハッカソンを経験したことのない方は是非体験してほしいと思っています。実際にものづくりになると、個人のスキルなどに依存する作業が多くなります。しかし、この時点でのアイデア出しはそういうこととは関係なくテーマに対して自由な発想で考えて行くことができます。

今回ご提供頂いたテクニカルサポート企業(APIなど)は以下のような感じでした。

f:id:ueponx:20171019234521j:plain f:id:ueponx:20171019234554j:plainf:id:ueponx:20171019234602j:plainf:id:ueponx:20171019234609j:plainf:id:ueponx:20171019234620j:plain

他にも持ち込み機材として

がありました。

自分が今回出したアイデアは過去の既視感を音楽や環境音で表現するような感じで、訪れた場所や人に身に着けたセンサーなどでその音楽を味付けする感じのもの(過去あった建造物ものに関連する環境音とか)だったような気がします。(あんまり記憶がない…)タイトルは「デジャーブ」

f:id:ueponx:20171019160611j:plain

その後、マホーのキーワード

f:id:ueponx:20171019145241j:plain

これを唱えながら、チーム結成を行っていきます。

今回は比較的似ていたアイデアを融合させて以下のような、「JamPepper」を作ることにしました。 Pepper、センサーなどを組み合わせるところが技術的なポイントです。これを街のホットスポットに設置してジャム・セッションを行って、街を楽しくしていこうというものになります。

【JamPepper草稿】 f:id:ueponx:20171019145322j:plain

このアイデアではPepperとセンサーを通信でつなぐのですが、自分はセンサーデータをサーバ側にアップロードする担当になりました。持ち込みのセンサーに3次元ジャイロ(Grove接続)があったのでそれと、個人で持ち込んでいたNefryBT(きびだんごで購入)を使い、Wifi経由でサーバ(今回はローカルサーバ)へセンサー情報を送っています。

ジャイロセンサー(温度センサーも入っている…すげ)

www.seeedstudio.com

NefryBT(ESP32でWiFiもBLEもいけて、Groveコネクタもある)

kibidango.com

ジャイロのデータはすぐ取得できた(さすがGrove接続!)ので通信部分を頑張っていたのですが、グローバル通信ではうまくいくのにローカルでは送れないという状況になり、そこで半日ほど使ってしましました。全体的な開発状況としては30%も行ってなかったような感じでかなり課題の残る一日目でした。

一日目の夜にNefryBTの作者のわみさんに通信動作に関してDメッセージで質問したりしたのですが、アドバイス頂いた処理を使っても、結局動作できず一日目の深夜が終わりました。深夜にご対応していただいてありがとうございます>わみさん

2日目の早朝にもう一度通信テストを行ってみると、問題なく動作していました。うまく行かなかったソースがなにもしなくて動作していたので小人の仕業かと思ってしました。でも少しソースをいじって(IPアドレス程度)NefryBTに書き込みをすると通信ができなくなる様でした。

個人的な予想なのですが、開発している際にリセットなどを頻繁に行っているためにIPアドレスの取得がうまく行かなくなることがあるのかなと考えました。(起動ログではIPは取れていると出ていますがpingすると疎通できていない感じ)この現象は簡単には解決できそうになかったので、まずはセンサー情報をシリアル経由でサーバ側にアップすることにし、時間があれば通信を行うという方向にしました。(デモでは無線通信はつかえませんけど)

かたや、Pepper側もなかなか難しい状況で開発環境のCoregraphの調子やPepperとの接続が悪くなったりということが突然やってきたりして大変だったようです。

そんなこんなで2日目の午前はあっという間に過ぎ、2日目の昼食が終わると残り2時間という感じになります。デモ中心の発表になるのでそこからはどれぐらいエッセンスを見せられるかを絞っての開発・調整となりました。

15:30ハッキングタイム終了です。

今回のハッカソンでは合計5チーム(※)が結成されて、発表をしています。(※1チームはボッチソンでしたが)

審査員は名古屋大学の河口先生、北名古屋市議の桂川さん、IAMAS小林先生だったので、かなり仕上がりとプレゼンの出来をバキうちされるのではないかと、ヒヤヒヤしていたのですがなんとかなったようです。

開発ばかりで頭が辛かったので、今回のプレゼンではペッパーとダンスをするパフォーマンスをしてみました。

f:id:ueponx:20171019145018j:plain f:id:ueponx:20171019145029j:plain

結果!ハッカソン最優秀賞は「CoT-Claimer of Toilet-」チーム名(QoT)となりました。汚いトイレの公開処刑はいいワードです。

f:id:ueponx:20171019144859j:plain f:id:ueponx:20171019235649j:plain f:id:ueponx:20171019144828j:plain

自分のチームはKonashi賞をいただけました(ありがたい)

hacklog.jp

センサーをハッカソンで使うのはかなり予習をしていないとだめだなーというのが今回の反省です。でも、久しぶりに頑張ってコードを書いていたので満足感半端なかったです。通信部分も含めてもう少しブラッシュアップしたいなと思っています。(予選通過したものよりその後ブラッシュアップしたもののほうが2次予選でいい成績を残すものも多いらしいので。)

ただPepperはないからなあ…と思っていたら、期間中は名古屋工業大学さんで使えるようになるそうです。

今回、名古屋でMAのハッカソンがないのはヤダーというところからのスタートでしたが、無事に開催し終了を迎えることができて本当に良かったです。こういうイベントは一度なくなるとなかなか復帰するのが大変そうだったので、あえて無理をしてでも開催したいと思っていました。

今回、無理を言って会場をお貸し出しいただいた白松先生本当にありがとうございました。 また、テクノロジーサポート企業の方々、まなみん、伴野さん、そして参加されたみなさんも本当にありがとうございます。

来年も名古屋でハッカソンのイベントやりたいです!

ちなみにこの一件をきっかけにIT系のコミュニティを立ち上げました。Mashup名古屋というコミュニティになります。正式にはまた告知をしようと思っています。

RaspberryPiでRFID-RC522を使ってみる【失敗編】

RaspberryPiでRFID-RC522を使ってみる【失敗編】

このエントリーは失敗編です。失敗に至るメモのため、情報が基本ないので読み飛ばしてください。

以前の勉強会で教えてもらった(もう数年も前になりますが)NFCのタグシールを何かに使えないかと思って購入してみました。そのまま使うのもなんなのでArduinoやRaspberryPiでも使えないかなと思って以下も同時に購入してみました。

リーダーもこの値段で購入できるんですね。いい時代なのか、それともちょっとやそっとではお金を稼ぐことができない時代なのか。

このリーダーをネットで検索すると、こちらのリンクにヒットしました。結構Arduinoのネタは多いようなんですが、RaspberryPiはすくないようです。

webcache.googleusercontent.com

ふむふむと読んでいくと…

・the kernel to use Device Tree?
 で Noを選択します。(コレ重要!)

raspi-configコマンドで表示されるメニューで設定値が表示されるようなのですが、うちのRaspberryPiでは表示されないようです。なぜ?カーネルのバージョンなどで違いがあるのでしょうか。念のため、昔使っていたRaspberryPi2でも同様の設定は出ませんでしたし。ネットを検索してもそれっぽい情報はありませんでした。

とりあえず、とても嫌な予感がしますが、そのまま進めていくことにします。

候補としては以下の2つになると思います。そろそろC言語はつらいので…

  • SPI-Py(SPI制御ライブラリ)&MFRC522-pythonの組合せ
  • pi-rc522

今回は、候補の中で使用するのはPythonからこのリーダーを扱えるpi-rc522モジュールになります。 リンク先のページではgitでCloneしてインストールしていますが、ヌルいPython使いの自分はpipでインストールしました。

github.com

$ pip install pi-rc522
Collecting pi-rc522
  Downloading pi-rc522-2.2.1.tar.gz
Collecting RPi.GPIO (from pi-rc522)
  Downloading RPi.GPIO-0.6.3.tar.gz
Collecting spidev (from pi-rc522)
  Downloading spidev-3.2.tar.gz
Building wheels for collected packages: pi-rc522, RPi.GPIO, spidev
  Running setup.py bdist_wheel for pi-rc522 ... done
  Stored in directory: /home/pi/.cache/pip/wheels/83/45/21/0bc28b47c8dfb87d00d806b10c8ffb733e36a8b927668ce42b
  Running setup.py bdist_wheel for RPi.GPIO ... done
  Stored in directory: /home/pi/.cache/pip/wheels/ae/4d/3b/e924997dbf06810adf3b2e37f1d9627b2327eb9cbb285949c9
  Running setup.py bdist_wheel for spidev ... done
  Stored in directory: /home/pi/.cache/pip/wheels/e4/9b/5f/cf0ec030fc958b72315a15412130e4e1dc6040cdb490aa21fb
Successfully built pi-rc522 RPi.GPIO spidev
Installing collected packages: RPi.GPIO, spidev, pi-rc522
Successfully installed RPi.GPIO-0.6.3 pi-rc522-2.2.1 spidev-3.2

同時にGPIOやSPIのモジュールもインストールされるようです。GitでCloneするとサンプルコードも同時に手に入るのですが、pipインストールでは手に入らないので、Githubにいってサンプルを取得します。

【サンプルページ】 github.com

$ wget https://raw.githubusercontent.com/ondryaso/pi-rc522/master/examples/Read.py
--2017-09-18 03:08:03--  https://raw.githubusercontent.com/ondryaso/pi-rc522/master/examples/Read.py
raw.githubusercontent.com (raw.githubusercontent.com) をDNSに問いあわせています... 151.101.72.133
raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 983 [text/plain]
`Read.py' に保存中

Read.py             100%[===================>]     983  --.-KB/s    in 0s

2017-09-18 03:08:03 (6.98 MB/s) - `Read.py' へ保存完了 [983/983]

RFIDのリーダーとの接続は RC522側の IRQだけ接続不要です。

RC522モジュール RaspberryPi
1 3.3V GPIO +3.3V(3V3)
2 RST GPIO GPIO25
3 GND GPIO GND(GND)
4 MISO GPIO MISO(GPIO9)
5 MOSI GPIO MOSI(GPIO10)
6 SCK GPIO SCLK(GPIO11)
7 SDA GPIO CE0(GPIO8)

以下のように実行すればうまく…

【Read.py】

#!/usr/bin/env python

import signal
import time
import sys

from pirc522 import RFID

run = True
rdr = RFID()
util = rdr.util()
util.debug = True

def end_read(signal,frame):
    global run
    print("\nCtrl+C captured, ending read.")
    run = False
    rdr.cleanup()
    sys.exit()

signal.signal(signal.SIGINT, end_read)

print("Starting")
while run:
    rdr.wait_for_tag()

    (error, data) = rdr.request()
    if not error:
        print("\nDetected: " + format(data, "02x"))

    (error, uid) = rdr.anticoll()
    if not error:
        print("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]))

        print("Setting tag")
        util.set_tag(uid)
        print("\nAuthorizing")
        #util.auth(rdr.auth_a, [0x12, 0x34, 0x56, 0x78, 0x96, 0x92])
        util.auth(rdr.auth_b, [0x74, 0x00, 0x52, 0x35, 0x00, 0xFF])
        print("\nReading")
        util.read_out(4)
        print("\nDeauthorizing")
        util.deauth()

        time.sleep(1)

はずだ…

$ python Read.py
Starting

10分後…全く動かねえ…

ではということで

方法その1であった、SPI-Py(SPI制御ライブラリ)&MFRC522-pythonの組合せもやってみます。

$ git clone https://github.com/lthiery/SPI-Py.git
$ cd SPI-Py
$ sudo python setup.py install
$ cd ..
$ git clone https://github.com/mxgxw/MFRC522-python.git
$ cd MFRC522-python
$ sudo python Read.py

これでも動作せず。/(^o^)\オワタ。

もう少し調べてみる。

ハード側が怪しいということも疑って、Aruduinoでも使用可能なのでそちらでハード側のテストを行ってみました。 Arduinoでやっている例は結構あるので、こちらのほうが楽かなとは思いますが、Unoでは電圧が5V系なのでその部分は注意が必要です。(リーダー系は3.3V系なので)

Arduinoで近距離無線通信 RFID-RC522 NFC by ボクにもわかる地上デジタル

github.com

f:id:ueponx:20170918044058p:plain

実際に動作させてみると…一応、リードできてる?UIDは普通に読めていますがデータブロックは読み取れていません。 (たまにブロックは読み込めることもある…。RFIF側のUIDの読み取りに空振ることもあるぽい…。) ハード的にはかなり怪しいようです。

粘ってみる…その1

更に情報をネットを検索すると

Raspberry PI 3 and RFID-RC522 Problem reading data - Raspberry Pi Forums

なる情報もdtoverlay=spi0-hw-cs/boot/config.txtの設定の追加のようです。

$ sudo vim /boot/config.txt

【変更前】

(略)
# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on
(略)

↓ 【変更後】

(略)
# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on
dtoverlay=spi0-hw-cs
(略)

dtはDeviceTreeの略のようです、最近ではraspi-configではなく/boot/config.txtで設定を行う様です。 でも、こちらを追加したのですが駄目でした。

粘ってみる…その2

ならばということでそもそもOSを変えてやれってバージョンを2つ落として2015-05-05-raspbian-wheezyにしてみました。 これで動かなかったらハードだろぐらいの気持ちです。

wheezyでは、jessie以降にはなかったDevice Treeの設定はちゃんとraspi-configコマンド内にも有効無効は存在しています。 それを設定すると検索でヒットするものと同じ状態になるみたいです。

$ dmesg  | grep spi
[    6.733677] bcm2708_spi bcm2708_spi.0: master is unqueued, this is deprecated
[    7.008711] bcm2708_spi bcm2708_spi.0: SPI Controller at 0x20204000 (irq 80)

$ lsmod |grep spi
regmap_spi              2307  1 snd_soc_wm8804
spi_bcm2708             6018  0

$ ls /dev/spidev* -l
crw-rw---T 1 root spi 153, 0  11  1970 /dev/spidev0.0
crw-rw---T 1 root spi 153, 1  11  1970 /dev/spidev0.1

ですが、動作しません。ムキー。

f:id:ueponx:20170918155520p:plain

粘ってみる…その3

よくわからない気分になってきたので、更に粘ってみました。今度はIchigoJamです。

fukuno.jig.jp

ドライバーもなにもないので、これでだめならハードが悪い(もうそんな気分ですが)と諦められます。

f:id:ueponx:20170918165753j:plain

結果、駄目でした…読み取り待ちにはなってくれるのですが、それ以上の処理には続かないようです。 このためにIchigoJamの使い方をさらに勉強とは、すごい自分にもやる気があるんだなと思いました(棒)

f:id:ueponx:20170918155910p:plain

終わりに

ハードさえ動けばやりたいこと全部やりきった感あります。 新規にハード買うかして再チャレンジしたいと思います。

f:id:ueponx:20170918160821p:plain

2日使ったのでかなり寝たいです。

RaspberryPiでTwitterのタイムラインを読み上げてみる(Amazon Polly & Twitter & Python)

RaspberryPiでTwitterのタイムラインを読み上げてみる(Amazon Polly & Twitter & Python

以前のエントリーでAmazon Polly(以下Polly)を使っていましたが、流石にもう少しちゃんと動くものを作ってみようかなと思います。具体的にはTwitterのタイムラインを取得してそれを読み上げようと思います。これまでも似たようなものとして、OpenJTalkで読み上げを行っていましたがOpenJTalkでは文章量が長くなると途中できれてしまうということが稀にあり、Pollyではそんなこともないと信じたいのでチャレンジしてみました。(Pollyは有料サービスなんでそんなには使えないですけどね)

f:id:ueponx:20170914195730p:plain f:id:ueponx:20170914195636p:plain

この組み合わせは Polly & Twitterバードの鳥鳥コンビなのはちょっと楽しい。

インストール

TwitterのタイムラインをPythonで取得するtweepyは以前のエントリーでも使っていますが、かなり前の話なので念のためインストールについても書いておきます。といってもpipを使えばそれでOKです。

uepon.hatenadiary.com

$ sudo pip install tweepy
Collecting tweepy
  Downloading tweepy-3.5.0-py2.py3-none-any.whl
Requirement already satisfied: six>=1.7.3 in /usr/lib/python2.7/dist-packages (from tweepy)
Requirement already satisfied: requests>=2.4.3 in /usr/local/lib/python2.7/dist-packages (from tweepy)
Requirement already satisfied: requests-oauthlib>=0.4.1 in /usr/lib/python2.7/dist-packages (from tweepy)
Requirement already satisfied: oauthlib>=0.6.2 in /usr/lib/python2.7/dist-packages (from requests-oauthlib>=0.4.1->tweepy)
Installing collected packages: tweepy
Successfully installed tweepy-3.5.0

とても簡単です。

あとはPollyですが、これでも以前のエントリーを参考にしてインストールしておきます。bot3のインストールを行えばOKです。

uepon.hatenadiary.com

これでPythonからTwitterPollyを使う準備が整いました。

読み上げプログラムを作ってみる

行おうとしているプログラムは基本的には以下の動作から構成されます。

  • Twitterの認証(tweepyモジュールの使用)
  • タイムラインの取得(tweepyモジュールの使用)
  • タイムラインの不要な部分の削除(正規表現:reモジュールの使用)
  • Pollyへテキストの送信とファイルへの保存(bot3へのアクセス)
  • pygameのmixerで再生

こちらがソースの全体となります。(関数化とかしてない…)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tweepy
import re
import datetime

from boto3 import Session
from botocore.exceptions import BotoCoreError, ClientError
from contextlib import closing
import os
import sys
import subprocess
import pygame.mixer
import time

# 個別にTwitterクライアント作成時に設定されるCONSUMER_KEY、CONSUMER_SECRETを設定してください
CONSUMER_KEY = 'XXXXXXXXXXXXXXXXXXX'
CONSUMER_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth_url = auth.get_authorization_url()

print('Please authorize: ' + auth_url)
verifier = raw_input('PIN: ').strip()

auth.get_access_token(verifier)

print("ACCESS_KEY = '%s'" % auth.access_token)
print("ACCESS_SECRET = '%s'" % auth.access_token_secret)

ACCESS_KEY = auth.access_token
ACCESS_SECRET = auth.access_token_secret

auth.set_access_token(ACCESS_KEY, ACCESS_SECRET)
api = tweepy.API(auth)
print(api.me().name)

now = datetime.datetime.now()
print(now)

# api.update_status('Tweepy! Sample start! ' + str(now))

session = Session(region_name="us-west-2")
polly = session.client("polly")

public_tweets = api.home_timeline()
for i, tweet in enumerate(public_tweets):
    print('---{}---'.format(i))
    print(tweet.author.screen_name + ":")
    # print(tweet.text)

    text = re.sub('RT', "", tweet.text)
    text = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
    text = re.sub(r'[##]([\w一-龠ぁ-んァ-ヴーa-z]+)', "", text)
    text = re.sub(r'@([\w]+):', "", text)
    text = re.sub('\s', "", text)

    print(text)

    try:
        response = polly.synthesize_speech(
            Text=text, OutputFormat="mp3", VoiceId="Mizuki")
    except (BotoCoreError, ClientError) as error:
        print(error)
        sys.exit(-1)
    if "AudioStream" in response:
        with closing(response["AudioStream"]) as stream:
            output = "speech.mp3"
            try:
                with open(output, "wb") as file:
                    file.write(stream.read())
            except IOError as error:
                print(error)
                sys.exit(-1)
        print("synthesize_speech OK ->>" + output)
    else:
        print("Could not stream audio")
        sys.exit(-1)

    pygame.init()
    pygame.mixer.init()
    pygame.mixer.music.load("speech.mp3")
    pygame.mixer.music.play()
    print("PLAY")
    while pygame.mixer.music.get_busy() == True:
        continue
    print("END")

    pygame.mixer.music.stop()
    pygame.mixer.quit()
    pygame.quit()

ソースの説明

以下では部分的に説明をしていきます。

Twitterの認証(tweepyモジュールの使用)

以下の部分でTwitterに関する認証を行っています。 CONSUMER_KEYCONSUMER_SECRETTwitterのアプリの作成画面に出ます。これを使って認証用のURLを呼び出し、PIN生成を行っていきます。 表示されるURLをブラウザに与えることでPIN表示まで持っていきます。

f:id:ueponx:20170916083004p:plain

起動すると以下のようなPINの入力待ち画面になります。

f:id:ueponx:20170914222010p:plain

この処理を行っているのが以下の処理になります。

CONSUMER_KEYCONSUMER_SECRETの値がXXXXX…となっていますがTwitterのアプリケーション設定画面の

  • Consumer Key (API Key)
  • Consumer Secret (API Secret)

の値を入れるようにしてください。

f:id:ueponx:20170914222407p:plain

Pythonの処理部分】

# 個別にTwitterクライアント作成時に設定されるCONSUMER_KEY、CONSUMER_SECRETを設定してください
CONSUMER_KEY = 'XXXXXXXXXXXXXXXXXXX'
CONSUMER_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth_url = auth.get_authorization_url()

print('Please authorize: ' + auth_url)
verifier = raw_input('PIN: ').strip()

表示されたURLをクリックすると

f:id:ueponx:20170914222645p:plain

認証画面に遷移します。【連携アプリを認証】をクリックします。(ログインを事前にしていないと、ログインを促された後にこの画面になるかもしれません。)

f:id:ueponx:20170914222912p:plain

すると7桁のPINコードが表示されるので、この値をコンソールに入力します。

PINを入力すると実際に使用するACCESS_TOKENACCESS_TOKEN_SECRETを取得する処理となります。

Pythonの処理部分】

auth.get_access_token(verifier)

print("ACCESS_KEY = '%s'" % auth.access_token)
print("ACCESS_SECRET = '%s'" % auth.access_token_secret)

ACCESS_KEY = auth.access_token
ACCESS_SECRET = auth.access_token_secret

auth.set_access_token(ACCESS_KEY, ACCESS_SECRET)
api = tweepy.API(auth)

これでアプリケーションの使用に必要なACCESS_KEYACCESS_TOKEN_SECRETの取得ができました。あとは認証処理に与えれば完了となりますauth.set_access_token(ACCESS_KEY, ACCESS_SECRET)の部分になります。これでTwitter APIの処理を行える様になりました。 本来であればこれらキーはファイルに保存して再利用するのですが、今回は端折っています。保存する場合にはアクセス権などには注意してください。

タイムラインの取得(tweepyモジュールの使用)

APIの使用ができるようになればタイムラインの取得は簡単です。今回はHomeタイムラインを取得しています。

Pythonの処理部分】

public_tweets = api.home_timeline()
for i, tweet in enumerate(public_tweets):
    print('---{}---'.format(i))
    print(tweet.author.screen_name + ":")
    # 表示する場合には以下のコメントを外してください
    # print(tweet.text)

取得したタイムラインはデフォルト件数は20件になります。必要であれば引数に追加を与えることになりますが、Pollyが有料サービスなのでこのままにしておきます。

タイムラインの不要な部分の削除(正規表現:reモジュールの使用)

タイムラインの文面から不要なURLなどを正規表現で削除して読み上げに適した文面に変換します。さすがにURLを読み上げられても?となってしまうので。

以下は正規表現でURL、【RT】接頭辞、ハッシュタグ、ユーザ名の削除を行いプレーンな文章に変換をして行きます。

Pythonの処理部分】

    text = re.sub('RT', "", tweet.text)
    text = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
    text = re.sub(r'[##]([\w一-龠ぁ-んァ-ヴーa-z]+)', "", text)
    text = re.sub(r'@([\w]+):', "", text)
    text = re.sub('\s', "", text)

これで完璧というわけではないのですがおおよそ大丈夫かなと思います。

Pollyへテキストの送信とファイルへの保存(bot3へのアクセス)

得られたタイムラインのデータをbot3にアクセスして、Pollyが読み上げした音声のデータを取得していきます。

このあたりは過去エントリー参照で大丈夫でしょう。

uepon.hatenadiary.com

Pythonの処理部分】

    print(text)

    try:
        response = polly.synthesize_speech(
            Text=text, OutputFormat="mp3", VoiceId="Mizuki")
    except (BotoCoreError, ClientError) as error:
        print(error)
        sys.exit(-1)
    if "AudioStream" in response:
        with closing(response["AudioStream"]) as stream:
            output = "speech.mp3"
            try:
                with open(output, "wb") as file:
                    file.write(stream.read())
            except IOError as error:
                print(error)
                sys.exit(-1)
        print("synthesize_speech OK ->>" + output)

この処理が終わるとPollyで生成された音声ファイルはspeech.mp3とされて保存されます。

pygameのmixerで再生

あとは変換された音声を再生するだけです。

Pythonの処理部分】

    pygame.init()
    pygame.mixer.init()
    pygame.mixer.music.load("speech.mp3")
    pygame.mixer.music.play()
    print("PLAY")
    while pygame.mixer.music.get_busy() == True:
        continue
    print("END")

    pygame.mixer.music.stop()
    pygame.mixer.quit()
    pygame.quit()

こちらも先程のエントリーを参考にしてもらえればと思います。(初期化処理・終了処理は毎回しなくてもいいかなとは思いますが。)

終わりに

ようやくPythonを使ってTwitterのタイムラインを取得して読み上げることができました。 まあ、読み上げられた文面もなんとか分かるかなという感じですがAAや絵文字なんかがあると謎の文面になってしまいますね。さすがに仕方ないですね。 長い文章での読み上げがうまくいくなら、ネットワークアクセスがある分遅くなってしまいますのでOpenJTalkの方がいいかも。

コンソールTwitterクライアント```rainbowstream```をつかってみた

コンソールTwitterクライアントrainbowstreamをつかってみた

なんとなく、linux環境でもコンソールでTwitterのタイムラインを見てみたいなあと思って、クライアントをさがしていました。(家で使うのはRaspbianだけなんで気まぐれな欲求なんですが)

その中でもなかなかいいなと思ったのがrainbowstreamでした。

テーマで色分けができたりというのがかなり見やすくていいかなと思いますし、気合を入れれば画面もそれなりにわかるというのもすごいと思います。そういう用途はGUI環境でやればいいとは思いますが。

ってことでインストールしてみました。

rainbowstreamのインストール

以下のリンクを参照にすれば簡単にインストールできます。環境はpythonなので本体はpipでのインストールになりますが、いろいろLibraryの依存があるのでそれも一緒にインストールする必要があります。

github.com

ドキュメントにはvirtualenvを使った方がいいということでしたが、今回は端折ってそのままインストールしています。

$ sudo apt-get install python-dev libjpeg-dev libfreetype6 libfreetype6-dev zlib1g-dev
$ sudo pip install rainbowstream

【参考】 Pipenv & Virtual Environments — The Hitchhiker's Guide to Python

インストールは問題なく終わると思います。(本体以外のLibraryもインストール済みのものがほとんどでした。)

rainbowstreamを実行してみる

実行は以下のようにするだけです。

$ rainbowstream

初回の実行ではTwitterアプリケーションのPIN認証が求められるので

f:id:ueponx:20170913232333p:plain

画面に表示されたURLをクリックしてブラウザで認証を行います。リンクをクリックすると以下のような画面になるので

f:id:ueponx:20170910153740p:plain

【連携アプリを認証】ボタンをクリックします。すると7桁数字のPINが表示されるのでそれをコピーなどしてコンソールに入力します。(画面では伏せています)

※2回目以降の起動では初回起動時の認証情報が~/.rainbow_oauthに格納されるでの認証は不要です。

f:id:ueponx:20170910154556p:plain

するとログインが行われて

f:id:ueponx:20170910154656p:plain

コマンドモードになりますが、このままほっておくと自分のタイムラインが表示されます。

f:id:ueponx:20170910155018p:plain

コマンドマニュアルはこちらになります。

Rainbow Stream — RainbowStream 1.3.7 documentation

おわりに

複数のTLを監視しない範囲であればコマンドラインでの実行でもかなりいいかなと思います。数世代前のPCでLinuxを動かす場合にはこちらは軽快かもしれませんねです。(Aspire one 753 Celeron U3400 / 2GBメモリー / USBメモリブート/ RaspberryPixel86)

本当は、この結果が標準出力されて、それを使ってpollyとかから読み上げでも行おうと思っていたのですが、標準出力はしてくれないようです。残念。 次回では真面目にPythonでTweepyを使ってタイムラインを拾って読み上げをさせてみたいと思います。

yapfの導入

yapfの導入

個人の開発スタイルのような感じですが、Windowsなどでpythonのソースを書いていて、vimへコピー・ペーストをすることがよくあるのですが、何故かインデントのフォーマットが異なってしまうので、そのあとの手間が多いのでなんとかしないと行けないなあと思っていました。(他の言語ならそれほど大きく問題はありませんが、pythonではそういうわけにも行きません。)

そこで、yapfとういうフォーマッタがあることがわかりました。

github.com

googleさん謹製のようです。

yapfのインストール

readmeを見ればわかりますが以下の様にインストールします。

$ pip install yapf
Collecting yapf
  Downloading yapf-0.17.0-py2.py3-none-any.whl (151kB)
    100% |????????????????????????????????| 153kB 1.1MB/s
Installing collected packages: yapf
Successfully installed yapf-0.17.0

raspberrypiでインストールするとyapf~/.local/bin/yapfにインストールされるようです。

実行するとこんな感じになります。

$ ~/.local/bin/yapf --version
yapf 0.17.0

$ ~/.local/bin/yapf --help
usage: yapf [-h] [-v] [-d | -i] [-r | -l START-END] [-e PATTERN]
            [--style STYLE] [--style-help] [--no-local-style] [-p]
            [files [files ...]]

Formatter for Python code.

positional arguments:
  files

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show version number and exit
  -d, --diff            print the diff for the fixed source
  -i, --in-place        make changes to files in place
  -r, --recursive       run recursively over directories
  -l START-END, --lines START-END
                        range of lines to reformat, one-based
  -e PATTERN, --exclude PATTERN
                        patterns for files to exclude from formatting
  --style STYLE         specify formatting style: either a style name (for
                        example "pep8" or "google"), or the name of a file
                        with style settings. The default is pep8 unless a
                        .style.yapf or setup.cfg file located in one of the
                        parent directories of the source file (or current
                        directory for stdin)
  --style-help          show style settings and exit; this output can be saved
                        to .style.yapf to make your settings permanent
  --no-local-style      don't search for local style definition
  -p, --parallel        Run yapf in parallel when formatting multiple files.
                        Requires concurrent.futures in Python 2.X

テスト

公式サイトのサンプルを使って実験してみます。

github.com

x = {  'a':37,'b':42,

'c':927}

y = 'hello ''world'
z = 'hello '+'world'
a = 'hello {}'.format('world')
class foo  (     object  ):
  def f    (self   ):
    return       37*-+2
  def g(self, x,y=42):
      return y
def f  (   a ) :
  return      37+-+a[42-x :  y**3]

f:id:ueponx:20170910140531p:plain

$ ~/.local/bin/yapf sample.py
Traceback (most recent call last):
  File "/home/pi/.local/bin/yapf", line 11, in <module>
    sys.exit(run_main())
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 296, in run_main
    sys.exit(main(sys.argv))
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 188, in main
    parallel=args.parallel)
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 236, in FormatFiles
    in_place, print_diff, verify)
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 259, in _FormatFile
    logger=logging.warning)
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/yapflib/yapf_api.py", line 91, in FormatFile
    verify=verify)
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/yapflib/yapf_api.py", line 129, in FormatCode
    tree = pytree_utils.ParseCodeToTree(unformatted_source)
  File "/home/pi/.local/lib/python2.7/site-packages/yapf/yapflib/pytree_utils.py", line 102, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/usr/lib/python2.7/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/usr/lib/python2.7/lib2to3/pgen2/driver.py", line 47, in parse_tokens
    for quintuple in tokens:
  File "/usr/lib/python2.7/lib2to3/pgen2/tokenize.py", line 429, in generate_tokens
    ("<tokenize>", lnum, pos, line))
  File "sample.py", line 11
    def g(self, x,y=42):
    ^
IndentationError: unindent does not match any outer indentation level

動かない…エラーメッセージから察するに、Webページのものをコピペして使うとpythonの文法まで崩れてしまっているようです。

Teratermのウインドウにドラッグ・アンド・ドロップすることで実行できる、scpなどでファイル転送をすると…

$ cat sample.py
x = {  'a':37,'b':42,

'c':927}

y = 'hello ''world'
z = 'hello '+'world'
a = 'hello {}'.format('world')
class foo  (     object  ):
  def f    (self   ):
    return       37*-+2
  def g(self, x,y=42):
      return y
def f  (   a ) :
  return      37+-+a[42-x :  y**3]

$ ~/.local/bin/yapf sample.py
x = {'a': 37, 'b': 42, 'c': 927}

y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}'.format('world')


class foo(object):
    def f(self):
        return 37 * -+2

    def g(self, x, y=42):
        return y


def f(a):
    return 37 + -+a[42 - x:y**3]

うまく実行できたようです。ファイルを変換したものに上書きするには-iをつければいいようです。他にも処理後との差分(diff)を表示する-d再帰的に実行する-rのスイッチがあります。

$ ~/.local/bin/yapf -i sample.py
pi@raspberrypi:~/yapf_sample $ cat sample.py
x = {'a': 37, 'b': 42, 'c': 927}

y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}'.format('world')


class foo(object):
    def f(self):
        return 37 * -+2

    def g(self, x, y=42):
        return y


def f(a):
    return 37 + -+a[42 - x:y**3]

REPL環境での実行でモジュールとして使用することもできる様です。 githubのページでは以下のようになると書いてあるのですが、

>>> from yapf.yapflib.yapf_api import FormatCode  # reformat a string of code

>>> FormatCode("f ( a = 1, b = 2 )")
'f(a=1, b=2)\n'

自分の環境では

$ python
Python 2.7.13 (default, Jan 19 2017, 14:48:08)
[GCC 6.3.0 20170124] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from yapf.yapflib.yapf_api import FormatCode  # reformat a string of code
>>> FormatCode("f ( a = 1, b = 2 )")
(u'f(a=1, b=2)\n', True)
>>>

となりました。python2だったからかな?念のためpython3でも実行してみました。

$ pip3 install yapf
Collecting yapf
  Using cached yapf-0.17.0-py2.py3-none-any.whl
Installing collected packages: yapf
Successfully installed yapf-0.17.0

$ python3
Python 3.5.3 (default, Jan 19 2017, 14:11:04)
[GCC 6.3.0 20170124] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from yapf.yapflib.yapf_api import FormatCode
>>> FormatCode("f ( a = 1, b = 2 )")
('f(a=1, b=2)\n', True)
>>> FormatCode("def g( ):\n    a=1\n    b = 2\n    return a==b", lines=[(1, 1), (2, 3)])
('def g():\n    a = 1\n    b = 2\n    return a==b\n', True)

ユニコード文字列を指すのuのプリフィックス以外は変わったところが無いみたい、あれ?

終わりに

コピペしてフォーマットが少し崩れるぐらいであれば、なんとかできるかなと思いましたが、派手に崩れる(文法的に崩れるレベル)とちょっとむずかしい様です。

RaspberryPiだけでなくWindowsのanaconda環境にもインストールしましたがこっちで使ったほうがいいのかもしれません。(コピペする前にこっちで整形をするという感じ)

Node.jsでRSSのデータを取得する【Windows10、RaspberryPi、DragonBoard】

Node.jsでRSSのデータを取得する【Windows10、RaspberryPi、DragonBoard】

以前からちょっと欲しいなと思っていたのですが、現在のテレビ番組を一覧的に表示できなかなと。テレビをつけてEPGを見ればいいじゃないとというのはもちろんわかっているんですが、もう自分はAmazon Primeなしには生きられない特撮バカですので入力切り替えとかそんなことはもうやりたくありません。(きっぱり)

どうせ、PCつけてコードとか書いているんだしPC上で見れればそれでいいかなと思っていたんです。

テレビを見なければいいという話もあるのかもしれませんが…そういうわけにもいかないので…。

テレビの放送状況を確認するにはテレビ王国というサイトがあります。

f:id:ueponx:20170903163256p:plain

SONYさんが運営しているサービスでHDDレコーダやiEPGなんかでも使われているので比較的確度の高い情報でしょう。

tv.so-net.ne.jp

このサイトは全国のエリアをカバーしているので、自分のような地方の人間にも優しいです。また、現在の放送番組のRSSも公開されているので最高です!

RSSはこんな感じ。

Gガイド.テレビ王国 - 放送中のRSS配信 - 地上波(東京)

あとはこのRSSを読み込んでしまえば…ということになります。

今回は他のシステムとの連携もしたかったのNode.jsコーディングしました。

Node.jsでRSSを読み込む

ネットで検索したところWebのスクレイピングではcheerio-httpcliを使用すると便利そうでした。

qiita.com

cheerio-httpcli

特徴としては以下の様です。

  1. 取得先WEBページの文字コードを自動で判定してHTMLをUTF-8に変換
  2. UTF-8に変換したHTMLをjQueryのように操作可能
  3. フォームの送信やリンクのクリックをエミュレート
  4. Node.jsお馴染みのコールバック形式と最近の流行であるプロミス形式どちらにも対応
  5. 同期リクエスト対応
  6. $(‘img’)要素画像のダウンロード(LazyLoad対応)
  7. $(‘a,img,script,link’)要素のURLを絶対パスで取得可能
  8. ブラウザ指定による簡単User-Agent切り替え機能
  9. 現在のクッキーの内容を簡単に取得(読み取り専用)
  10. XMLドキュメントを自動判別してパース処理を切り替え

半分ぐらいは今回とはあまり関係はないのですが、今後使えそうな機能が多いのでこちらを採用しました。

cheerio-httpcliのインストール

今回の実行環境(Windows10)は以下の通りです。

PS C:\Users\xxx\Documents\node> node -v
v6.11.2
PS C:\Users\xxx\Documents\node> npm -v
3.10.10

インストール作業はnpmを使えば簡単です。(LinuxでもWindowsでも違いはありません。プロンプトが違うぐらいです。)

PS C:\Users\xxx\Documents\node> npm install cheerio-httpcli

問題なくインストールできました。

コーディング

先程のRSSのURLを.fetch()の引数に与えてあとは、コールバック形式かプロミス形式で処理を行えば問題ありません。プロミスってなんだろうと思ったんですが、「非同期の動作を扱う際にコールバックからメソッドチェーンにすることができコールバック地獄を回避することができる」ってことのようです。非同期処理では結構苦労するので、こういう改良が必要なんですね。

今回は規模的にはどちらを選択しても良かったのですが、せっかくなので

【sample.js】

var client = require('cheerio-httpcli');
// かんとーちほーの現在の放送番組
var RSS = "https://tv.so-net.ne.jp/rss/schedulesByCurrentTime.action?group=10&stationAreaId=23";

client.fetch(RSS, {})
.then(function (result){
    if (result.error) { console.log("error"); return; }
    result.$("item").each(function (idx) {
        programName = result.$(this).find("title").text();
        programFrame = result.$(this).find("description").text();
        console.log(programName);        
        console.log(programFrame);
        console.log("---");
    });
    console.log("\n" + "放送中番組のタイトルを取得しました。");
});

これを実行してみます。

PS C:\Users\xxx\Documents\node> node .\sample.js
Hulu傑作シアター[]
9/3 1:302:30 [日テレ(Ch.4)]
---
名棋士が読み解く 藤井聡太 “強さ”の秘密[][]
9/3 1:452:30 [NHK総合・東京(Ch.1)]
---
ワールドプロレスリング
9/3 2:002:30 [テレビ朝日(Ch.5)]
---
お買い物情報
9/3 2:002:30 [TOKYO MX1(Ch.9)]
---
HERO'S[デ]
9/3 2:05~2:45 [フジテレビ(Ch.8)]
---
ランク王国 秋のサキドリSP!
9/3 2:08~3:08 [TBS(Ch.6)]
---
深夜に発見!新shock感~一度おためしください~
9/3 2:10~2:35 [テレビ東京(Ch.7)]
---
放送休止
9/3 2:12~5:00 [NHKEテレ1・東京(Ch.2)]
---

放送中番組のタイトルを取得しました。

うまく行ったようです。これで取得できるのはタイトル、放送枠時間、放送局だけなので内容がほしい場合には RSS上のlinkタグにあるURLを更にスクレイピングする必要があるので少し面倒ですね。

終わりに

あとはうまい感じで表示できればOKですね。

少し欲を出して、番組情報も出せるようにしました。1回目のRSSスクレイピングで詳細情報のリンク先を取得し、 2回目のスクレイピングで取得するという感じなのですが、JavaScriptは非同期実行なので、データにタイミングがコードの想定とはズレてしまうようでした。 Objectに格納しようとしたら格納時にはデータがなく、どこでデータ来るのかを判別するのがちょっと難しかったでの悩んでいました。

最初に書いた

  1. 同期リクエスト対応

こちらのことを思い出して、ググってみました。 以下のリンクに同期的実行に関して情報が乗っていたのでそれを試して見ました。

【参考】 qiita.com

【sample2.js】

var client = require('cheerio-httpcli');
var RSS = "https://tv.so-net.ne.jp/rss/schedulesByCurrentTime.action?group=10&stationAreaId=23";
var programsList = [];

var result1 = client.fetchSync(RSS);
result1.$("item").each(function (idx) {
    var program = new Object();
    var programName = result1.$(this).find("title").text();
    var programFrame = result1.$(this).find("description").text();
    var programLink = result1.$(this).find("link").text();
    var result2 = client.fetchSync(programLink);
    programInfo = result2.$(".subUtileSetting").eq(0).find("p").text();

    program["Name"] = programName;
    program["Frame"] = programFrame;
    program["Link"] = programLink;
    program["Info"] = programInfo; 
    programsList.push(program);
});

programsList.forEach(function(element) {
    console.log("\n--->\n");
    console.log("番組名  :" + element["Name"]);        
    console.log("番組時間 :" + element["Frame"]);        
    console.log("番組リンク:" + element["Link"]);            
    console.log("番組詳細 :" + element["Info"]);
}); 

これを実行させると…

PS C:\Users\xxx\Documents\> node .\sample2.js

--->

番組名  :サンバリュ 「外国人観光客に思い切って注意してみました ニッポンのマナー」[]
番組時間 :9/3 13:1514:15 [日テレ(Ch.4)]
番組リンク:http://tv.so-net.ne.jp/schedule/101040201709031315.action?from=rss
番組詳細 :満開の桜の枝をポッキリ折る!市場で生魚を手でベタベタ触る!世界遺産で環境破壊!目に余るマナー違反の外国人観光客を、勇気を出して注意!すると逆ギレ&驚きの言い訳が

--->

番組名  :45th フジサンケイクラシック 最終日[][]
番組時間 :9/3 13:3014:55 [フジテレビ(Ch.8)]
番組リンク:http://tv.so-net.ne.jp/schedule/101056201709031330.action?from=rss
番組詳細 :世界基準コース富士桜に挑む!
初優勝思い出の舞台で復活V目指す岩田寛!
22歳の若武者が初の頂点へ稲森佑貴!

--->

番組名  :舞台芸術の魅力 第8回「世界の現代演劇-演劇における「20世紀」の意味-」
番組時間 :9/3 13:4514:30 [放送大学1(Ch.12)]
番組リンク:http://tv.so-net.ne.jp/schedule/101088201709031345.action?from=rss
番組詳細 :京都造形芸術大学教授 森山 直人

--->

番組名  :海外出張オトモシマス!「フランス 激レア&絶品!チョコ・ハンター」[][][]
番組時間 :9/3 13:5014:20 [NHK総合・東京(Ch.1)]
番組リンク:http://tv.so-net.ne.jp/schedule/101024201709031350.action?from=rss
番組詳細 :海外出張のプロにオトモして、世界の流行の最前線に潜入する番組。今回の出張は、フランス!激レア絶品チョコの大捜索!パリからブルターニュ地方へ。日本初上陸のチョコ!

--->

番組名  :科捜研の女16 #11[][][]
番組時間 :9/3 13:5514:50 [テレビ朝日(Ch.5)]
番組リンク:http://tv.so-net.ne.jp/schedule/101064201709031355.action?from=rss
番組詳細 :「グルメ格付け殺人!死を呼ぶ裏メニューで人間発火!?」沢口靖子()▽身元不明の外国人男性が殺害された。マリコは 被害者が事件の夜に食事したフレンチ店に鍵があると考え…

--->

番組名  :日本の話芸 林家正雀 落語「怪談牡丹灯籠から お札はがし」[][]
番組時間 :9/3 14:0014:30 [NHKEテレ1・東京(Ch.2)]
番組リンク:http://tv.so-net.ne.jp/schedule/101032201709031400.action?from=rss
番組詳細 :第698回東京落語会から林家正雀さんの「怪談牡丹灯籠から お札はがし」をお送りします(平成29818日(金) 東京・虎ノ門 ニッショーホールで収録)

--->

番組名  :今夜!最大の悲劇がー日曜劇場「ごめん、愛してる」スペシャルダイジェスト[]
番組時間 :9/3 14:0014:50 [TBS(Ch.6)]
番組リンク:http://tv.so-net.ne.jp/schedule/101048201709031400.action?from=rss
番組詳細 :クライマックスに向け大きく動き出す長瀬智也主演の胸に迫る切ない物語「ごめん、愛してる」。そこで後編のストーリー&今夜の見所をご紹介!

--->

番組名  :日曜ミステリー「嫌われ監察官 音無一六3」[] 小日向文世主演[][]
番組時間 :9/3 14:0016:00 [テレビ東京(Ch.7)]
番組リンク:http://tv.so-net.ne.jp/schedule/101072201709031400.action?from=rss
番組詳細 :連続殺人事件の現場に残されたのは、恋愛を詠った百人一首の札。怨恨か?それとも…?監察官・音無一六が“深読み力”で真相を暴く! ≪解説放送あり≫

--->

番組名  :MXショッピング
番組時間 :9/3 14:0014:30 [TOKYO MX1(Ch.9)]
番組リンク:http://tv.so-net.ne.jp/schedule/123608201709031400.action?from=rss
番組詳細 :H2OスチームFX 【スチームクリーナー】

個人的には同期的にやったほうがスッキリしますねw。コードも短いし…。

同じソースコードでRaspberryPiとDragonBoard410Cでも実行してみます。

$ node sample2.js

--->

番組名  :決定!こども囲碁名人~第38回少年少女囲碁大会~
番組時間 :9/3 14:3016:30 [NHKEテレ1・東京(Ch.2)]
番組リンク:http://tv.so-net.ne.jp/schedule/101032201709031430.action?from=rss
番組詳細 :囲碁を志す全国の小・中学生にとって日本一の栄冠をかけたあこがれの舞台「少年少女囲碁大会・全国大会」今年も、未来の井山七冠を夢見るこどもたちが熱い戦いを繰り広げた

--->

番組名  :路線バスで寄り道の旅[]
番組時間 :9/3 15:4016:30 [テレビ朝日(Ch.5)]
番組リンク:http://tv.so-net.ne.jp/schedule/101064201709031540.action?from=rss
番組詳細 :日頃秒刻みスケジュールに疲れ気味の徳さん御一行。のんび~り旅を味わうために選んだのは“路線バス"。今回、目指すのは桜木町~馬車道~山下公園で横浜の裏 通りを散策旅

--->

番組名  :大捜索ドキュメント! 屋久島“伝説の超巨大杉”[字][再]
番組時間 :9/3 16:00~16:50 [NHK総合・東京(Ch.1)]
番組リンク:http://tv.so-net.ne.jp/schedule/101024201709031600.action?from=rss
番組詳細 :世界遺産・屋久島で語られる『超巨大杉伝説』。縄文杉を超える杉の大捜索に挑んだ。最新航空調査で森をスキャンし、秘境を大冒険。ついに捜索隊は驚きの巨大杉と遭遇する!

--->

番組名  :グラチャンバレー1番は誰?開幕直前SP[字]
番組時間 :9/3 16:00~16:55 [日テレ(Ch.4)]
番組リンク:http://tv.so-net.ne.jp/schedule/101040201709031600.action?from=rss
番組詳細 :9月5日(火)開幕、グラチャンバレー!徳井義実&佐藤栞里がバレー全日本女子を直撃!気になる噂の真相を選手が激白!

--->

番組名  :なるほど!今うなぎが食べたくなるテレビ【旬は秋!画面から香ばしい香りが?!】[解][字]
番組時間 :9/3 16:00~17:00 [TBS(Ch.6)]
番組リンク:http://tv.so-net.ne.jp/schedule/101048201709031600.action?from=rss
番組詳細 :芸能界のグルメ王・渡部建が名店の秘密に迫る!★芸能界屈指の料理人・梅沢富美男が安くて美味しいうなぎレシピに挑戦!★幻の「青うなぎ」を求めて漁に出るのはみやぞん!

--->

番組名  :YOUは何しに日本へ?予習復習▽日本茶&歌声に熱狂YOUマジ惚れ来日大騒ぎ[字]
番組時間 :9/3 16:00~17:05 [テレビ東京(Ch.7)]
番組リンク:http://tv.so-net.ne.jp/schedule/101072201709031600.action?from=rss
番組詳細 :本気で日本茶学びたい!…20歳青年YOUが本場静岡でガチ住み込み修行…超高級茶葉に昇天!?■遠い異国で人生変えた…坂本九匹敵神歌にYOUが全力マジ惚れ旅■ベビ ーメタル

--->

番組名  :いただきハイジャンプ 土曜お昼にお引っ越しSP[字]
番組時間 :9/3 16:00~17:25 [フジテレビ(Ch.8)]
番組リンク:http://tv.so-net.ne.jp/schedule/101056201709031600.action?from=rss
番組詳細 :(予定変更の場合あり)
様々な一大事にJUMPが立ち向かう▽番組引っ越し記念で人気企画を厳選!6歳少年が苦手克服に挑戦…涙のワケとは?

--->

番組名  :お買い物情報
番組時間 :9/3 16:00~16:30 [TOKYO MX1(Ch.9)]
番組リンク:http://tv.so-net.ne.jp/schedule/123608201709031600.action?from=rss
番組詳細 :トゥルースリーパープレミアケア 【低反発マットレス】

--->

番組名  :障害を知り共生社会を生きる 第8回「難病を知る」
番組時間 :9/3 16:00~16:45 [放送大学1(Ch.12)]
番組リンク:http://tv.so-net.ne.jp/schedule/101088201709031600.action?from=rss
番組詳細 :愛知県立大学教授 吉川 雅博 フリーアナウンサー 岡 知沙登

問題なく動作します!まあNode.jsなんで違いは無いはずですけどね。

うまくいったようです! これで快適なAmazonPrimeの特撮ライフがおくれそうです。