Amazon PollyとPythonでお手軽スクレイピング&サーバーレスなポッドキャスト配信 #reinvent

eyecatch_polly

はじめに

先日の re:Invent 2016 で Amazon Polly が発表されました。

高度な深層学習テクノロジーを使用した Amazon AI サービスの 1 つで、24 の言語と 47 の音声 で文章をリアルな音声に変換するサービスです。

Amazon Polly のユースケースとして、製品のトップページには「記事を音声に変換して MP3 でダウンロードする」という例が紹介されています。

Diagrams_Polly_Content-Creation

Amazon Polly のウェビナーを視聴していると、このユースケースにあるような

  • Python による RSS のクローリングとスクレイピング
  • Amazon Polly の text-to-speech 変換

を駆使してサーバーレスにポッドキャスト配信サービスを構築するアーキテクチャーが紹介されていました。

本ブログでは Amazon Polly と AWS のサーバーレスサービスをうまく活用したこのアーキテクチャーをステップバイステップでウォークスルー形式で解説します。

ウォークスルーの流れ

  1. 音源を置く S3 の設定
  2. Lambda の設定
  3. Lambda 関数のテスト実行
  4. Lambda 関数のトリガー設定
  5. Lambda の動作確認

ステップ1:音源を置く S3 の設定

音源の設置先となる S3 バケットの設定を行います。

S3 バケットの作成

まず S3 バケットを新規に作成します。

静的ウェブホスティングの設定

次の作成したバケットのプロパティの Static Website Hosting で次のように設定します。

  • Enable website hosting
  • Index Document: podcast.xml
  • Error Document: error.html

s3-website-hosting

アクセス許可の設定

最後にすべてのユーザーがこの S3 バケット内のオブジェクトにアクセス出来るように、バケットポリシーを次のように設定します。

{
  "Version":"2012-10-17",
  "Statement":[{
    "Sid":"PublicReadForGetBucketObjects",
        "Effect":"Allow",
      "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::BUCKET-NAME/*"
      ]
    }
  ]
}

JSON ファイル内の BUCKET-NAME は実際に作成したバケット名に変更して下さい。

ステップ2:Lambda の設定

クローリング&スクレイピングする Lambda の設定をします。

Lambda 用 IAM ロールの作成

IAM の画面で新規ロール作成画面に遷移し

  • Role Name : 必須
  • Role Type : AWS Service Roles : AWS Lambda
  • Attach Policy : スキップ
  • プレビュー : 内容を確認後、作成

作成した IAM ロールを開き、 Inline Policies の Create Role Policy からインラインでパーミッションを設定します。

Lambda 関数の取得

Lambda 関数は GitHub にあがっているため、レポジトリ awslabs/amazon-polly-sample をクローンします。

$ git clone https://github.com/awslabs/amazon-polly-sample.git

Lambda 関数のアップロード

今回利用する Lambda 関数は クローンしたソースコードの以下のパスにあります。

mazon-polly-sample/dist/package.zip

AWS 管理画面にアップロードします。

Lambda 関数の新規作成画面に移動し次のように設定します。

Configure Triggers

あとで設定するため、「Next」

Configure function

  • Name : 必須
  • Decription : 任意
  • Runtime : Python 2.7
  • Lambda function code : GitHub から先程クローンした package.zip を指定

Lambda function handler and role

  • Handler : lambda_function.lambda_handler
  • Role : Create a custome role からロールの作成画面に遷移。ポリシーの編集画面に移動し、次のポリシーを設定
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "polly:SynthesizeSpeech",
                "s3:ListBucket",
                "s3:PutObject"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Lambda Polly IAM Permissions

Lambda 関数は

  • S3 からのキーの取得、S3 へのオブジェクトの PUT
  • Polly の text-to-speech API 呼び出し

を行います。 このための最小限の権限を付与しています。

Advanced settings

クローリングを行うため、タイムアウト時間を伸ばします。

  • Memory : 128 MB
  • Timeout : 5 min

Review

入力した情報を確認後、問題がなければ Create function で作成します。

ステップ3 : Lambda 関数のテスト実行

作成した Lambda 関数の個別画面に遷移し、"Action" タブから "Configure Test Event" を選択します。

Inline test event の画面で、RSS フィードの URL と音源の保存先 S3 バケットを指定します。

{
  "rss": "http://feeds.feedburner.com/AmazonWebServicesBlog", 
  "bucket": "YOUR_BUCKET_NAME"
}

今回は RSS フィードとして AWS Blog の RSS フィードを指定します。 YOUR_BUCKET_NAME の箇所は作成した S3 バケット名に変更して下さい。

Lambda test event

"Save and test" をクリックすると、テストイベントが保存され、Lambda 関数が実行されます。

