pythonでRSSを取得してみる

前回はWordCloud作ってみましたが、そんだけだとテキストファイルをいちいち整形しないといけないので、なんか自動で情報取得できないかなぁと思ってRSSフィードの内容を取得するプログラムなんかあればWordCloudに食わせるファイル作るの楽になるんじゃないかなぁと思ってRSSフィード取得ツールを作ってみました。
んーエラー制御がザルですのでダメダメですけど、、、

◆参照サイト
1.CSV取扱い関連
https://note.nkmk.me/python-csv-reader-writer/
http://esu-ko.hatenablog.com/entry/2016/02/16/Python%E3%81%A7CSV%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E6%89%B1%E3%81%86%28%E3%83%AA%E3%82%B9%E3%83%88%E3%82%84%E8%BE%9E%E6%9B%B8%E3%81%A8%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95
https://qiita.com/nabenabe0928/items/fcdf2c81e5fff3364c6f
http://kaworu.jpn.org/python/Python%E3%81%A7CSV%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95
http://y0m0r.hateblo.jp/entry/20120712/1342100284
https://www.craneto.co.jp/archives/1309/
http://pynote.hatenablog.com/entry/python-csv-io-recipe
https://www.sejuku.net/blog/68322
https://qiita.com/shinsaka/items/299159968d7226c9bdb9
https://qiita.com/hkambe/items/73912f03c311a3b43785
https://artgear.hatenablog.com/entry/20120126/1327569476
https://alexandco-python.tokyo/archives/217
http://ja.pymotw.com/2/csv/
https://www.headboost.jp/python-file-read-in/
https://qiita.com/msk02/items/c3a1c4a1e1ef94c37228

2.RSSfeed取得について
http://make.bcde.jp/python/rss%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B/
https://pg-chain.com/python-yahoo-rss-scraping
https://kagasu.hatenablog.com/entry/2018/06/24/205104
https://qiita.com/ha_se/items/0a556b2a9a2bb1b368e5
https://hack-le.com/python-rss/
https://ymgsapo.com/scraping-google-news/
https://note.nkmk.me/python-feedparser-tutorial/
https://yukituna.com/1046/
https://tech-blog.s-yoshiki.com/2018/07/243/
https://okayu.qrunch.io/entries/QPwwB6RfVon5ynXO
https://yokonoji.work/python-scraping-3
https://tonari-it.com/feed-rss-url/
http://nichibotsu.blog/?p=449
https://srbrnote.work/archives/1093
https://senaten18.com/post-263
https://stockinfo.hatenablog.jp/entry/2018/08/28/231938
https://yokonoji.work/twitter-rss
https://employment.en-japan.com/engineerhub/entry/2018/05/18/110000
https://www.yoheim.net/blog.php?q=20180703
https://barniclebattleline.com/python-rss-webscraping-yj/
http://esu-ko.hatenablog.com/entry/2016/02/16/Python%E3%81%A7RSS%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%82%92%E5%87%A6%E7%90%86%E3%81%99%E3%82%8B
https://coreblog.org/ats/stuff/minpy_web/06/03.html
http://bty.sakura.ne.jp/wp/archives/1272
https://yuzuremon.hatenablog.com/entry/2018/12/04/015529
https://teratail.com/questions/40777
https://imabari.hateblo.jp/entry/2017/10/26/000537
https://www.walbrix.com/jp/blog/2014-01-python-feedgenerator-rss.html
https://www.indetail.co.jp/blog/raspberry-pi2-twython-newsbot3/
https://a-zumi.net/python-feed-mulitprocess/
https://qiita.com/aoksh/items/aaf0850898a25a43cb6f
https://techacademy.jp/magazine/18884

3.日付関連


◆作業等
1.ライブラリインストール
 RSS処理用にfeedparserをインストールします。

$ sudo pip3 install feedparser --proxy=http://proxyaddr


◆ソースコード
1.改善前
 汚いですが改善前のシングルスレッドのソースです。
#!/usr/bin/env python3
# coding: utf-8
import csv
import collections
import json
import pprint
import sys
import time
import traceback

import feedparser


def getfeedurl(infile):
    """
    CSVファイルからフィード一覧を取得する

    """
    try:
        result=[]
        with open(infile, newline = "" ,encoding = "utf-8-sig") as f:
            read_dict = csv.DictReader(f, delimiter=",", quotechar='"')
            for d in read_dict:
                result.append(d)
        return result
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

def rssparse(feedurl):
    """
    RSSフィードからタイトルと概要と更新日時を取得する。

    """
    try:
        result=[]
        keys = ['title','description','link','updated']
        feed = feedparser.parse(feedurl, response_headers={"content-type": "text/xml; charset=utf-8"})
        for x in feed.entries:
            values = [x.title,x.description,x.links[0].href,time.strftime('%Y-%m-%d %H:%M:%S', x.updated_parsed)]
            o = dict(zip(keys,values))
            result.append(o)
        return result
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

