Closed17

【Flutter x PHP WebAPI】日本一タメにならないニュースアプリを作る

歩兵歩兵

ついに気づいてしまった。

1日の中で「GunosyとTwitterとSafari」しか見ていないことに。

ついでにいうと見ている内容は、経済動向であるとかテックトレンドとか最新ガジェットとか、そんなものであるわけもなく、
「2chまとめサイト、クスッと笑えるつぶやき、ゲーム系・Vtuber系のブログ」だけなのである。

エンジニアの端くれとして、これはマズい
技術の勉強に、最新の情報に、ついて行かなければならない。
そうは思うが、いつも思うだけである。手がついていかない

興味があることに対しては没頭出来ていたはずなのに、最近歳を取るたびに手が動かなくなっていく。
「これは面白い」というサービスを思いついても、頭の中で考え、想像しては完結してしまう。

そしてついに、新たな情報を得ようというアンテナすら放棄してしまっている自分に気づいてしまったのだ。
"タメにならない"情報ばかりを追いかけていることに。

発想の転換

ポジティブにいこう
よく考えてみれば"タメにならない"情報に関していえば、今の私は興味があるということである。
では、"タメにならない"情報究極的なUXで提供できるアプリを、

自分のために作ってみてはどうなんだろうか。

それなら、私が毎日使っている"タメにならない"情報への時間投資も有効活用できるではないか。

よし、作ろう。

歩兵歩兵

まとめサイトを見るのにGunosy

先述した通り、僕は2chまとめサイトを見るのにGunosyを利用しています。
もちろん2chまとめのアプリを利用したこともありますが、下記点からいつもGunosyに舞い戻ってしまいます。

2chまとめアプリは...

  • 総じてデザインクソダサい
  • レコメンドのロジックが皆無
  • エロネタがやたら多い

デザインは除いたとしても、
既存のアプリはほぼ全てと言っていいほど「人気or新着」の一覧表示ロジックしか持ち合わせてません。

僕のようなユーザーは1日に何度も開くor長時間利用するので
2~3回開くころには上部表示の記事は全て読み終えていて、
新着タブでひたすらリロードしているか、ありえない深さまで下スクロールするかの2パターンしかありません。

この行為を、もう数年は繰り返してきました
この数年の無駄な行為に終止符を打とうと思います。

また「エロネタがやたら多い」という部分も大きな理由の一つで、
そりゃ男なのでエロネタは嬉しいっちゃ嬉しいんですが、
Gunosyを開くタイミングは電車や待ち時間など、人が周りにいる時間が意外と多く
もし画面を見られたらヘンタイだと思われてしまう...ッ!」と思うと気軽に開くことが出来ません。

その点Gunosyは上場企業が運営しているので、記事選定に加え危ないワードは変換されているので安心です。

なので、目標としては「Gunosyに勝るUXの"タメにならない"ニュースアプリを作る」ということになります。
がんばりましょう。

歩兵歩兵

Gunosyのポイント


まずはGunosyのポイントをまとめてみました。
ひとつづつ見ていきましょう。

  • 多様なメディアからの収集
    言わずもがなでしょう。
    どれくらいのメディア数が提携されているのか数えるのも面倒なレベルです。
    決算書などにもメディア数についての記述はありませんでした。

  • 独自のレコメンドロジック
    Gunosyでは「推薦システム(記事のレコメンドロジック)」に関して論文を出されているそうです。

Greedy Optimized Multileaving for Personalization

先ほどの投稿で"Gunosyに勝る"と言いましたが、無理です負けました
ちなみに日本語でも概要のロジックを解説されている記事が存在しますが、

