LoginSignup
3
6

More than 3 years have passed since last update.

AWS Lambda + Typescript + PuppeteerでWebスクレイピング

Last updated at Posted at 2019-12-24

AWS Lambda + Typescript + PuppeteerでWebスクレイピング

前提条件

  • 開発環境
    • Windows10
    • Node.js 12.14.0-x64
    • AWS CLI
    • VS Code
  • AWSアカウント
  • IAMユーザー※

※IAMユーザーはServerlessFrameworkのリソース作成に必要な権限が付与されていること(今回はAdministratorAccessを使用)、AWS CLIに--profile serverlessで設定していることを前提とする

Serverless Framework のプロジェクト作成

AWSリソースの管理にServerless Frameworkを使います。AWS Lambdaを使う上で必要な面倒くさいことを大体やってくれます。すごい。

Serverless Frameworkのインストール

サービス作成コマンドを使うためにグローバルインストールします。
どうしてもグローバルに入れたくない人は、後述するファイル群を自作しても良いです。

> npm install -g serverless

> serverless --version
Framework Core: 1.60.4
Plugin: 3.2.6
SDK: 2.2.1
Components Core: 1.1.2
Components CLI: 1.4.0

サービスの作成

aws-nodejs-typescriptでサービスを作成します。

> serverless create --template aws-nodejs-typescript --name scraping-service --path scraping-service

以下のファイルが作成されます。

scraping-service
|-.vscode ※vscode(ドット無し)で作成されるので、先頭にドットを追加する
| `-launch.json
|-.gitignore
|-handler.ts
|-package.json
|-serverless.yml
|-tsconfig.json
`-webpack.config.js

グローバルモジュールの削除

> npm remove -g serverless

各種設定の調整

.gitignore

そのまま使います。

.gitignore
# package directories
node_modules
jspm_packages

# Serverless directories
.serverless

# Webpack directories
.webpack

Serverless Framework

  • NPMモジュールをLambda Layerに入れるための設定
    • includeModules: false
    • plugins:serverless-layersを追加
    • deploymentBucket:にS3バケット名を指定 ※S3バケットは事前に作成します
  • テンプレートでAPIGatewayのエンドポイントが設定されているので削除
  • stage:を引数から受け取れるように設定
  • profile:にAWS CLIのprofile名を設定
  • region:にデプロイ先のリージョンを設定
  • functions:に関数定義execute:を追加して、メモリサイズとタイムアウト時間を設定
serverless.yml
service:
  name: scraping-service
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name

custom:
  webpack:
    webpackConfig: ./webpack.config.js
    includeModules: false

# Add the serverless-webpack plugin
plugins:
  - serverless-layers
  - serverless-webpack

provider:
  name: aws
  stage: ${opt:stage, 'dev'}
  profile: serverless
  region: ap-northeast-1
  runtime: nodejs12.x
  #apiGateway:
  #  minimumCompressionSize: 1024 # Enable gzip compression for responses > 1 KB
  environment:
    AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1
  deploymentBucket:
      name: scraping-service-deploy # 事前にS3バケットを作成してバケット名を指定

functions:
  #hello:
  #  handler: handler.hello
  #  events:
  #    - http:
  #        method: get
  #        path: hello
  execute:
    handler: handler.execute
    memorySize: 1024
    timeout: 30

NPMモジュール

  • 必要なパッケージを追加。Chromeはchrome-aws-lambdaを使用します。
    • npm install chrome-aws-lambda
    • npm install puppeteer-core
    • npm install -D @types/puppeteer-core
    • npm install -D serverless
    • npm install -D serverless-layers
  • scriptsにローカル実行用のコマンドを追加します。

※chrome-aws-lambdaでインストールされるバイナリがWindowsでうまく動かないので、ローカル実行は普通にインストールしたChromeで代替しています。WindowsとLambda(AmazonLinux?)でうまく両用できるパッケージがあれば切り替えたいところ。

package.json
{
  "name": "scraping-service",
  "version": "1.0.0",
  "description": "Serverless webpack example using Typescript",
  "main": "handler.js",
  "scripts": {
    "local": "node ./node_modules/serverless/bin/serverless invoke local -f execute",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "chrome-aws-lambda": "^2.0.1",
    "puppeteer-core": "^2.0.0",
    "source-map-support": "^0.5.10"
  },
  "devDependencies": {
    "@types/aws-lambda": "^8.10.17",
    "@types/node": "^10.12.18",
    "@types/puppeteer-core": "^2.0.0",
    "fork-ts-checker-webpack-plugin": "^3.0.1",
    "serverless": "^1.60.4",
    "serverless-layers": "^1.4.3",
    "serverless-webpack": "^5.2.0",
    "ts-loader": "^5.3.3",
    "typescript": "^3.2.4",
    "webpack": "^4.29.0",
    "webpack-node-externals": "^1.7.2"
  },
  "author": "The serverless webpack authors (https://github.com/elastic-coders/serverless-webpack)",
  "license": "MIT"
}

