app-ads.txt(ads.txt)を自動テストする

この記事はKAYAC Advent Calendar 2022の18日目です。
こんにちは、ハイパーカジュアルゲームチームでマーケターをやっているひだかです。

今日は広告配信で欠かせないapp-ads.txt(ads.txt)の話です。


ハイパーカジュアルゲームとは

まずハイパーカジュアルゲームの話からです。呼び名が長いのでハイカジと略します。
ハイカジの商売は、一般的なソーシャルゲームとは違い課金ではなく広告収益で成り立っています。
広告収益は比較的広告単価の高い日本のお客様でも1ユーザーあたり50円とか60円とかの低単価で勝負をしています。
すごく単価が低いので多くのユーザー流入が必要になります。
なので広告費を使ってどこかのメディアなどに広告を掲載して、その広告からユーザーの流入があり、アプリ内の広告を見てもらうことで広告収益が上がります。
ハイカジでは、ざっくり言うと 広告費 < 広告収益 の場合に利益を出すことができます。
その広告収益を得るために、自社アプリの中に広告枠を用意し広告を配信するので、広告配信に必要なapp-ads.txtの話をします。
ちょっと詳しい仕組みについては以下の記事に記載されていますので興味がある方はご覧ください。 www.kayac.com


app-ads.txt(ads.txt)とは

詳しくは 「app-ads.txtとは」 で検索や仕様を見てほしいのですが、ざっくりまとめると以下の特徴があります。

  • 広告枠を販売する側(アプリを出している個人や会社)が準備しないといけないウェブページ
  • 広告配信会社のクローラがapp-ads.txtを確認する
  • 広告配信会社でapp-ads.txtの確認が取れ、認証されないと配信されない広告がある

一言で言うと「app-ads.txtを適切に配置管理しないと損をする。」です。
カヤックはハイカジのアプリ内で広告枠を売っているので、app-ads.txtを定期的にメンテナンスして損をしないようにしています。

Google AdMobでapp-ads.txtの設定をしておらず怒られている様子



