Organizationページの「最新の投稿」をAWS Lambda(node.js)でスクレイピングし、新しい投稿の記事のタイトルとURLをSlackに投稿するBotを作ってみました
コードはここに置いてます(大分汚いです)
https://github.com/uji/qiita-organization-scraping/blob/master/app.js
AWS Lambdaの処理の流れ
- 「最新の投稿」にある記事をスクレイピングで取得
- AWS S3にあるtxtファイルを確認し、Slack未投稿の記事の有無を確認
- 未投稿の記事をSlackに投稿
- 最新記事のタイトルをtxtファイルでAWS S3に保存
投稿済みの記事を永続化する必要があるのですが、DB使うのは大げさな気がしたのでS3にtxtで保存します
スクレイピング
AWS Lambda 用のchromium chrome-aws-lambda を使ってスクレイピングしました
Google Cloud Functionsでも使えるっぽいです
puppeteer
と使用感はほぼ同じです
.of-ItemLink_header-title
classを取ってくると「最新の投稿」の要素を取ってこれます
const chromium = require("chrome-aws-lambda");
exports.handler = async (event, context) => {
let browser = null;
let elems = [];
try {
browser = await chromium.puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless,
});
let page = await browser.newPage();
await page.goto("http://qiita.com/organizations/" + process.env.ORGANIZATION_NAME);
const selector = ".of-ItemLink_header-title";
elems = await page.$$eval(selector, es => es.map(e => [e.textContent, e.href]));
} catch (error) {
return context.fail(error);
} finally {
if (browser !== null) {
await browser.close();
}
}
return context.succeed(elems);
};
Slackに通知する
@slack/bolt
または@slack/web-api
でSlack APIを叩き、取得した記事をSlackに投げます
Slack Botでメッセージを送るには、singning secret
、bot token
、 channel id
が必要になります
const { App, ExpressReceiver } = require("@slack/bolt");
const receiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET
});
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
receiver: receiver
});
app.client.chat.postMessage({
channel: process.env.SLACK_CHANNEL,
text: "message",
token: process.env.SLACK_BOT_TOKEN
});
S3のテキストファイル読み込み、上書き
投稿済みの記事のタイトルを永続化します
aws-sdk
を使うと結構簡単にS3にアクセスできます
const AWS = require("aws-sdk");
const s3 = new AWS.S3({ region: "ap-northeast-1" });
let s3Params = {
Bucket: process.env.BACKET_NAME,
Key: process.env.FILE_NAME
};
s3.getObject(s3Params, (err, data) => {
if (err) return context.fail(err);
else latest = data.Body.toString();
});
const AWS = require("aws-sdk");
const s3 = new AWS.S3({ region: "ap-northeast-1" });
let s3Params = {
Bucket: process.env.BACKET_NAME,
Key: process.env.FILE_NAME,
Body: "タイトル"
};
s3.putObject(s3Params, (err) => {
if (err) return context.fail(err);
});
AWS Lambdaの設定
ランタイムにnode.jsを指定してLambdaを作成します
まあまあ時間のかかる処理なので、基本設定からメモリ、タイムアウト時間を変更しておきます
メモリは512MB、タイムアウト時間は1分にしました
node_modulesはzipにまとめてLayerに登録します
AWS Lambda レイヤー
AWS Lambdaで動かす
コードをLambdaに書いてテストを実行してみます
Slackに通知されました
AWS Cloud Watch Eventsで定期実行
AWS LambdaのトリガーにAWS Cloud Watch Eventsを追加して定期実行されるようにします
毎朝9時に実行される設定にしました
まとめ
今回はスクレイピングで取得しましたがQiita APIを使ってユーザーごとの最新記事をとる方法もあります
そっちの方が良さそう