初回実行時は、各フィードをテキストから音源に変換するため、時間がかかります。

実行結果(Execution result) が "succeeded" であれば成功です。 エラーが発生した場合は、エラーメッセージに従って調査して下さい。

ステップ4 : Lambda 関数のトリガー設定

CloudWatch Events を使って Lambda 関数を定期的に呼び出すように設定します。

CloudWatch Event Rule の作成

CloudWatch Events の Rules に移動し、"Create rule" からルール作成画面に移動します。

Event Selector

  • Event source : Schedule
  • 間隔 : Fixed rate of 1 Hours

Targets

  • target type : Lambda function
  • Function : 作成した Lambda 関数
  • Configure version/alias : デフォルトのまま
  • Configure input : 以下のJSON
{
  "rss": "http://feeds.feedburner.com/AmazonWebServicesBlog", 
  "bucket": "YOUR_BUCKET_NAME"
}

クロールする RSS フィードと音源の保存先 S3 バケットを指定します。 YOUR_BUCKET_NAME の箇所は作成した S3 バケット名に変更して下さい。

設定例

CloudWatch Events Rule

CloudWatch Event Rule の詳細設定

Rule の詳細を設定します。

  • Name : 必須
  • 概要 : 任意
  • Stage : Enabled にチェック

ステップ5 : Lambda の動作確認

CloudWatch Event Scheduler を有効にしたあとは放置します。

翌日などに確認すると、 S3 のインデックスファイル(podcast.xml)が更新され、新しい記事が音源化されていることを確認できます。

$ curl --silent http://YOUR-BUCKET-NAME.s3-website.eu-central-1.amazonaws.com/podcast.xml
<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
  <channel>
    <title>Audio podcast based on: AWS Security Blog</title>
    <link>https://aws.amazon.com/blogs/security/</link>
    <description>Just another AWS Brew Blogs  site</description>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>python-feedgen</generator>
    <lastBuildDate>Thu, 22 Dec 2016 11:24:17 +0000</lastBuildDate>
    <item>
      <title>EU Compliance Update</title>
      <guid isPermaLink="false">728f967e2480fc19156cbd0790baef67d1e1850c_0</guid>
      <enclosure url="http://BUCKET.s3-website.eu-central-1.amazonaws.com/728f967e2480fc19156cbd0790baef67d1e1850c_0.mp3" length="0" type="audio/mpeg"/>
      <pubDate>Mon, 12 Dec 2016 14:50:32 +0000</pubDate>
    </item>
    <item>
      <title>EU Compliance Update</title>
      <guid isPermaLink="false">728f967e2480fc19156cbd0790baef67d1e1850c_1</guid>
      <enclosure url="http://BUCKET.s3-website.eu-central-1.amazonaws.com/728f967e2480fc19156cbd0790baef67d1e1850c_1.mp3" length="0" type="audio/mpeg"/>
      <pubDate>Mon, 12 Dec 2016 14:50:31 +0000</pubDate>
    </item>
...

S3バケットの中身

polly-podcast-s3-bucket

うまく動作していない時は、Lambda の実行ログを確認してください。

あとは作成した Podcast のフィードを登録するだけです。

ソースコードから Lambda パッケージの作成方法

GitHub からクローンした amazon-polly-sample のトップディレクトリで

$ pip install -r requirements.txt -t .
$ zip -r package.zip .

と実行すると、ソースコードから Lambda パッケージを作成できます。

Lambda 関数のカスタマイズポイント

以下で Lambda 関数のカスタマイズポイントを紹介します。

Polly のリージョン

現時点では Polly は一部のリージョンでのみ提供されています。 そのため、

from boto3 import Session
...
session = Session(region_name="us-west-2")
polly = session.client("polly")

というように Polly が提供されているリージョン向けにべた書きされています。

Lambda 関数と同じリージョンの Polly を利用する場合

from boto3 import client
...
polly = client("polly")

というようにシンプルに書けます。

日本語の音声を利用

日本語を利用する場合は VoiceIdMizuki に変更します。

response = polly.synthesize_speech(
        Text=entry['content'],
        OutputFormat="mp3",
        VoiceId="Mizuki")

最後に

今回は

  • Python のクローリングとスクレイピング
  • Amazon Polly の text to speech
  • AWS のサーバーレス技術

を組み合わせてサーバーレスにポッドキャスト配信するアーキテクチャーを紹介しました。

Lambda 内のビジネスロジックをいじるだけで、いろいろ使いまわしができそうなサーバーレスアーキテクチャーとなっています。

簡単にいじれるため、ボット化や画像認識対応(Amazon Recognition)など、いろいろ遊んで見てはいかがでしょうか?

参考