→コード: v4.3.0 実行時にツイートIDを指定するように変更して、使いやすくしました。あと投票も整形できるようにしました
>Fet-Fe (→コード: v4.2.0 Nitterが使えなくなるので従来のモードをdeprecatedでマーク) |
>Fet-Fe (→コード: v4.3.0 実行時にツイートIDを指定するように変更して、使いやすくしました。あと投票も整形できるようにしました) |
||
11行目: | 11行目: | ||
"""Twitter自動収集スクリプト | """Twitter自動収集スクリプト | ||
ver4. | ver4.3.0 2024/2/19恒心 | ||
当コードは恒心停止してしまった https://rentry.co/7298g の降臨ショーツイート自動収集スクリプトの復刻改善版です。 | 当コードは恒心停止してしまった https://rentry.co/7298g の降臨ショーツイート自動収集スクリプトの復刻改善版です。 | ||
71行目: | 71行目: | ||
from abc import ABCMeta, abstractmethod | from abc import ABCMeta, abstractmethod | ||
from argparse import ArgumentParser, Namespace | from argparse import ArgumentParser, Namespace | ||
from collections import deque | |||
from collections.abc import Callable | from collections.abc import Callable | ||
from dataclasses import dataclass | from dataclasses import dataclass | ||
144行目: | 145行目: | ||
"""``--search-unarchived`` オプションを付けたときに使用する設定値。 | """``--search-unarchived`` オプションを付けたときに使用する設定値。 | ||
""" | """ | ||
url_list_filename: Final[str] = 'url_list.txt' | url_list_filename: Final[str] = 'url_list.txt' | ||
"""Final[str]: URLのリストをダンプするファイル名。 | """Final[str]: URLのリストをダンプするファイル名。 | ||
500行目: | 480行目: | ||
if self._options.preferences.get('javascript.enabled'): # pyright: ignore[reportUnknownMemberType] # noqa: E501 | if self._options.preferences.get('javascript.enabled'): # pyright: ignore[reportUnknownMemberType] # noqa: E501 | ||
logger.warning(f'{url} でreCAPTCHAが要求されたナリ') | logger.warning(f'{url} でreCAPTCHAが要求されたナリ') | ||
print('reCAPTCHAを解いてね(笑)、それはできるよね。') | print('reCAPTCHAを解いてね(笑)、それはできるよね。\a\a\a') | ||
print('botバレしたら自動でブラウザが再起動するナリよ') | print('botバレしたら自動でブラウザが再起動するナリよ') | ||
WebDriverWait(self._driver, self.WEB_DRIVER_WAIT_TIME).until( | WebDriverWait(self._driver, self.WEB_DRIVER_WAIT_TIME).until( | ||
1,098行目: | 1,078行目: | ||
self._name: str = self.CALLINSHOW | self._name: str = self.CALLINSHOW | ||
else: | else: | ||
name_optional: str | None = self. | name_optional: str | None = self._input_name(accessor) | ||
if name_optional is not None: | if name_optional is not None: | ||
self._name: str = name_optional | self._name: str = name_optional | ||
1,109行目: | 1,089行目: | ||
logger.info('クエリは自動的になしにナリます') | logger.info('クエリは自動的になしにナリます') | ||
else: | else: | ||
self. | self._input_query() | ||
page_optional: str | None = accessor.request( | page_optional: str | None = accessor.request( | ||
urljoin(self.NITTER_INSTANCE, self._name + '/' | urljoin(self.NITTER_INSTANCE, self._name + '/' | ||
1,129行目: | 1,109行目: | ||
if krsw: | if krsw: | ||
logger.info('終わりにするツイートは自動的になしにナリます') | logger.info('終わりにするツイートは自動的になしにナリます') | ||
self._stop: str = '' if krsw else self. | self._stop: str = '' if krsw else self._input_stop_word() | ||
logger.info( | logger.info( | ||
1,242行目: | 1,222行目: | ||
return tuple(instance_list) | return tuple(instance_list) | ||
def | def _input_name(self, accessor: AccessorHandler) -> str | None: | ||
"""ツイート収集するユーザー名を標準入力から取得する。 | """ツイート収集するユーザー名を標準入力から取得する。 | ||
1,277行目: | 1,257行目: | ||
return account_str | return account_str | ||
def | def _input_query(self) -> None: | ||
"""検索クエリを標準入力から取得する。 | """検索クエリを標準入力から取得する。 | ||
""" | """ | ||
1,307行目: | 1,287行目: | ||
self._table_builder.dump_file() | self._table_builder.dump_file() | ||
def | def _input_stop_word(self) -> str: | ||
"""ツイートの記録を中断するための文をユーザに入力させる。 | """ツイートの記録を中断するための文をユーザに入力させる。 | ||
1,859行目: | 1,839行目: | ||
self._table_builder.dump_file() | self._table_builder.dump_file() | ||
return False | return False | ||
def _signal_handler( | |||
self, signum: int, frame: FrameType | None) -> NoReturn: | |||
"""ユーザがCtrl + Cでプログラムを止めたときのシグナルハンドラ。 | |||
Args: | |||
signum (int): シグナル番号。想定されるのはSIGINTのみ。 | |||
frame (FrameType | None): 現在のスタックフレーム。 | |||
""" | |||
logger.info('ユーザがプログラムを中止したなりを') | |||
self._table_builder.dump_file() | |||
sys.exit() | |||
@deprecated('\033[31m' | @deprecated('\033[31m' | ||
1,881行目: | 1,873行目: | ||
:class:`~ArchiveCrawler` はarchive.todayからツイートを収集するため引き続き利用可能。 | :class:`~ArchiveCrawler` はarchive.todayからツイートを収集するため引き続き利用可能。 | ||
""" | """ | ||
# Seleniumドライバーを必ず終了するため、with文を利用する。 | # Seleniumドライバーを必ず終了するため、with文を利用する。 | ||
1,909行目: | 1,891行目: | ||
# ツイートを取得し終えるまでループ | # ツイートを取得し終えるまでループ | ||
signal.signal(signal.SIGINT, | signal.signal(signal.SIGINT, self._signal_handler) | ||
try: | try: | ||
while True: | while True: | ||
1,945行目: | 1,927行目: | ||
Todo: | Todo: | ||
* ちゃんとテストする。 | * ちゃんとテストする。 | ||
""" | """ | ||
1,976行目: | 1,942行目: | ||
"""検索条件を設定する。 | """検索条件を設定する。 | ||
:class:`~TwitterArchiver` | :class:`~TwitterArchiver` のメンバをセットし、ツイートを収集するアカウントを入力させ、 | ||
収集するツイートのうち最古のツイートIDを指定させる。 | |||
Args: | Args: | ||
1,991行目: | 1,958行目: | ||
self._name: str = self.CALLINSHOW | self._name: str = self.CALLINSHOW | ||
else: | else: | ||
name_optional: str | None = self. | name_optional: str | None = self._input_name(accessor) | ||
if name_optional is not None: | if name_optional is not None: | ||
self._name: str = name_optional | self._name: str = name_optional | ||
else: | else: | ||
return False | return False | ||
# 探索対象のうち最古のツイートID取得 | |||
oldest_id: str = self._input_oldest_id() | |||
self._oldest_id_queue: deque[int] = deque(map(int, oldest_id)) if \ | |||
len(oldest_id) > 0 else deque([0]) | |||
self._latest_id_digit: int = self._oldest_id_queue.popleft() | |||
self._oldest_id_queue.append(0) # 空になると予想外の動作を起こしかねないため | |||
self._oldest_url: str = self.TWITTER_URL + self._name + '/status/' \ | |||
+ oldest_id | |||
logger.info( | logger.info( | ||
'ユーザー名: @' + self._name + 'で検索しまふ' | 'ユーザー名: @' + self._name | ||
+ ', 最古のURL: ' + self._oldest_url + '*' + 'で検索しまふ' | |||
) | ) | ||
self._twitter_url_pattern: Pattern[str] = re.compile( | self._twitter_url_pattern: Pattern[str] = re.compile( | ||
'^' + self.TWITTER_URL + self._name + r'/status/\d+') | '^' + self.TWITTER_URL + self._name + r'/status/\d+') | ||
2,009行目: | 1,984行目: | ||
return True | return True | ||
def _input_oldest_id(self) -> str: | |||
def | """探索対象のうち、最古のツイートのIDを入力させる。 | ||
正しい形式のツイートIDまたは空白が入力されるまで繰り返す。 | |||
' | Returns: | ||
str: ツイートIDまたは空文字列。 | |||
""" | |||
while True: | |||
print('ツイートを新しい順に取得するので、収集をストップするツイートIDを入力して下さいナリ') | |||
print('空白だと全ツイートを収集しますを') | |||
print('> ', end='') | |||
oldest_id: str = input() | |||
if re.match(r'^\d*$', oldest_id) is not None: | |||
return oldest_id | |||
else: | |||
print('整数か空白を入力して下さいナリ') | |||
def _get_tweet_urls_from_wiki(self, accessor: AccessorHandler) -> None: | def _get_tweet_urls_from_wiki(self, accessor: AccessorHandler) -> None: | ||
2,047行目: | 2,033行目: | ||
template_soup.select('.wikitable > tbody > tr > td a'))) | template_soup.select('.wikitable > tbody > tr > td a'))) | ||
for url in urls: | for url in urls: | ||
logger.info(f'{unquote(url)} | logger.info(f'{unquote(url)} で収集済みツイートを探索中でふ') | ||
page: Final[str | None] = ( | page: Final[str | None] = ( | ||
accessor.request_with_requests_module(url)) | accessor.request_with_requests_module(url)) | ||
2,086行目: | 2,072行目: | ||
) # 最初にマッチしたURLを返す | ) # 最初にマッチしたURLを返す | ||
if url_matched is not None: | if url_matched is not None: | ||
if url_matched.string < self._oldest_url: | |||
logger.debug(url_matched.string + 'は最古の探索対象よりも古いのでポア') | |||
continue | |||
a_first_child: Final[Tag | None] = tweet.select_one( | a_first_child: Final[Tag | None] = tweet.select_one( | ||
'a:first-child') | 'a:first-child') | ||
2,146行目: | 2,135行目: | ||
accessor: AccessorHandler, | accessor: AccessorHandler, | ||
tweet_url_prefix: str, | tweet_url_prefix: str, | ||
incremented_num: int) -> None: | incremented_num: int, | ||
incremented: bool = False) -> None: | |||
"""ツイートのURLを、数字部分をインクリメントしながら探索する。 | """ツイートのURLを、数字部分をインクリメントしながら探索する。 | ||
2,156行目: | 2,146行目: | ||
tweet_url_prefix (str): ツイートURLの数字部分のうち、インクリメントする桁以前の部分。 | tweet_url_prefix (str): ツイートURLの数字部分のうち、インクリメントする桁以前の部分。 | ||
incremented_num (int): ツイートURLのうちインクリメントする桁の現在の数字。 | incremented_num (int): ツイートURLのうちインクリメントする桁の現在の数字。 | ||
incremented (bool, optional): `incremented_num` がインクリメント済みの場合True。 | |||
Examples: | Examples: | ||
`https://twitter.com/CallinShow/status/1707` | `https://twitter.com/CallinShow/status/1707` で始まるURLから最新まですべて探索する場合 | ||
:: | :: | ||
self._next_url(accessor, '1707', 0) | self._next_url(accessor, '1707', 0) | ||
`https://twitter.com/CallinShow/status/165` | `https://twitter.com/CallinShow/status/165` で始まるURLから最新まですべて探索する場合 | ||
:: | :: | ||
self._next_url(accessor, '16', 5) | self._next_url(accessor, '16', 5) | ||
""" | """ | ||
assert 0 <= incremented_num <= 9, \ | assert 0 <= incremented_num <= 9, \ | ||
2,195行目: | 2,181行目: | ||
page_num: Final[int] = int(page_num_matched[1]) | page_num: Final[int] = int(page_num_matched[1]) | ||
if page_num > 100: # ツイート数が100を超えると途中でreCAPTCHAが入るので、もっと細かく検索 | if page_num > 100: # ツイート数が100を超えると途中でreCAPTCHAが入るので、もっと細かく検索 | ||
self._next_url(accessor, | # ユーザが最初に"1512"と指定した時、tweet_url_prefix="151"ならincremented_numを2から開始したいが、 | ||
# tweet_url_prefix="152"や"160"などならincremented_numを0から開始したい | |||
if incremented: | |||
self._next_url(accessor, | |||
tweet_url_prefix + str(incremented_num), 0, | |||
True) | |||
else: | |||
self._latest_id_digit = self._oldest_id_queue.popleft() | |||
self._oldest_id_queue.append(0) # 空になると予想外の動作を起こしかねないため | |||
self._next_url(accessor, | |||
tweet_url_prefix + str(incremented_num), | |||
self._latest_id_digit) | |||
else: | else: | ||
logger.debug( | logger.debug( | ||
2,213行目: | 2,209行目: | ||
return | return | ||
else: | else: | ||
self._next_url(accessor, tweet_url_prefix, incremented_num + 1) | self._next_url(accessor, | ||
tweet_url_prefix, | |||
incremented_num + 1, | |||
True) | |||
def _parse_images(self, soup: Tag, | def _parse_images(self, soup: Tag, | ||
2,287行目: | 2,286行目: | ||
'{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}') | '{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}') | ||
return tag | return tag | ||
@override | |||
def _get_tweet_poll(self, tweet: Tag) -> str: | |||
card_poll: Tag | None = tweet.select_one('div[data-testid="cardPoll"]') | |||
poll_txt: str = '' | |||
if card_poll is not None: | |||
polls: list[tuple[str, str]] = [] | |||
max_ratio: float = 0. | |||
leader_idx: int = 0 | |||
tweet_lis = card_poll.select('li') | |||
for i, li in enumerate(tweet_lis): | |||
poll_data: ResultSet[Tag] = li.select('div > span') | |||
text_tag = poll_data[0] | |||
ratio_tag = poll_data[1] | |||
ratio: str = ratio_tag.text | |||
float_ratio: float = float(ratio[0:-1]) | |||
if max_ratio < float_ratio: | |||
leader_idx = i | |||
max_ratio = float_ratio | |||
polls.append((text_tag.text, ratio)) | |||
for i, poll_result in enumerate(polls): | |||
if i == leader_idx: | |||
poll_txt += ( | |||
'<br>\n' | |||
' <span style="display: inline-block; ' | |||
'width: 30em; background: linear-gradient(' | |||
'to right, ' | |||
f'rgba(29, 155, 240, 0.58) 0 {poll_result[1]}, ' | |||
f'transparent {poll_result[1]} 100%); ' | |||
'font-weight: bold;">' | |||
) + poll_result[1] + ' ' + poll_result[0] + '</span>' | |||
else: | |||
poll_txt += ( | |||
'<br>\n' | |||
' <span style="display: inline-block; ' | |||
'width: 30em; background: linear-gradient(' | |||
'to right, ' | |||
f'rgb(207, 217, 222) 0 {poll_result[1]}, ' | |||
f'transparent {poll_result[1]} 100%);">' | |||
) + poll_result[1] + ' ' + poll_result[0] + '</span>' | |||
votes_count_tag: Tag | None = card_poll.select_one( | |||
'div[data-testid="cardPoll"] > div') | |||
assert votes_count_tag is not None | |||
poll_txt += '<br>\n <span style="font-size: small;">' \ | |||
+ votes_count_tag.text + '</span>' | |||
return poll_txt | |||
@staticmethod | @staticmethod | ||
2,321行目: | 2,370行目: | ||
self, | self, | ||
url_pairs: list[UrlTuple], | url_pairs: list[UrlTuple], | ||
accessor: AccessorHandler) -> | accessor: AccessorHandler) -> None: | ||
"""魚拓からツイート本文を取得する。 | """魚拓からツイート本文を取得する。 | ||
Args: | Args: | ||
url_pairs (list[UrlTuple]): URLとその魚拓URLのペアのリスト。 | url_pairs (list[UrlTuple]): URLとその魚拓URLのペアのリスト。 | ||
accessor (AccessorHandler): アクセスハンドラ。 | accessor (AccessorHandler): アクセスハンドラ。 | ||
""" | """ | ||
table_builder: Final[TableBuilder] = TableBuilder() | table_builder: Final[TableBuilder] = TableBuilder() | ||
2,345行目: | 2,388行目: | ||
if article is not None and article.select_one( | if article is not None and article.select_one( | ||
'span[data-testid="socialContext"]') is None: | 'span[data-testid="socialContext"]') is None: | ||
logger.debug(url_pair.url + 'を整形しますを') | logger.debug(url_pair.url + 'を整形しますを') | ||
2,403行目: | 2,445行目: | ||
lambda t: f'[[ファイル:{t}|240px]]', image_list)) | lambda t: f'[[ファイル:{t}|240px]]', image_list)) | ||
text = self._concat_texts(text, image_txt) | text = self._concat_texts(text, image_txt) | ||
text = TableBuilder.escape_wiki_reserved_words(text) | |||
# | # 投票の処理 | ||
poll_txt: Final[str] = self._get_tweet_poll(article) | |||
text = self._concat_texts(text, poll_txt) | |||
except Exception as e: | except Exception as e: | ||
2,411行目: | 2,456行目: | ||
text = 'エラーが発生してツイートが取得できませんでした\n' + ''.join( | text = 'エラーが発生してツイートが取得できませんでした\n' + ''.join( | ||
TracebackException.from_exception(e).format()) | TracebackException.from_exception(e).format()) | ||
table_builder.append( | table_builder.append(tweet_callinshow_template, text) | ||
else: | else: | ||
logger.warn(url_pair.url + 'はリツイートなので飛ばすナリ。' | logger.warn(url_pair.url + 'はリツイートなので飛ばすナリ。' | ||
2,420行目: | 2,462行目: | ||
table_builder.dump_file() | table_builder.dump_file() | ||
@override | @override | ||
2,454行目: | 2,495行目: | ||
# 未掲載のツイートのURLを取得する | # 未掲載のツイートのURLを取得する | ||
self._next_url(accessor, | self._next_url(accessor, '', self._latest_id_digit) | ||
logger.debug(f'{len(self._url_list)} tweets are missed') | logger.debug(f'{len(self._url_list)} tweets are missed') | ||
# URL一覧ファイルのダンプ | |||
with codecs.open(self.URL_LIST_FILENAME, 'w', 'utf-8') as f: | |||
for url_pair in self._url_list: | |||
f.write(url_pair.url + '\n') | |||
logger.info('URL一覧手に入ったやで〜') | |||
# ツイート本文を取得する | # ツイート本文を取得する | ||
signal.signal(signal.SIGINT, self._signal_handler) | |||
try: | try: | ||
self._get_tweet_from_archive(self._url_list, accessor) | |||
except Exception: | except Exception: | ||
# エラーが起きたらURLのリストをそのまま返す | # エラーが起きたらURLのリストをそのまま返す | ||
logger.exception('異常が起きたので終了するナリ。' | logger.exception( | ||
'異常が起きたので終了するナリ。' | |||
+ self.URL_LIST_FILENAME + 'を元に自分でツイートを整形してほしいナリ') | |||
2,514行目: | 2,552行目: | ||
== 実行例 == | == 実行例 == | ||
=== | === 2月17日 === | ||
{|class="wikitable" style="text-align: left;" | {|class="wikitable" style="text-align: left;" | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758537053602816220|2=https://archive.vn/vEzcy}} | ||
|- | |- | ||
| | | | ||
オレの投稿に捨て垢で張り付く馬鹿はウケるな。<br> | |||
<br> | <br> | ||
ブロックは楽しみ。 | |||
|- | |||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758540840874778691|2=https://archive.vn/mo5TW}} | |||
|- | |||
| | |||
やりますよ<br> | |||
{{Archive|1=@totonoijinseiのリツイートがあります|2=リツイートがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758542361486094374|2=https://archive.vn/uYdF3}} | ||
|- | |- | ||
| | | | ||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br> | |||
<br> | <br> | ||
ゼロプリ好き<br> | |||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758557889927737554|2=https://archive.vn/2HXQP}} | ||
|- | |- | ||
| | | | ||
逮捕、送検情報を、テレビが報じるときって、警察が広報すると決めた案件なんだ。<br> | |||
その中で被疑者の供述が出ている時は、警察からの情報提供を、記者クラブの記者が、記事にしているんだ。 | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758569972891316469|2=https://archive.vn/opwxx}} | ||
|- | |- | ||
| | | | ||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br> | |||
<br> | <br> | ||
ジャボリーミッキーのお姉さんの権利関係が気になる。<br> | |||
<br> | <br> | ||
ちゃんとお姉さんに収益が落ちて欲しいな。<br> | |||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758570299287843130|2=https://archive.vn/3ZvbE}} | ||
|- | |- | ||
| | | | ||
マ・ドンソク主演<br> | |||
<br> | |||
このシリーズ最高。<br> | |||
{{Archive|1=@hanzaitoshi3のリツイートがあります|2=リツイートがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758692104111542606|2=https://archive.vn/https%3A%2F%2Ftwitter.com%2FCallinShow%2Fstatus%2F1758692104111542606}} | |||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | |||
|- | |- | ||
| | | | ||
<nowiki>#</nowiki>NowReading<br> | |||
[[ファイル:GGgg24Ja0AArevQ.jpg|240px]] | |||
[[ファイル: | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758702560230396318|2=https://archive.vn/Fe1hA}} | ||
|- | |- | ||
| | | | ||
はい!<br> | |||
{{Archive|1=@totonoijinseiのリツイートがあります|2=リツイートがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758721953941291263|2=https://archive.vn/O4bfO}} | ||
|- | |- | ||
| | | | ||
どの株買ったって自分で口外して煽っているのって、馬鹿引っ掛ける手口だよな。<br> | |||
<br> | |||
胡散臭い仮想通貨売ってるときと変わりはない。 | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758723779872870712|2=https://archive.vn/4dmB2}} | ||
|- | |- | ||
| | | | ||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br> | |||
<br> | <br> | ||
有隣堂しか知らない世界<br> | |||
<br> | <br> | ||
{{Archive|1= | ブッコローとか、この世界観天才。<br> | ||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758724192646930697|2=https://archive.vn/YSgQE}} | ||
|- | |- | ||
| | | | ||
弁護士になっただけで逆転できる人生はない。<br> | |||
<br> | <br> | ||
何をしていくかだよ。<br> | |||
{{Archive|1=@bengo4topicsのリツイートがあります|2=リツイートがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758724799046819840|2=https://archive.vn/mGxKa}} | ||
|- | |- | ||
| | | | ||
昔こうだったから今◯◯になった、はい、立志伝<br> | |||
<br> | |||
みたいなのって職業に貴賎がある意識の表れがある気がするからオレは好きではない。 | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758814041370431746|2=https://archive.vn/hrSe6}} | ||
|- | |- | ||
| | | | ||
明日はフェブラリーステークス<br> | |||
<br> | <br> | ||
勝つのは?<br> | |||
<br> | <br> | ||
長岡騎手勝って欲しいな。<br> | |||
<br><br> | |||
<span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgb(207, 217, 222) 0 19.5%, transparent 19.5% 100%);">19.5% オメガギネス</span><br><br> | |||
<span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgb(207, 217, 222) 0 23.6%, transparent 23.6% 100%);">23.6% ウィルソンテソーロ</span><br><br> | |||
<span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgba(29, 155, 240, 0.58) 0 29.3%, transparent 29.3% 100%); font-weight: bold;">29.3% ドゥラエレーデ</span><br><br> | |||
<span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgb(207, 217, 222) 0 27.6%, transparent 27.6% 100%);">27.6% ガイアフォース</span><br><br> | |||
<span style="font-size: small;">123 votes·Final results</span> | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | |} | ||
=== 2月18日 === | |||
{|class="wikitable" style="text-align: left;" | |||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758882444055588957|2=https://archive.vn/7iZ2B}} | |||
|- | |- | ||
| | | | ||
<nowiki>#</nowiki>フェブラリーS<br> | |||
< | <nowiki>#</nowiki>フェブラリーステークス<br> | ||
[[ファイル:GGjOK1OaAAAh0_U.jpg|240px]] | |||
<br> | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758882526012334134|2=https://archive.vn/uZuec}} | ||
|- | |- | ||
| | | | ||
<nowiki>#</nowiki>小倉大賞典<br> | |||
[[ファイル:GGjOTBbbMAAX1du.jpg|240px]] | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759086217491276082|2=https://archive.vn/pFBxj}} | |||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | |||
|- | |- | ||
| | | | ||
藤田会長に電磁波攻撃はあるのか聞いてみたい。<br> | |||
{{Archive|1=@tabbataのリツイートがあります|2=リツイートがあります}} | |||
{{Archive|1= | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759088039216009629|2=https://archive.vn/tBoZM}} | ||
|- | |- | ||
| | | | ||
中島みゆきの夜会、やってるんだな。<br> | |||
<br> | <br> | ||
昔、見て感動した。<br> | |||
<br> | <br> | ||
チケット、リセールで手に入るかな。 | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759205451168973134|2=https://archive.vn/bLTYB}} | ||
|- | |- | ||
| | | | ||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br> | |||
<br> | <br> | ||
オレは今泣いている。<br> | |||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}} | |||
|- | |- | ||
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/ | !{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759205706182590719|2=https://archive.vn/15dN0}} | ||
|- | |- | ||
| | | | ||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br> | |||
<br> | <br> | ||
みんなこれを聞いてくれ。<br> | |||
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}} | |||
|- | |- | ||
|} | |} |