Azure Functions (FaaS)で スクレイピングするため、Puppeteer / Praywrightを動かしたい

### やりたいこと

- 標題の通り、定期的にスクレイピングを行いたいが自身のPCやIaaSだと24時間稼働のコストがかかるため、Functions as a Serviceで動かしたい
- スクレイピング先のツールがクライアントサイドでJavaScriptを動かす必要があるため、beautifulsoupではなくPlaywright / Puppetterを利用する必要がある

### 調べたこと

- Azure FunctionsでPlaywrightを使う or Google Cloud RunでPuppetterを使う、が一番簡単に環境を構築できそう
    - Playwright / Puppetterはheadless Chrominiumを利用して動作するが、この要領が大きいため、Function as a Serviceでは環境構築上の制限がある
- 2017年の記憶では、Google Cloud FunctionでPuppetterを使うのは簡単だった記憶があるが、2024年現在、Google Cloud RunでPuppetterを使うのはひと工夫必要そうである
    - Google Cloudに詳しくないので、どれくらいの労力がかかりそうかは分からない
- 一方、Azure FuctionsではLinux基盤の場合、headless Chrominiumがイメージに組み込まれており、簡単に動作する、との情報があり
    - [Azure FunctionsでPlaywrightを使ったWebスクレイピング - めもおきば](https://d.nekoruri.jp/entry/2020/12/01/azure-functions-web-scraping)
    - [Running headless Chromium in Azure Functions with Puppeteer and Playwright](https://anthonychu.ca/post/azure-functions-headless-chromium-puppeteer-playwright/)
    - [Azure Functions on Linux で Puppeteer を動かす #tips - Qiita](https://qiita.com/georgeOsdDev@github/items/636314b488286b6024a4)
- 他方、上記の方法では時期によってAzure側のLinuxイメージが違い手順が安定しない、ハマりやすいといった欠点が指摘されている。そのため、ローカルでカスタムイメージを作成してそれをAzuure Container Appsで動作させる方がハマりどころが少ない、という意見もある。※ この方法でも従量課金で動かせるため、やりたい事は実現できる
    - [Serverless Playwright the easy way with Azure Container Apps](https://anthonychu.ca/post/serverless-playwright-azure-container-apps/)
    - [Running a Playwright scheduled job with Azure Container Apps](https://anthonychu.ca/post/scheduled-playwright-job-azure-container-apps/)
    - [Azure Functions on Azure Container Apps で Playwright をサーバーレスで実行する](https://zenn.dev/awell/articles/auzre-functions-on-container-apps-for-playwright)

### やること

- ハマるのは嫌
- 新しい知識を勉強するため、Azure Container Appsでの実行にチャレンジ

### やったこと

#### 写経

[Azure Functions on Azure Container Apps で Playwright をサーバーレスで実行する](https://zenn.dev/awell/articles/auzre-functions-on-container-apps-for-playwright)を写経させていただく。

##### 1. ローカルにプロジェクトディレクトリ・HTTP Trigger 関数を作成

```powershell
# Azure Functions のプロジェクトディレクトリを作成します。
func init --worker-runtime node --language javascript --docker
# HTTP Trigger の関数を作成します。
func new --name httpTrigger --template "HTTP trigger" --authlevel "anonymous"
```

##### 2. Playwrightのコードを作成

src/functions/httpTrigger.js に適当なコードを作成

##### 3. npm パッケージをインストール

Playwright の npm パッケージをインストールします。

```powershell
npm install playwright
```

##### 4. Azure リソースを作成

1.変数を設定

- ここでは大文字小文字数字が入り混じってしまっているが、Azureにプッシュする名前のためアルファベット26文字のみで構成したほうが良い
- 利用するコンソールに注意。途中までpowershellで実行したら通らないコマンドがあったので、多少コマンドを変更して成功した
    - 写経元はWSLをコンソールとして利用している

```
$RG="NodeScraperRG20240930"
$LOCATION="japaneast"
$APP_CONTAINER_ENV_NAME="NodeScraperEnvRG20240930"
$REGISTRY_NAME="nodescraperregistryrg20240930"
$IMAGE_NAME="nodewebscraperrg20240930"
$TAG="WebScraperAppRG20240930"
$APP_NAME="webscraperapprg20240930"
$STORAGE_NAME="storage20240930"
```
2.Azure CLI 拡張機能とプロバイダーの登録

```
# Azure CLI 拡張機能のインストール
az extension add --name containerapp --upgrade -y

# プロバイダーの登録
az provider register --namespace Microsoft.Web
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
```

3.リソースグループを作成
```
az group create --name $RG --location $LOCATION
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147841/94b8661a-ac93-2d32-62bd-453447afe173.png)

4.Azure Container Apps インスタンスを作成

```
az containerapp env create --name $APP_CONTAINER_ENV_NAME --resource-group $RG --location $LOCATION
```

5.Storage Account を作成

```
az storage account create --name $STORAGE_NAME --resource-group $RG --location $LOCATION --sku Standard_LRS
```

6.Azure Container Registry を作成

```
az acr create --name $REGISTRY_NAME --resource-group $RG --location $LOCATION --sku Basic --admin-enabled true
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147841/a1e084db-3c31-74db-487b-0ff3b228aeec.png)

##### 5. Docker イメージをビルドして Azure Container Registry にプッシュ

1.Dockerfile を修正します。手順 1 で生成された Dockerfile を編集し、Playwright のインストールを追加します。

```dockerfile
# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/node:4-node18-appservice
FROM mcr.microsoft.com/azure-functions/node:4-node18

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY . /home/site/wwwroot

RUN cd /home/site/wwwroot && \
    npm install

# Install Playwright
RUN apt-get update && \
    npx playwright install-deps && \
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147841/a2064d5d-06e7-0085-edcf-4e88f44a7d98.png)

2.Docker イメージをビルドします。

```docker
docker build -t nodescraperregistryrg20240930.azurecr.io/nodewebscraperrg20240930:WebScraperAppRG20240930 . --platform linux/amd64
```

```
# コンテナレジストリのログインサーバーの取得
LOGIN_SERVER=$(az acr show --name $REGISTRY_NAME --resource-group $RG --query loginServer --output tsv)

# コンテナレジストリのユーザー名の取得
REGISTRY_NAME=$(az acr credential show --name $REGISTRY_NAME --resource-group $RG --query username --output tsv)
# コンテナレジストリのパスワードの取得
ADMIN_PASSWORD=$(az acr credential show --name $REGISTRY_NAME --resource-group $RG --query "passwords | [0] | value" --output tsv)

# Azure Container Registry にログイン
echo $ADMIN_PASSWORD | docker login $LOGIN_SERVER -u $REGISTRY_NAME --password-stdin

# Docker イメージを Azure Container Registry にプッシュ
docker push $LOGIN_SERVER/$IMAGE_NAME:$TAG
```

##### 6. Azure Function をデプロイする

```
az functionapp create --name $APP_NAME --resource-group $RG  --functions-version 4 --runtime node  --environment $APP_CONTAINER_ENV_NAME --workload-profile-name "Consumption"  --storage-account $STORAGE_NAME  --registry-server $LOGIN_SERVER --registry-username $REGISTRY_NAME --registry-password $ADMIN_PASSWORD --image nodescraperregistryrg20240930.azurecr.io/nodewebscraperrg20240930:WebScraperAppRG20240930
```

##### 7. 動作確認

以下のコマンドで帰ってきたURLにブラウザ経由でアクセス

```
az functionapp function show -g $RG -n $APP_NAME --function-name "httpTrigger" --query "invokeUrlTemplate" --output tsv
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147841/7c15d640-8083-9e92-4ced-1d683c19371e.png)

#### 感想

- ちゃんと動いてよかった
- 写経しただけで仕組みが分かっていないので、理解が必要
- クラウドあるあるだが、6.Azure Functionデプロイ後に不安定な時間があるので気長に待つ
- 作成して時間が経過していないのでどれくらいの課金が発生しているか分からない。後日確認する(課金アラートは設定済み)

### メモ

[【令和最新版】令和のWebスクレイピング(クロール)【ベストプラクティス】](https://zenn.dev/fp16/articles/42b8047dad3bdc)