負けました
「そうなんですね」としか言えません。末端の雑魚エンジニアである僕にはもうすでに理解できません。
ここは独自ロジックの最適化を出来るところまでやります。

  • サムネによる簡易内容把握
    Gunosyは、競合のスマートニュースやYahooニュースと比較するとサムネイルの表示割合大きいです。
    これは、時事ニュースに加えスポーツ、アニメ、エンタメなどの幅広いカテゴリを持っていることや
    動画、ライブなどのコンテンツを主軸に構えていることからも、画像比率の大きさにユーザーが違和感が覚えないことが推測できます。
    ただ、2chまとめ記事においては、サムネイルは「全く必要ない記事/重要な記事」の二極化されます。

    例えばGunosyのこの「アラフォー同僚〜」という記事に対してのネコのサムネイルですが、これだけのコンテンツサイズを取っていますが、全くと言っていいほどその狙いは機能していません
    それどころか写真素材の背景も白であることで、コンテンツの境目すらわかりにくくなっています。

  • 記事元の表記(信頼)
    Gunosyでは記事詳細ページの他に、記事一覧でも出典元の記載をしています。
    これは情報の信用力を増加させる目的を持っているように思います。
  • 「スフィンクスを見物する侍(東洋経済オンライン)」
  • 「スフィンクスを見物する侍(ロケットニュース24)」
    上記の二つを比較してみると記事に対する印象が全く変わりますよね。

  • 1日数回の更新頻度
    先述した通り、Gunosyはレコメンドロジックに力を入れすぎているせいか、更新頻度はあまり多くありません。
    短い時間で何度もアプリを開くことよりも、1日に数度、情報をまとめて確認するような利用を想定しているような気がします。

  • 記事内容のサマリー(余排除)
    Gunosyのアプリ内で掲載されるにあたって、元記事(ブログなど)のヘッダー情報や関連記事情報など
    記事に対して直接関係のないメディアの情報は排除され整形されています。
    ただ、この作用が全てのメディアに対して設定されているせいか、
    2chまとめ記事を見ていると重要な情報が抜け落ちていることがよくあり、わざわざSafariで該当サイトまで見にいくことがあります。

調べれば調べるほどに勝算はなくなりましたが、ポジティブに行きましょう。

歩兵歩兵

技術選定(フロントエンド)

元々Swiftでの開発経験は4年ほどありますが、今回はAndroid版も同時リリースを目指すためクロスプラットフォームフレームワークを利用していきます。
ReactNative とFlutter パフォーマンス比較
クロスプラットフォームフレームワーク比較 2021(Flutter, React Native, Xamarin, Unity)

ふむふむ。
もう少し調査してみよう...。

Wantedlyで調査
Flutterの求人、めっちゃ多い。React Nativeと比べて雲泥の差。

!!
好きなアプリを作って職場選択肢も増えてしまうとは...潰しがきくとはこの事か...。

ということで、あまりにいやらしい理由ではあるものの、
複合的な観点からフレームワークはFlutterを利用していくことに決定しました。


技術選定(バックエンド)

化石と謳われるLAMP環境(&オレオレフレームワーク)でWeb APIを構築していきます。
この機会に新しい技術を、とも思いましたが、
複雑なロジックを構築する上では、慣れ親しんだ技術でやることが僕自身にとってストレスが少ないためです。

歩兵歩兵

PHPフレームワーク

本業ではLaravelやCakephpを使ってきましたが、フレームワークのメリットをあまり感じないんですよね。
軽量フレームワークであるSlim(4系)も利用してみましたが、う〜ん...。
今回は自作のフレームワークで行こうと思います。

PHPでオレオレフレームワークを作ってみた

歩兵歩兵

レコメンドロジック

記事のレコメンドロジックについては、いくつかの要素に点数付けをして最適化していこうと考えています。
現時点で加点要素になりうるものは下記です。

■ ユーザー側の要素
- 年代
- 性別
- 好みのジャンル
- 好みのタグ(形態素)
- 特定の減点要素

■ 記事側の要素
- メディアパワー
- ジャンル
- タグ(形態素)
- 鮮度
- Good
- Bad
- シェア
- 平均滞在時間
- クリック率

それぞれの値には重みがついており、加算・乗算などでスコアリングしていきます。
記事側の要素については、サーバー側でスコア付けをし、記事毎の暫定スコアを算出します。
それに対し、ユーザー側の要素を掛け合わせ、ユーザーに対する最終的なスコアリングを算出し、ソートして表示する形です。

