【.NET】Twitter APIを使って推しの画像に溺れたい③
こんにちは。
会社の研修で忙しくてちょっと間が空きました。
あと、ヴィクトリアマイルを現地でみてきました。ソダシが抜け出したときに歓声が一段と大きくなって、みんなでアイドルホースを応援できる競馬はいいなぁと思いました(なお馬券はry
今回は「検索のチューニング」と「画像URL取得」です。
検索条件を詳細にする
前回検索で使ったGetSearchTweetsV2Iterator
メソッドを含むクラスは以下のように定義されています。
クエリ指定
よく見ると1個目のGetSearchTweetsV2Iterator
の引数名がkeywordとかではなくqueryとなっています。
なぜqueryと名付けているかというと、ここには検索ワードだけでなく検索条件をいろいろ詰め込んで複雑な検索ができるようになっているからです。
クエリの組み立て方は以下に記載されています。
説明を参考にクエリを組み立てると "#ほしまちぎゃらりー -(is:retweet OR is:reply) has:images" となります。
意味は「本文に"#ほしまちぎゃらりー"を含む」かつ「リツイートおよびリプライのツイートではない」かつ「画像がある」です。
パラメータクラス指定
さらに2個目のGetSearchTweetsV2Iterator
の引数にはISearchTweetsV2Parameters
インターフェイスが指定されています。
1つ1つのフィールドの説明は書いていませんが、おそらく以下のように使われます。
type | field | usage |
---|---|---|
DateTime | EndTime | この時刻までのツイートを取得 |
string | Query | 検索条件を指定するクエリ文字列(前セクションに記述の通り) |
int | PageSize | Iterator検索時に一度に取得するツイート数(1~100 既定は100) |
string | SinceId | 取得ツイートの最小ID |
DateTime | StartTime | この時刻からのツイートを取得 |
string | UntilId | 取得ツイートの最大ID |
ツイート時刻をStartTime~EndTimeで、ツイートIDをSinceId~UntilIdで絞り込めるようになっています。
PageSizeは基本指定しなくていいと思います。レスポンスサイズを小さくしたいときは100未満の数値を入れるのでしょう。
Queryは前セクションのクエリです。
実際に使ってみよう
TwitterClient userClient = new(COMSUMER_KEY, CONSUMER_KEY_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
// このあとプロパティ設定を追加するのでパラメータクラス指定にする
SearchTweetsV2Parameters parameters = new("#ほしまちぎゃらりー -(is:retweet OR is:reply) has:images");
// イテレータ取得
var searchIterator = userClient.SearchV2.GetSearchTweetsV2Iterator(parameters);
List<string> csvLines = new();
int count = 0;
while (!searchIterator.Completed)
{
// コンソールに進捗を出しておく
count++;
Console.WriteLine($"{count}ループ目");
var searchPage = await searchIterator.NextPageAsync();
var searchResponse = searchPage.Content;
var tweets = searchResponse.Tweets;
foreach(var tweet in tweets)
{
csvLines.Add(string.Format(
"{0}, {1}, {2}, {3}, {4}",
tweet.CreatedAt.ToLocalTime().ToString("yyyy/MM/dd hh:mm:ss"),
tweet.PublicMetrics.LikeCount, // いいね数
tweet.PublicMetrics.RetweetCount, // リツイート数
tweet.Text.Replace("\n", "").Replace(",", ""),
$"https://twitter.com/user/status/{tweet.Id}" // ツイートURL
));
}
}
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
File.WriteAllLines(@"C:\Users\user\Downloads\TweetSeatchTest.csv", csvLines, Encoding.GetEncoding("Shift_JIS"));
結果
きちゃあああああああヾ(。>﹏<。)ノ゙✧*。
さっそく推しに溺れよう
いいね数で降順に並べ、一番いいほしまちぎゃらりーを見に行きます。
あああああかわいいいい!!!!すいちゃん大好き!!!!!!
画像ダウンロード用のURLを取得する
ツイートのURLはわかりましたが、画像をダウンロードするためのURLはまだわかりません。
次は画像URLもCSVファイルに出力できるように実装します。
メディアURLの在り処
ツイート情報(Tweet object
)には2つのプロパティがあります(上サイトのSample Response参照)。
-
data
:ツイート本体の情報(ツイートID、ユーザーID、ツイート日時など) -
includes
:付随する情報(メディア情報、位置情報、関連するツイートなど)
これまで取得してきた情報はすべてdata
に含まれるもので、紐づく画像URLはincludes
に含まれています。
これを知らなかったために、結構時間を食わされました。
ツイート情報とメディア情報の紐づき
理想的には1つのツイートに複数のメディアが紐づいた状態でひとまとまりになり、それを配列で返してくれると嬉しいのですが、上記の通りそうはなっていません。
data
とincludes
に分かれているように、ツイート情報のみの配列と付随情報のみの配列で別々にデータがやってきます。
そのため各付随情報はどのツイートに紐づいているのかがわからないと使えません。
紐づきを担っているのがdata.attachments.media_keys
です。
このキーを使ってincludes.media
を検索します。
media
の構造はMedia object
で定義されています。
Media objectにmedia_key
があり、これによりツイートに付随するメディアを特定できます。
Tweetinviではどうなっているのか
APIのレスポンスと同じようにクラスが構成されています。
イテレータから取得したレスポンスデータは、JSONをデシリアライズしたものをひとまとめにした構造になっています。
実際に使ってみよう
TwitterClient userClient = new(COMSUMER_KEY, CONSUMER_KEY_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
var parameters = new SearchTweetsV2Parameters("#ほしまちぎゃらりー -(is:retweet OR is:reply) has:images");
var searchIterator = userClient.SearchV2.GetSearchTweetsV2Iterator(parameters);
// K:MediaKey V:URL
Dictionary<string, string> imageUrls = new();
// メディアURLのリスト
List<string> urlList = new();
List<string> csvLines = new();
int count = 0;
// CSV Header
csvLines.Add("id,datetime,like,retweet,text,url1,url2,url3,url4");
while (!searchIterator.Completed)
{
count++;
Console.WriteLine($"{count}ループ目");
var searchPage = await searchIterator.NextPageAsync();
var searchResponse = searchPage.Content;
// `media.includes.media`抜き出し
var media = searchResponse.Includes.Media;
// `data`抜き出し
var tweets = searchResponse.Tweets;
// MediaからMediaKeyとURLだけを詰め直し
foreach(var medium in media)
{
imageUrls.Add(medium.MediaKey, medium.Url);
}
foreach(var tweet in tweets)
{
// ツイートに紐づくメディアを検索しリスト化
foreach(var key in tweet.Attachments.MediaKeys)
{
string url = imageUrls[key];
urlList.Add(url);
}
csvLines.Add(string.Format(
"{0},{1},{2},{3},{4},{5}",
tweet.Id,
tweet.CreatedAt.ToLocalTime().ToString("yyyy/MM/dd hh:mm:ss"),
tweet.PublicMetrics.LikeCount,
tweet.PublicMetrics.RetweetCount,
tweet.Text.Replace("\n", "").Replace(",", ""),
String.Join(",", urlList)
));
urlList.Clear();
}
imageUrls.Clear();
}
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
File.WriteAllLines(@"C:\Users\user\Downloads\TweetSeatchTest.csv", csvLines, Encoding.GetEncoding("Shift_JIS"));
結果
よっしゃああああああ!!!これがほしかった!!!
では溺れましょう
はぁ~~~miCometてぇてぇ~...😇
まとめ
- イテレータからのレスポンスにAPIからのレスポンスをデシリアライズしたインスタンスが詰まっている
- ツイート情報とメディアはmedia_keyで紐づいている
次回
ひとまず欲しい情報はすべて手に入りました。
次回は設定関連です。
Discussion