やったこと
Mattermostを導入してなにかやってみたかったので、RSSフィードを投稿するプログラムを作りました。(あとで、公式のプロジェクトがあったことに気づいたのは秘密)
いろいろと応用が利くかなと思い、取得したfeedをpandasで処理して
DBに格納することとしました。
※Mattermostってなんぞやって言う人はこちら
環境
- CentOS7
- Python2.7
- pandas
- feedparser
- sqlalchemy
- requests
- PostgreSQL9.5
- MatterMost
PostgreSQLについては、dockerイメージから導入しました。
docker pull postgres:9.5
docker run -p 5432:5432 --name postgres-server -v /var/lib/postgresql:/var/lib/postgresql:rw postgres:9.5
firewall-cmd --permanent --add-port=5432/tcp
firewall-cmd --reload
これでとりあえずリモート接続可能なPostgreSQLコンテナが立ちあがるはず。
docker便利だなぁ。。。
そのほかの環境構築は省略、Pythonはpyenv環境で実施しています。
1. RSSフィードを取得する。
feedparser
というPythonライブラリを使います。
ここら辺を参考にして、pip
でインストールしました。
import feedparser
RSS_URL = "http://b.hatena.ne.jp/hotentry/it.rss"
print("Start get feed from %s" % (RSS_URL))
feed = feedparser.parse(RSS_URL)
これでフィードが取得できます。
(ちなみに↑ははてなのテクノロジーカテゴリのホットエントリを取得しています。)
2. 取得したフィードをpandas.DataFrameに展開
今後の処理のしやすさなどを考えて、pandas.DataFrameにマッピングします。
import pandas as pd
entries = pd.DataFrame(feed.entries)
...終わり。
pandasさん優秀ですね。
はてなのRSSフィードの場合、以下の12列の要素が取得されていました。
- content
- hatena_bookmarkcount
- id
- link
- links
- summary
- summary_detail
- tags
- title
- title_detail
- updated
- updated_parsed
ここまでくれば、あとはpandasの機能で自由にデータをいじれます。
3. 新着フィードのチェック
feedparserはとても便利ですが、アクセスした時点のフィードを取得するので過去に取得したフィードと重複がでます。
ここでDataFrameに展開した意味が出てくるのです!
以下はDataFrameの操作で、新着のフィードのみを取り出して表示する例です。
already_print_feeds = pd.Series()
while True:
time.sleep(300)
feed = feedparser.parse(RSS_URL)
entries = pd.DataFrame(feed.entries)
new_entries = entries[~entries['id'].isin(already_print_feeds)]
if not new_entries.empty:
for key, row in new_entries.iterrows():
feedinfo = "[**%s**](%s)\n\n>%s" % (row['title'], row['link'], tag_re.sub('', row['summary']))
print(feedinfo)
already_print_feeds = already_print_feeds.append(new_entries['id'])
ちょっと解説
new_entries = entries[~entries['id'].isin(already_print_feeds)]
これは、取得したRSSフィードから新着のみをぬきだしています。
already_print_feeds
にはこれまで取得したRSSフィードのid
が入っている前提です。
そうすると、entries
に格納されたフィードのうち、
新着の行のみにTrue
が設定されたSeriresが返却されるので、
これをentries
のインデックスに指定すれば新着だけ抜き出せます。
~entries['id'].isin(already_print_feeds)
# =>
0 False
1 True # => ★New!
2 False
3 False
4 False
5 False
6 False
7 False
8 False
9 False
10 False
11 False
12 False
13 False
14 False
15 False
16 False
17 False
18 False
19 True # => ★New!
20 False
21 False
22 False
23 False
24 False
25 False
26 False
27 False
28 False
29 False
Name: id, dtype: bool
already_print_feeds
にはこれまで表示した新着フィードのIDを追記していけばいいです。
already_print_feeds = already_print_feeds.append(new_entries['id'])
ただし上記のコードだとalready_print_feeds
に無限にデータが貯まっていくので、いつか破綻します(メモリが)
1日1回フラッシュしたり、DBから読み取るようにしたりしましょう
4. DB(PostgreSQL)に保存
取得した、RSSフィードをPostgreSQLに保存します。
ただし、列は下記に絞りました。
- id
- link
- title
- summary
- updated
まずDBにテーブルを作ります。
create table feed ( id text primary key , link text, title text, summary text, updated timestamp );
一応、idには主キー制約を入れて、かつupdatedはtimestamp型としておきました。
(はてなのフィードのupdatedはそのままtimestamp型としてINSERTできる形式のようです。)
from sqlalchemy import create_engine
DATABASE_CONN = "postgresql://xxxx:xxxx@xxxxx:xxx/xxxx"
DATABASE_TABLE = "feed"
# connect database
engine = create_engine(DATABASE_CONN)
# Store database
stored_entries = new_entries.ix[:, [
"id", "link", "title", "summary", "updated"]]
stored_entries.to_sql(DATABASE_TABLE, engine, index=False, if_exists='append')
DataFrameのto_sql
メソッドを使います。
- index=False
とすることで、格納時に勝手にインデックス列を付与しなくなり、
- if_exists='append'
で、すでに存在するテーブルにデータを追記する挙動となります。
5. MatterMostに投稿する
HTTPリクエストを送るrequest
というPythonライブラリで非常に簡単に投稿できます。
import requests
import json
mattermosturl = "MatterMostのincomming webhook URL"
username = "好きな名前"
header = {'Content-Type': 'application/json'}
payload = {
"text": feedinfo,
"username": username,
}
resp = requests.post(mattermosturl,
headers=header, data=json.dumps(payload))
というわけで
せっかくpandasにマッピングしたので、
機械学習的なこともやっていきたいと思います。