LoginSignup
11
15

More than 5 years have passed since last update.

Python+pandasを使ってRSSフィードを取得→Mattermostに投稿&DBに保存

Posted at

やったこと

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'])

:warning: ただし上記のコードだと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にマッピングしたので、
機械学習的なこともやっていきたいと思います。

11
15
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
11
15