広告配信会社のクローラがどうやってapp-ads.txtを認識するかというと、Google Play Storeの場合はアプリを公開する際に「デベロッパーの連絡先」の「ウェブサイト」にURLを入力しますが、そこのドメイン直下にapp-ads.txtを配置していると見てくれるので、カヤックの場合はここ(https://www.kayac.com/app-ads.txt)にひっそりとあります。

Google Play StoreでのPark MasterのウェブサイトURLの記載場所



実際のファイルはインデントなどがないので非常にごちゃごちゃして見えますが、行単位で別ものになっているので行単位で見るとわかりやすいです。
app-ads.txtの記法はこんな感じで、各媒体のapp-ads.txtの集合体です。

# Hidaka Adsという媒体が用意したapp-ads.txt
hidaka.com, 20221218-aaa, DIRECT
hidaka.com, 20221218-bbb, RECELLER
hidaka.com, 20221218-ccc, RECELLER, fugafuga

# Kenta Adsという媒体が用意したapp-ads.txt
kenta.jp, 20221218-ddd, DIRECT
kenta.jp, 20221218-eee, RECELLER, hogehoge
kenta.jp, 20221218-fff, RECELLER

なお、app-ads.txtはスマホアプリ向けの広告枠を認証する仕組みで、ads.txtはウェブサイト上の広告枠を認証する仕組みになります。
app-ads.txtもads.txtもファイル内に記載する内容は同じなので、後述のテストのコードは流用できるかと思います。


app-ads.txtのメンテナンス

今現在カヤックでは、app-ads.txtに10媒体ほどのapp-ads.txtを記載しています。
この媒体というのは広告を表示するサービスで、馴染みがあるところだとTwitter AdsやUnity AdsやGoogle AdMobなどになります。
アプリ内では、広告枠に広告を表示させるために都度オークションが実施され、各媒体が入札をします。
そのオークションで勝った媒体だけが広告を表示することができます。
そのため、オークションに参加する各媒体が広告を表示させる可能性があるため、各媒体が用意するapp-ads.txtの全てを自社のapp-ads.txtにまとめて記載しておく必要があります。

この10媒体それぞれが管理するapp-ads.txtが、それぞれの媒体の管理画面などで不定期に更新されているので、常に最新を保つのは難しいのですが、常に最新にしておいた方が正しく広告が配信されるようになります。


なぜテストをするのか

媒体側が用意したapp-ads.txtが意外と間違っています。
記載している10媒体のうち毎月1つくらいはミスがあります。
大きな変更が入るときや、その時の気分でこのサービスにapp-ads.txtをコピペしてバリデーションをしていたのですが、毎回コピペするのは面倒で、自動で勝手にテストしてくれると嬉しいのでテストを追加することにしました。


テストコード

カヤックのapp-ads.txtはGithubでリポジトリ管理されております。
よくあるCI同様に、リポジトリにpushしたら、Github Actionsでpushをトリガーにテストが走るようにしました。
自社だけのチューニングがあるわけではないので、実際に使っているコードを別リポジトリに切り出してそのまま公開します。
app-ads.txtは実際に使っているのを持ってくるのは気が引けたので、文量が多いDigital Turbineという媒体ひとつをコピペしています。 github.com

Shellでテストを書くのは大変なので、実際にはperlでテストされるようにしています。
行数多いんだからyamlにベタ書きじゃなくて他のファイルに逃がせやと思うでしょうが、僕も思います。
リポジトリにも入っているtest.plでテストのテストをした後にあえてyamlひとつで済むようにしました。
なぜなら使う人はかなり限られるだろうけれども、なんとなくコピペ1回で済むようにしたかったので。

さて、実際のテスト内容は各行を正規表現でチェックしているだけです。
皆さん大好きな正規表現です。
仕様(p8-9あたり)を眺めつつそれぞれ見ていきましょう。
正規表現もガチガチの厳密にやるというよりも読めるものにしたかったので、可読性下がらず厳密にできることがあればコメントかPRください。
リポジトリからcloneしてもらって perl test.pl したらすぐ試せるかと思います。

サンプル

hidaka.com, 20221218-ccc, RECELLER, fugafuga
# カンマごとに、[第1フィールド],[第2フィールド],[第3フィールド],[第4フィールド] と区別して見ていきます

第1フィールド: hidaka.com
第2フィールド: 20221218-ccc
第3フィールド: RECELLER
第4フィールド: fugafuga

第1フィールド

ドメイン、サブドメインが記載されるところです。

# Domain name of the advertising system
$add_str->($s1) if $s1 !~ /^([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/;

第2フィールド

IDが入るところです。
英数字と_-が入ります。
厳密には_-は文頭文末には来ません。

# Publisher’s Account ID
$add_str->($s2) if $s2 !~ /^([a-zA-Z0-9]|[a-zA-Z0-9]|\-|\_)+$/;

第3フィールド

大文字小文字区別なくDIRECTかRESELLERという単語のみが入ります。
ここだけは自信があります。

# Type of Account/Relationship
$add_str->($s3) if $s3 !~ /^(DIRECT|RESELLER)$/i;

第4フィールド

まずコメントアウトを含んでいるかも知れないので除去します。
オプショナルなのでnullチェックをしつつ、適当に英数字かのテストします。

# Certification Authority ID(optional)
($s4) = split("#", $s4); # コメントアウト以下を捨てる
$add_str->($s4) if $s4 != undef && $s4 !~ /^[a-zA-Z0-9]+$/;


まとめ

多数の人がアプリ内広告(ウェブ広告)を運用するときには、そんなの用意する必要あるのか〜とか思った記事だと思います。(多分)
意外と忘れがちだったり目立たないものなので、設定を忘れていて見えない機会損失が発生しているかも知れません。
そんな地味で大事なところだからこそ、雑にでもいいから最初は最低限のテスト書いていって気分次第でテストを補強するくらいの軽い気持ちで楽な運用目指していきましょう。

明日19日目は tennichi によるSRE的なことです。明日もお楽しみに!