TikTok へスクレイプするバッチを GCP 上で構築しました。 GCP 構築のシステム設計話と、その構築時に、ハマったことを共有します。
2020 年、最もダウンロードされたアプリが Facebook を抜いてTikTokが一位になったそうです。
私も TikTok を利用しています。
ネットサーフィンをしている時に、tiktok-scraperというライブラリをcloudflare のサイトで発見しました。これを使って、TikTok の情報収集できるんじゃないかなと思い始めましたのがきっかけです。
※ スクレイプは私的利用であることが前提です。また、TikTok へ負荷をかけないようスクレイプ間隔に配慮しましょう。
Scrape and download useful information from TikTok. No login or password are required. This is not an official API support and etc. This is just a scraper that is using TikTok Web API to scrape media and related meta information.
上記とおり、TikTok の WebAPI を通してスクレイプします。 ライブラリでは、特定の TikTok 動画をダウンロードすることができますが、次の切り口で、TikTok 動画を一括ダウンロードすることもできます。


加えて、メタ情報(フォロワー数やいいね数など)も手に入ります。
中には、ユーザー画像や動画カバー画像などの TikTok CDN へのリンクもあります。(https://p16-sign-va.tiktokcdn.com)
リンクには、有効期限を示す文字が含まれており、一定の時間が経過すると Access Denied となります。

手に入れられない情報は、ログインが必要なものです。 例えば、私がフォローしているユーザーとかです。 その情報が欲しかったので、どうにかして手に入れました。(詳細は省きます) そのユーザー情報を使って、先程のユーザーという切り口で TikTok の動画やメタ情報を収集するバッチを作ろうと考えました。
※ WebAPI を叩きすぎると、TikTok 側のブラックリストに追加され、アクセス拒否されます。
バッチを動かす環境ですが、プライベートでよく使っている GCP 上で構築しようと思いました。 バッチで収集したデータを閲覧する Web アプリケーションも作ろうと考え、Netlify と React で動かすことにしました。

私がフォローしているユーザーの TikTok 動画やメタ情報を集めること。
実際に構築した GCP のシステム設計図が、次の画像のとおりです。

GCP リソースの用途は、次のとおりです。
| GCP リソース | 用途 |
|---|---|
| Cloud Scheduler | バッチ起動のスケジュールを管理 |
| Cloud Worlflows | バッチのワークフローを制御 |
| Cloud Run | 役割に応じて処理 |
| PubSub | Cloud Run を繋げる |
| Cloud Storage | 動画を保存 |
| AutoML Vision | 動画のカバー画像をラベル検出 |
| Cloud SQL | 全てのメタ情報を管理 |
各 Clour Run の役割は、次のとおりです。
| Cloud Run 名 | 役割 |
|---|---|
| Loader | ユーザー情報を読み込む |
| Processor | 一連の処理を行む |
| Scraper | TikTok へスクレイプする |
| Storer | 渡された情報を保存する |
| Uploader | 動画をダウンロードし、Storage へアップロードする |
| Visioner | 画像を(Vision API を通して)ラベル情報を抽出する |
| API | Cloud SQL とのインターフェース |
当初、PubSub は使わずに、Cloud Run の連携は Cloud Workflows で行おうと考えていました。 PubSub でワークフローを制御するよりも、Cloud Workflows の yaml でワークフローを制御した方が分かりやすいと思ったからです。 具体的には、Cloud Run へ HTTP リクエストし、HTTP レスポンスに応じて、次の Cloud Run を呼び出そうと考えていました。
ただ、Cloud Workflows には、次のページに書いてあるとおり、いくつかの制限があります。
特に困ったのが、全ての変数のメモリ合計が、64kb だということです。 HTTP レスポンスの Body を変数保持する構成を取ると、そのサイズを考慮しなければいけません。 いくつかやり方を見直してみたのですが、思うような形に仕上げることができず、断念しました。 結果、PubSub を使って Cloud Run を連携することになりました。 Cloud Workflows は、バッチのキック、通知などをすることとなりました。
GCP でデータストレージで、無料枠がある Firestore を当初使っていました。 理由は、単純に GCP 無料枠として Firestore があったからです。
当初、Firestore を使って、バッチと Web アプリを書いていました。 Web アプリには、バッチで収集した TikTok の動画を一覧表示する View を用意しました。
閲覧する TikTok 動画が多くなると、ページネーションが欲しくなりました。 そこで、Firestore でページネーションの実現方法を調べてみると、次の資料を発見しました。
firebase.google.comこれを見ると、ページネーションは、現在位置から ±1 ページの移動は簡単です。
資料にあるサンプルコードのように、startAfterを使えばよいだけです。
var first = db.collection("cities").orderBy("population").limit(25);
return first.get().then((documentSnapshots) => {
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length - 1];
console.log("last", lastVisible);
// Construct a new query starting at this document,
// get the next 25 cities.
var next = db
.collection("cities")
.orderBy("population")
.startAfter(lastVisible)
.limit(25);
});しかし、現在位置から ±2 ページ目以降への遷移がしたい場合は、どうすれば良いでしょうか。
上記のサンプルコードで言えば、firstをコピペしてsecond変数を生成するのでしょうか。
それよりも、offsetメソッドがほしいところです。
しかし、次の資料を発見し、諦めることになります。
オフセットは使用しないでください。その代わりにカーソルを使用します。オフセットを使用すると、スキップされたドキュメントがアプリケーションに返されなくなりますが、内部ではスキップされたドキュメントも引き続き取得されています。スキップされたドキュメントはクエリのレイテンシに影響し、このようなドキュメントの取得に必要な読み取りオペレーションは課金対象になります。
という訳で、クエリカーソルを推奨されています。
解決策としては、順序を示すフィールドがあれば、解決するかもしれません。
例えば、orderというフィールドを用意し、1,2,3 とインクリメントしたデータがあれば、クリアできるかもしれません。
startAfterの引数は document オブジェクトだけではなく、orderBy 句で指定したフィールドの変数を含めることができます。
var next = db.collection("cities").orderBy("order").startAfter(50).limit(25);これだと、1 ページ 25 個のデータを表示するならば、3 ページ目(51~75)を取得できます。(startAfterは開始点を含めません)
そもそも、ドキュメントベースの設計よりも、RDB の設計に慣れていた私は、 Firestore よりも、Cloud SQL の方が扱いやすいと思いました。 そこで、データストレージを Firestore から Cloud SQL へ切り替えることとしました。 改修自体、Cloud Run の役割が明確に分離されていたので、一部の処理を書き換えるだけで、簡単にできました。
Cloud Run と PubSub の連携には、Eventac を使用します。
昨年 10 月、60 を超える Google Cloud ソースから Cloud Run にイベントを送信できる新しいイベント機能、Eventarc を発表いたしました。Eventarc は、さまざまなソースから監査ログを読み取り、それらを CloudEvents 形式のイベントとして Cloud Run サービスに送信します。また、カスタム アプリケーションの Pub/Sub トピックからイベントを読み取ることもできます。
この Eventarc のソースとして、Cloud Storage の Object.create をトリガーとして設計を考えていました。 しかし、そのイベントをフィルタリングする選択肢は、2 つしかありません。
できるのは、執筆時点(2021 年 8 月)で、次の 2 つです。
All resource は、Cloud Storage の全てのバケットにおける Object.create イベントがトリガーとなります。
Specific resource は、特定の Obeject 名が Object.create された場合のみ、トリガーとなります。
欲しいなと思ったのは、Specific resouce の正規表現によるフィルタリング、任意のバケットやフォルダの配下で限定など
のフィルタリングです。例えば、gs://bucket/folder/*.json のような形式です。現状は、gs://bucket/folder/A.jsonとするしかありません。
今回は、PubSub のイベントのみでトリガーするようにしました。
Cloud Run で、5XX 系のエラーとなった場合、PubSub の再試行されます。
何度も PubSub が実行されると、Cloud Run のコンピューティングリソースが消費され続けます。 そうすると、課金が発生するので、対策が必要です。
Cloud Workflows は、あくまでワークフローの管理です。 変数処理などは、基本的に使わず、ワークフローのタスクを連結するだけにした方が良いです。 次の資料には、Cloud Workflows で使える標準機能です。
ワークフローのタスクを並列処理する機能は、まだ実験段階なので、本番環境は使えないようです。
システム設計変更が度々変更がありつつも、目的とする TikTok 動画やメタ情報を収集することは達成できました。 変更があったとしても、役割をできる限り小さく保つことで、変更に柔軟に対応することができます。 また、実際に動かすことで、気付けるポイントもあるので、フィードバックサイクルを短くすることも大切です。
まだまだ改善する余地はあります。ユーザー情報という切り口で情報収集していましたが、トレンドやハッシュタグなどからも 取得できるようにしたいです。また、ユーザーの RSS を作ることで、金銭的な節約もしてみたいと思っています。
-
タグ「サービス」の記事
どうも、Web業界で働き始めて9年目の駆け出しエンジニア、silverbirderです。Spotifyで音楽を聴いていると「この曲、どこかで聞いたことがあるけど、何の主題歌だったかな?」と思うこと、ありませんか?特にドライブ中や作業中に、ふと気になることが多いですよね。 私もそんな経験があり、気になったその曲が主題歌だったアニメを見始めたことがきっかけで、「簡単にタイアップ情報(アニメやドラマなど)を調べられるアプリがあったら便利だな」と思い、このアプリを作ることにしました。
結論 `iframe.contentWindow` から `twttr` オブジェクトを見つけて、`event.bind("rendered", () => {})` の第二引数に、表示処理を書くことです。 背景 `https://twitter.com/openwc/status/1427617679427440643` のような URL から、埋め込みコンテンツをブログサイトなどに表示したいです。 `https://publish.twitter.com/oembed?url=${URL}` のレスポンスの中の html が、埋め込みコンテンツになります。これを iframe の srcdoc に設定することで、埋め込みコンテンツを表示することができます。
みなさん、Zoom使っていますか? ZoomのMeetingを自動生成するGASライブラリを公開しましたので、そのきっかけと使い方について紹介しようと思います。
タグ「クラウドインフラ」の記事
Dockerイメージ内の構造や設定が期待通りかどうかを検証する `container-structure-test` を知りました。container-structure-test GitHub リポジトリ。せっかくなので、試してみました。
BigQuery、皆さん使っていますか? 私は、業務でBigQueryを使ったデータ構築をしています。品質担保のため、BigQueryのSQLに対してテストをしたいと考えています。本記事では、BigQueryだけで完結し、かつ、Mockデータを差し替え可能なユニットテスト手法について、紹介します。
2020年3月頃からコロナが流行りだし、もう12月になります。働き方が大きく変わり、リモートワークが当たり前の時代となりました。エンジニアの働き方も同様に変わりました。そこで、今回はCloud IDEというものを紹介しようと思います。
2020-12-12
タグ「成果物」の記事
個人サイト(ジブンノート)をリニューアルしました。本記事では、個人サイトをリニューアルした際にあった出来事などを振り返りたいと思います。ちなみに、個人サイトは以下のページです。ノート風デザインで、ブログ記事が読めるようになりました!🎉
個人サイトをリニューアルしました!🎉 https://silverbirder.github.io リニューアルは、今回で6回目です。制作期間は、去年の12月27日から今年の1月28日までの約1ヶ月間です。個人的には最速の開発期間でした。AIの力は偉大ですね。本記事では、個人サイトのリニューアルでこだわったポイントについて紹介します。
2026-01-28
個人開発として、機能リクエスト投稿サービスを作成しました。 サービス名は Fequest で、Feature Request の略です。 Fequest は、プロダクトに対して「この機能を追加してほしい」「ここを改善してほしい」といった要望
2025-12-28
タグ「クローリング」の記事
zodのrefineを使っていたのですが、pathの使い方を全く理解できておらず、小一時間ほどハマってしまったことがあったので、備忘録として残しておきます。
WikiWikiWeb というコンセプトが好きで、そのコンセプトが含まれている Obsidian や Scrapbox が好きです。Obsidian には、obsidian-gitという Git 連携のプラグインがあります。こちらには、デスクトップだけでなく、モバイルからでも Git Commit できるようになりました。