def main():
    """
    主処理
    第1引数から入力ファイル、第2引数から出力ファイルを取得する
    """
    try:
        start_t = time.perf_counter()
        input_file = sys.argv[1]
        output_file= sys.argv[2]
        csv = getfeedurl(input_file)
        size = len(csv)
        print('RSSフィード数:{0}件'.format(size))
        i = 1
        items = []
        f = open(output_file,'w')
        while (i < size):
            url = csv[i]['FeedURL']
            sn = csv[i]['sitename']
            r=rssparse(url)
            msg='{0}行目:FeedURL:{1}:ページ名:{2} '.format(i,url,sn)
            items.append(r)
            i +=1
        f.write(json.dumps(items, indent=2, ensure_ascii=False))
        end_t = time.perf_counter()
        process_time = end_t - start_t
        print('処理時間は:{0}秒です。'.format(process_time))
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(msg)
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))
    finally:
        f.close()

if __name__ == '__main__':
   main()

2.改善後
 速度出なかったのでマルチスレッドにしてみました。
#!/usr/bin/env python3
# coding: utf-8
import csv
import collections
import json
from multiprocessing import Pool
import pprint
import sys
import time
import traceback

import feedparser

def getfeedurl(infile):
    """
    CSVファイルからフィード一覧を取得する

    """
    try:
        result=[]
        with open(infile, newline = "") as f:
            for r in csv.reader(f):
                result.append(r[1])
        del result[0]
        return result
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

def rssparse(feedurl):
    """
    RSSフィードからタイトルと概要と更新日時を取得する。
    """
    try:
        result=[]
        keys = ['title','description','link','updated']
        feed = feedparser.parse(feedurl, response_headers={"content-type": "text/xml; charset=utf-8"})
        for x in feed.entries:
            values = [x.title,x.description,x.links[0].href,time.strftime('%Y-%m-%d %H:%M:%S', x.updated_parsed)]
            o = dict(zip(keys,values))
            result.append(o)
        return result
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

def main():
    """
    主処理
    第1引数から入力ファイル、第2引数から出力ファイルを取得する
    """
    try:
        start_t = time.perf_counter()
        input_file = sys.argv[1]
        output_file= sys.argv[2]
        urls = getfeedurl(input_file)
        size = len(urls)
        print('RSSフィード数:{0}件'.format(size))
        p = Pool(10)
        out = p.map(rssparse, urls)
        with open(output_file,'w') as f:
             f.write(json.dumps(out, indent=2, ensure_ascii=False))
        end_t = time.perf_counter()
        process_time = end_t - start_t
        print('処理時間は:{0}秒です。'.format(process_time))
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))
    finally:
        p.close()

if __name__ == '__main__':
   main()


シングルだと220件のフィードで110秒前後かかってたのが、マルチスレッドだと
50秒前後と爆速になりました。食わせるフィード用のCSVは以下の様な感じです。
---------------------------------------------
サイト名,フィードURL,カテゴリ
---------------------------------------------

なんだかんだでpython歴半年くらいになってるなぁ。
何とかコピペコードの意味も理解できるようになってきました。
けどマルチスレッドだとテキスト吐くときに変な[]が出力されるんだよなぁ、、
これいずれは修正したい。
修正しました。(2019年5月20日)
3.修正版

#!/usr/bin/env python3
# coding: utf-8
import csv
import collections
import json
import pprint
import sys
from concurrent.futures import ThreadPoolExecutor
import time
import traceback

import feedparser

def getfeedurl(infile):
    """
    CSVファイルからフィード一覧を取得する

    """
    try:
        result=[]
        with open(infile, newline = "") as f:
            for r in csv.reader(f):
                result.append(r[1])
        del result[0]
        return result
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

def rssparse(feedurl):
    """
    RSSフィードからタイトルと概要と更新日時を取得する。
    """
    try:
        result=[]
        keys = ['title','description','link','updated']
        feed = feedparser.parse(feedurl, response_headers={"content-type": "text/xml; charset=utf-8"})
        for x in feed.entries:
            if x is not None:
               if hasattr(x, 'title') and \
                  hasattr(x, 'description') and \
                  hasattr(x, 'href') and \
                  hasattr(x, 'updated_parsed'):
                  values = [x.title,x.description,x.links[0].href,time.strftime('%Y-%m-%d %H:%M:%S', x.updated_parsed)]
                  o = dict(zip(keys,values))
                  result.append(o)
        if result:
           return result
        else:
            return
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

def main():
    """
    主処理
    第1引数から入力ファイル、第2引数から出力ファイルを取得する
    使用例:$ python getrssfeed.py feedurl.csv output.json
    
    """
    try:
        start_t = time.perf_counter()
        input_file = sys.argv[1]
        output_file= sys.argv[2]
        urls = getfeedurl(input_file)
        size = len(urls)
        print('RSSフィード数:{0}件'.format(size))
        wk=20
        out=[]
        #マルチスレッドでRSSを取得する
        with ThreadPoolExecutor(max_workers=wk) as executor:
             r = list(executor.map(rssparse, urls))
        #記事が存在するもののみ抽出
        out=[ e for e in r if e]
        #ファイルに書込
        with open(output_file,'w') as f:
             f.write(json.dumps(out, indent=2, ensure_ascii=False))
        end_t = time.perf_counter()
        process_time = end_t - start_t
        print('処理時間は:{0}秒です。'.format(process_time))
    except Exception as e:
        t, v, tb = sys.exc_info()
        print(traceback.format_exception(t,v,tb))
        print(traceback.format_tb(e.__traceback__))

if __name__ == '__main__':
   main()

コメント

このブログの人気の投稿

証券外務員1種勉強(計算式暗記用メモ)

GASでGoogleDriveのサブフォルダとファイル一覧を出力する

マクロ経済学(IS-LM分析)