LoginSignup
7
6

More than 3 years have passed since last update.

AWS LambdaによるスクレイピングでQiita Organizationの最新記事を取得しSlackに通知するBotを作った

Posted at

Organizationページの「最新の投稿」をAWS Lambda(node.js)でスクレイピングし、新しい投稿の記事のタイトルとURLをSlackに投稿するBotを作ってみました

コードはここに置いてます(大分汚いです)
https://github.com/uji/qiita-organization-scraping/blob/master/app.js

AWS Lambdaの処理の流れ

  1. 「最新の投稿」にある記事をスクレイピングで取得
  2. AWS S3にあるtxtファイルを確認し、Slack未投稿の記事の有無を確認
  3. 未投稿の記事をSlackに投稿
  4. 最新記事のタイトルを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 secretbot tokenchannel id が必要になります

boltの場合

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

スクリーンショット 2020-02-23 0.20.28.png

Slackに通知されました

AWS Cloud Watch Eventsで定期実行

AWS LambdaのトリガーにAWS Cloud Watch Eventsを追加して定期実行されるようにします

スクリーンショット 2020-02-24 1.38.12.png

毎朝9時に実行される設定にしました

まとめ

今回はスクレイピングで取得しましたがQiita APIを使ってユーザーごとの最新記事をとる方法もあります
そっちの方が良さそう

7
6
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
7
6