Typescript

そのまま使います。

tsconfig.json
{
  "compilerOptions": {
    "lib": ["es2017"],
    "removeComments": true,
    "moduleResolution": "node",
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "sourceMap": true,
    "target": "es2017",
    "outDir": "lib"
  },
  "include": ["./**/*.ts"],
  "exclude": [
    "node_modules/**/*",
    ".serverless/**/*",
    ".webpack/**/*",
    "_warmup/**/*",
    ".vscode/**/*"
  ]
}

Webpack

  • source-mapの設定を調整
webpack.config.js
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = {
  context: __dirname,
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  entry: slsw.lib.entries,
  devtool: slsw.lib.webpack.isLocal ? 'inline-source-map' : 'source-map',
  resolve: {
    extensions: ['.mjs', '.json', '.ts'],
    symlinks: false,
    cacheWithContext: false,
  },
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js',
  },
  target: 'node',
  externals: [nodeExternals()],
  module: {
    rules: [
      // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
      {
        test: /\.(tsx?)$/,
        loader: 'ts-loader',
        exclude: [
          [
            path.resolve(__dirname, 'node_modules'),
            path.resolve(__dirname, '.serverless'),
            path.resolve(__dirname, '.webpack'),
          ],
        ],
        options: {
          transpileOnly: true,
          experimentalWatchApi: true,
        },
      },
    ],
  },
  plugins: [
    // new ForkTsCheckerWebpackPlugin({
    //   eslint: true,
    //   eslintOptions: {
    //     cache: true
    //   }
    // })
  ],
};

NPMモジュールのインストール

> npm install

※この時点で新しいバージョンが出ているモジュールが存在するので、必要に応じでnpm outdatedで確認してバージョンアップしましょう。

Lambda関数

ハンドラ

handler.ts
import 'source-map-support/register';
import { Handler } from 'aws-lambda';
import * as chromium from 'chrome-aws-lambda';

export const execute: Handler = async (_event, _context) => {
  let result;
  let browser;

  try {
    // chrome-aws-lambda でインストールされるバイナリがWindowsで動かないため
    // ローカルで実行する際は通常インストールしたChromeを代替とする
    browser = await chromium.puppeteer.launch({
      args: chromium.args,
      ignoreDefaultArgs: process.env.IS_LOCAL ? ['--single-process'] : [],
      defaultViewport: chromium.defaultViewport,
      executablePath: process.env.IS_LOCAL ?
        'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe' : await chromium.executablePath,
      headless: chromium.headless, // ここをfalseにするとChromeのウインドウが表示されるので、開発時にうまくいかない場合は変更すると良いです
    });

    const page = await browser.newPage();

    await page.goto('https://example.com');

    result = await page.title();
  } finally {
    if (browser) {
      await browser.close();
    }
  }

  return {
    statusCode: 200,
    body: JSON.stringify({
      message: `Title: ${result}`,
    }, null, 2),
  };
}

実行

> npm run local
...
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Title: Example Domain\"\n}"
}

デバッグ

VS Codeでデバッグ > 構成を開く

launch.json
{
  "configurations": [
    {
      "name": "Lambda",
      "type": "node",
      "request": "launch",
      "runtimeArgs": ["--inspect"],
      "program": "${workspaceFolder}/node_modules/serverless/bin/serverless",
      "args": ["invoke", "local", "-f", "execute", "-d", "{}"],
      "outFiles": ["${workspaceFolder}/.webpack/service/*"]
    }
  ]
}

ブレークポイントを設定してF5でデバッグ実行

デプロイ

デフォルトではdevステージにデプロイされます。
本番と分けたい場合は-sオプションでステージを指定できます。

> npx sls deploy -v

CloudFormationを利用して必要なリソースが作成されます。らくちん。

AWS上で実行

こちらも同じく-sオプションでステージを指定できます。

> npx sls invoke -f execute
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Title: Example Domain\"\n}"
}

CloudWatchで実行ログを確認できます。

おわり

最後に不要な課金が発生しないようにAWSリソースを削除しましょう。

> npx sls remove

実際のスクレイピング処理が完成したら、CloudWatch Eventsのスケジュールなり、SNS Topicなりをトリガーに設定しましょう。
一部はserverless.ymlでも設定できます。
更新はデプロイコマンドを実行するだけ。

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