React+AWS環境を使ってアンテナサイト(RSS)を作成②
Reactでアンテナサイトを作成してみます。
いわゆるアンテナサイトをReactで作ってみよう思い、その時のメモを公開しておきます。 今回はバックエンド部分を作成します。
【前回】React+AWS環境を使ってアンテナサイト(RSS)を作成①
環境
PyCharmをAWS SAMのプロジェクトを作成します。 今回はdocker Imageで作成してみます。
- PyCharm 2021.3.3 (Community Edition)
- Package Type: Image
- Architecture: x86_64
- Runtime: python 3.9
本記事で出来上がるもの
- Lambda実行コード(python)
Lambda実行コード
仕様はざっくり以下の通り。
- LambdaはAWS EventBridge(旧Cloud Watch)でキックして実行
- S3に配置した設定ファイルを配置
- 設定ファイルにはクロールするサイト情報(RSS)を記載
- Lambda実行時に設定ファイルを読み込みRSS情報を取得
- S3に取得した情報を保存
■ app.py(メイン処理)
import json
import feedparser
import boto3
import os
import datetime
import email.utils
CLIENT = boto3.client('s3')
# Lambdaの環境変数を取得する(template.yaml記載)
S3_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME')
# 設定ファイル名
CONFIG_FILE = 'config.json'
def lambda_handler(event, context):
# 現在時刻の取得
now = datetime.datetime.now()
# 現在-1日前の時刻取得
pre = now - datetime.timedelta(days=1)
# 年 文字列(当日)
yNow = now.strftime('%Y')
# 年 文字列(前日)
yPre = pre.strftime('%Y')
# 年月日(当日)
ymdNow = now.strftime('%Y-%m-%d')
# 年月日(前日)
ymdPre = pre.strftime('%Y-%m-%d')
# ファイル名(当日)
fileNameNow = ymdNow + '.json'
# ファイル名(前日)
fileNamePre = ymdPre + '.json'
# フルパス(当日)
fullPathNow = yNow + '/' + fileNameNow
# フルパス(前日)
fullPathPre = yPre + '/' + fileNamePre
# データ取得先の存在チェック(当日)
linksNow = check_file(fullPathNow)
# データ取得先の存在チェック(前日)
linksPre = check_file(fullPathPre)
if linksNow:
print(fullPathNow, 'is exist')
postsNow = linksNow
else:
print(fullPathNow, 'is not exist')
postsNow = []
if linksPre:
print(fullPathPre, 'is exist')
postsPre = linksPre
else:
print(fullPathPre, 'is not exist')
postsPre = []
# 設定情報ファイルのチェック
config = check_file(CONFIG_FILE)
# 新規データフラグ
isNewNow = False
isNewPre = False
# ファイル有り->
if config:
try:
for rss in config['RSSlinks']:
print(rss)
# RSSの情報を取得
feed = feedparser.parse(rss['url'])
# 取得した情報から記事の日付、タイトル、URLを取得
for entry in feed.entries:
# 記事の日付取得(datetime型)
postDate = check_date(entry)
# 記事の年 取得
ymdPost = postDate.strftime('%Y-%m-%d')
# 追加するデータ生成
post = {
'date': postDate.strftime('%Y/%m/%d %H:%M'),
'title': entry.title,
'url': entry.link,
'siteName': rss['name'],
'siteUrl': feed.feed.links[0].href,
'category': rss['category']
}
print(post)
if ymdNow == ymdPost:
if post in postsNow:
print('Post exist in Now')
else:
postsNow.append(post)
isNewNow = True
elif ymdPre == ymdPost:
if post in postsPre:
print('Post exist in Pre')
else:
postsPre.append(post)
isNewPre = True
if isNewNow:
# 日付の降順で並べ替え
postsNow = sorted(postsNow, key=lambda x: x['date'], reverse=True)
# データ登録
CLIENT.put_object(Body=json.dumps(postsNow), Bucket=S3_BUCKET_NAME, Key=fullPathNow)
else:
print('New Post don\'t exist in Now')
if isNewPre:
# 日付の降順で並べ替え
postsPre = sorted(postsPre, key=lambda x: x['date'], reverse=True)
# データ登録
CLIENT.put_object(Body=json.dumps(postsPre), Bucket=S3_BUCKET_NAME, Key=fullPathPre)
else:
print('New Post don\'t exist in Pre')
except:
print('RSSlinks element is not found')
# ファイル無し->
else:
print('Config File don\'t exist')
def check_file(file_name):
"""登録先のファイルの有無をチェック
:rtype: object
"""
try:
response = CLIENT.get_object(Bucket=S3_BUCKET_NAME, Key=file_name)
check_result = json.loads(response['Body'].read())
except:
check_result = False
return check_result
def check_date(entry):
"""日付チェック
:rtype: object
"""
try:
if entry.get('published') is not None:
result = parse_date_string_to_datetime(entry.get('published'))
elif entry.get('date') is not None:
result = datetime.datetime.fromisoformat(entry.get('date'))
else:
print('Date element is not found')
result = None
except Exception as e:
print('[error] ', e)
result = False
return result
def parse_date_string_to_datetime(date_string_rfc822):
"""
RFC2822形式の日付を解析して、
JST aware な datetime にする
"""
timetuple = email.utils.parsedate_tz(date_string_rfc822)
d = datetime.datetime(*timetuple[:7]) # native
d -= datetime.timedelta(seconds=timetuple[-1])
d = d.replace(tzinfo=UTC()).astimezone(JST())
return d # aware
class UTC(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(0)
def dst(self, dt):
return datetime.timedelta(0)
def tzname(self, dt):
return "UTC"
class JST(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(hours=9)
def dst(self, dt):
return datetime.timedelta(0)
def tzname(self, dt):
return "JST"
■ requirements.txt(Lambdaプロジェクトのファイル)
requests
boto3
feedparser
■ config.json(S3配置の設定ファイル)
{
"RSSlinks": [
{
"name": "漫画まとめ速報",
"url": "https://manga.itsys-tech.com/index.xml",
"category": "漫画"
},
{
"name": "キニ速",
"url": "http://blog.livedoor.jp/kinisoku/index.rdf",
"category": "ネタ"
},
{
"name": "ゴールデンタイムズ",
"url": "http://blog.livedoor.jp/goldennews/index.rdf",
"category": "ネタ"
},
{
"name": "ガハろぐNewsヽ(・ω・)/ズコー",
"url": "http://gahalog.2chblog.jp/index.rdf",
"category": "ニュース"
}
]
}
次回はCloudFormationmでAWSのリソースを作っていきます。
Share this post
Twitter
Facebook
Email