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に書いてテストを実行してみます
![スクリーンショット 2020-02-24 0.53.23.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F402968%2F9d7efc1b-3028-cbc1-9eba-02c9b1179c26.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=f7bba7bdf971ad47ce999f353eabd58b)
![スクリーンショット 2020-02-23 0.20.28.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F402968%2F58da46e9-1511-9263-d0cb-4ce97bfc1a38.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=25c785c13fff0bc64deb011be8655635)
Slackに通知されました
AWS Cloud Watch Eventsで定期実行
AWS LambdaのトリガーにAWS Cloud Watch Eventsを追加して定期実行されるようにします
![スクリーンショット 2020-02-24 1.38.12.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F402968%2Fec48efc7-9187-86e0-db94-8139271a919b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=56bb355c69201dd259486dc32b7c1e04)
毎朝9時に実行される設定にしました
まとめ
今回はスクレイピングで取得しましたがQiita APIを使ってユーザーごとの最新記事をとる方法もあります
そっちの方が良さそう