ちなみに、Gunosyのレコメンドについて、記事に記述がありました

  1. ユーザーがクリックした記事の内容語を元にユーザーを一定のクラスタ数にクラスタリング
  2. クラスタに所属するユーザーのImpression, Clickを集計しクラスタ毎に記事のCTRを算出
  3. ユーザーからのリクエストに対してユーザーの所属するクラスタで流行っている記事 (CTRが高い記事) を返却する

この場合、記事にも記載があるのですが「ユーザー個々人にあった記事の配信ができていない」という問題が生まれます。
詳細はこちらをご覧ください。
グノシーのパーソナライズアルゴリズムを刷新した話 (モデル編)

グノシーでは時事性に重きをおくためのロジックにされているようですが、2chまとめ記事だけにフォーカスするのであれば、時事性はそこまで重要ではなく
それよりも、利用する当人にとって「見た事のない記事」が重要になってきます。
そうなると、「データの蓄積」が重要になってきます。

このあたりの調整は実際にロジックを組み、シミュレーションしてチューニングしていくしかなさそうですね。

歩兵歩兵

memo
レコメンドロジックを完全にシステム側に一任してしまうか、
ユーザーインターフェースである程度、常に調整できるようにしてしまうか

歩兵歩兵

UIを構築するにあたって

まず最も重要なUI画面は「記事一覧」です。
記事(コンテンツ)の内容(≒クオリティ)については、まとめブログ様に一任することになりますが、
読む記事を選定する」という行為について、このアプリでしか出来ないので、頭をフル回転させて考えます。

表示要素

初めに考えることは「必要な要素」です。
記事一覧画面には何の要素が必要なのか?ということなんですが
どのように考えていけば良いと思いますか?
一緒に考えてみましょう。

・・・。

Youtube型とTwitter型

僕が勝手に分類してるだけなのですが、SNSの一覧画面の設計は2つに分類できます

  • 事前情報が少なく、アクションの後にボリュームあるコンテンツが見れるもの
     例: Youtube
     事前情報: タイトル+サムネ→一覧情報多
     コンテンツ: 動画コンテンツ(数分〜数十分)

  • 事前情報がある程度(概要レベル)あり、アクションの後に残りのコンテンツが見れるもの
     例: Twitter
     事前情報: 本文+画像など→一覧情報少
     コンテンツ: 補足Tweet、コメント、動画など

前者は、一覧で見れる情報の数は多く・事前情報は少ないが、コンテンツのボリュームがあり(=情報量が多く)、一度見始めてしまえばユーザーを虜にできる魅力的なコンテンツであることが多いです。
後者は逆に、一覧で見れる情報の数は少ないものの、概要レベルのコンテンツがほぼ主目的であるため流し読みすることが出来ます。また、補足コンテンツを見て概要コンテンツを補強していくような楽しみ方になります。
※「一覧で見れる情報」=一度にUIに表示される要素数

これをニュースアプリに当てはめると、Gunosyやスマートニュースは前者、Newspicksなどがギリギリ後者にあたります。
ただそれはコンテンツがニュースの場合で、2chまとめアプリでは前者のパターンしか存在しません。

しかし、僕が利用する限り、2chまとめアプリこそ後者のパターンの楽しみ方が最適であるはずなのです。
よく「TwitterはInstagramやFacebookの仲間ではなく、2ちゃんねるの仲間だ」なんて言われたりしますがまさにその通りです。
情報の取得の仕方、楽しみ方がInstagramやFacebookとは違うのです。

それでは上記を踏まえて、UIを作っていきます。

歩兵歩兵

コンテンツの表示形式

あと大事なことを忘れていました。
記事一覧画面でのコンテンツの表示形式についてですが、もうひとつポイントがあります。
既存のニュースアプリやまとめアプリでは「一覧表示」という形式が使われています。

特定の順序でソートされた記事が、一覧で表示されているというものです。
この形式のデメリットとして、「読んだ記事」も次の更新タイミングまでは一覧画面に滞在し、非表示になることはありません。
もちろんこれによるメリットはありますが、僕がGunosyを読んでいて、同じ画面で二度その記事に入ったことは8000回に一度くらいです。
つまり、同じ記事を表示させる必要はなく、その箇所を非表示にし新しい記事を表示させた方がユーザーからすると効率的になります。

フィード型

これを解決したのがFacebookなどに代表されるフィード型のアプリです。
一度閲覧した記事については、「ニュースフィード(≒記事一覧)」に表示されることは(特定条件下を除いて)ありません。
再度見たくなった場合には、該当の人物のフィードに行く事で閲覧することが出来るようになっています。

これでいいんですよ

QuoraなどのQAアプリではこの形式になってきていますが、
やはりフィード型だと滞在時間が段違いに増加します。
「見るものがなくなった」という感覚をUIから感じとることが出来なくなるので
時間が過ぎたことも、自分がどれだけコンテンツを消費したのかすら忘れてしまうほどに没頭してしまいます。

なぜいまだにニュースアプリではこの形式がないのか疑問で仕方ありません。
(ちなみにNewspicksは一部この形式を採用してますがUIがクソすぎるので論外です)
今回制作するアプリではこの「ニュースフィード」形式を採用していきます。

歩兵歩兵

UI表示における重要な要素

読む記事を選定する」うえで重要な内容は「概要レベルのコンテンツ」だと先述しました。

これを2chまとめアプリに当てはめてみると、記事の種類によって「重要な要素」が変化してきます。
画像系のスレなのか、雑談系のスレなのか、1のレス(内容)に言及していくスレなのか。

今回はUIを構築するうえで、二つのUIに分類することにしました。

  • 画像系まとめスレ
  • それ以外のスレ

画像系のまとめスレについては、いわずもがな「スレに貼られた画像の数々」が概要把握できることが目的達成につながります。
これは2chまとめアプリの先人たちが残してくれたUIをもとに設計します。

画像系まとめスレUIイメージ


要素としてはこんな感じですね。
これにいいね・シェアボタン付けるとか、その他の要素を付け足していきます。

それ以外のスレについては難しいところです。
タイトルだけでは、概要を把握するには不十分です。
impを増やすために「惹きタイトル」といってタイトルの内容を盛っているまとめブログも多くある中で、タイトル情報と内容が相違し、記事の滞在時間が短くなってしまうとUXに悪影響を及ぼします。
(クリックすることは行動消費であり、内容を確認して戻る行為は"無駄な行為をした"と認識してしまう)

Gunosyなどのニュースアプリではサムネも表示していますが、先述したようにサムネは必要ではありますが、2chまとめ記事においてサムネを大きく表示するのは意味がないことが多いです。

それ以外にも何が必要なのか探るために、
他アプリで「内容を読み進めるか判断する要素」がなんなのか気にしながら触ってみました。
そこで判明したのはこれらです。

  • タイトル(最重要)
  • サムネ(あった方がいい)
  • イッチの内容(タイトルの次に重要で、内容に相違がないか)
  • 数個のコメント(世間の評価と自身の感情の相違を確認する)

多くの場合は「イッチの内容」にスレ概要が表示されていました。
ここを読むことでスレの内容を把握する事ができました。
また、いくつかのコメントを読むことでも自分と記事の意見の相違を把握し、記事の内容や流れによって読み進めたいという動機になることもわかりました。

ではさっそく、これらをUIにしてみましょう。

それ以外のスレUIイメージ


う〜ん、概要がないと内容の把握が出来ない...



う〜ん、コメントがないとどういう流れか把握が出来ない...



いい感じ
内容の把握と流れの把握ができるので、記事がどういった構成で作られているのか「概要が把握できる
また、既存のニュース・まとめアプリと比較すると一覧表示としてのコンテンツ割合は大きいものの、一度見たら表示されなくなる「フィード型」なので、そのストレスも軽減されます。

いい感じですね。
これをベースに改良していきます。

歩兵歩兵

ちなみに僕はUIを作るときはXDを使っています。

Flutterに書き出しもできるようなので、メリットだらけですね。
Adobe XD to Flutter プラグインが一般公開されました!
※こういった書き出し系のプラグインは痒いところに手が届かない事が多々あるので実際利用するかは別ですが

デザインについても全くの独学ですが、dribbbleなどで参考UIを確認して良いところだけ真似していけば最新のUIが作れるようになると思います。

dribbble

ただ「おしゃれなUI」といっても、

  • 色使い
  • 配置
  • フォント

この3つがおしゃれを構成する要素の9割です。
シャドウとかグラデはついでみたいなもんです。本当に

「サービスを作りたい!」と思う以上は企画もデザインも開発も自らでやっていくしかないので、経験を積んで自分自身が納得できるデザインは作れるようにしていきましょう。

誰かに「ダセェwww」と言われても自身が納得出来ていたらあまり気にならないものです(多分)。

歩兵歩兵

権利関係

既存のまとめアプリで、記事本文のUIでは下記2パターンに分かれる場合がある

  • WebView表示(ページURLをそのまま表示させる)
  • 一部を整形し表示(ヘッダー+本文のみを抽出し表示させる)

後者の場合、該当のまとめブログ様への許可が必要なのだろうか
ただ、多くのアプリで任意ブログ追加を可能にしているので、無断で整形しているのだろう。
そもそも2ch自体がまとめブログに対して許可性を取っているが、まとめブログのまとめに対しては判断を一任しているのだろうか...。
ただ、Gunosyやまとめアプリ大手が全てのサイト経由で2chへの許可を取っているとも考えられないため、黙認状態なのか。

RSSに表示される情報のみを扱う記事一覧は問題なさそうだが、本文については確認が必要そう。

歩兵歩兵

テーブル設計

・記事ID取得テーブル(最新記事xxx件)

・RSSマスターテーブル
・RSS情報格納テーブル
・整形済記事概要格納テーブル(=記事一覧画面用)
・整形済記事本文格納テーブル(=記事詳細画面用)

・GOODログテーブル
・BADログテーブル
・シェアログテーブル
・記事閲覧ログテーブル
・記事閲覧時間ログテーブル

大体こんな感じでしょうか。
基本的な流れとしては、

No 流れ
1 RSSマスタのRSS URLから各記事の情報取得
2 (RSS情報にcontent情報が格納されている場合は)情報から整形して記事概要格納テーブルへデータ格納
3 記事URLから記事詳細用のデータを整形して、記事本文格納テーブルへ格納
4 上記が完了した記事IDを記事ID取得テーブルへ追加(そしてテーブルが規定値以上の場合は古いものからDELETE)
*ここまでがバッジ処理
*ここからユーザーアクション処理
5 ユーザーからリクエストがあったら、記事ID取得テーブルからxxx件の記事IDとスコアを取得
6 ローカルで既読記事の精査
7 ユーザー(ローカル)側要素のスコアを各記事に加算
8 記事ID一覧を最終スコア順にソート
9 アプリ側のスクロールに合わせてxx件づつ記事概要テーブルからデータを取得
10 記事詳細ページに遷移する際に記事本文テーブルからデータを取得
11 各ユーザーアクションは非同期で、各ログテーブルへ格納
※ 記事ID取得テーブルのAPIは5分間隔でパラメータを持たせて、CDNを挟んで負荷対策

ただ、このロジックの懸念点は、特定期間以外(xxx件超過分)の記事が評価&表示されないことなんですが、
そこまではニュースフィード画面でやる必要はないので、UI上で機能と目的を切り分けていきます。

歩兵歩兵


アプリ側への受け渡しはjsonで行います。
フィード画面へ渡すデータは、投稿の種類(通常スレ/画像スレ)によって変動しますが、このような感じ。
※これらは全てRSSの情報のみから取得した情報です。

画像URLがメディアの直リンクになっているので、一旦こちらのサーバーに置き直してURLを置換する予定。

ちなみにキー名のxxx_1、xxx_2とかという命名方式は汎用性がないのであまり好きじゃないんですが、運用するときに何かと便利なので結局使ってます。

このスクラップは2021/11/08にクローズされました