「利用者:夜泣き/スクリプト」の版間の差分

→‎コード: v4.3.0 実行時にツイートIDを指定するように変更して、使いやすくしました。あと投票も整形できるようにしました
>Fet-Fe
(→‎コード: v4.2.0 Nitterが使えなくなるので従来のモードをdeprecatedでマーク)
>Fet-Fe
(→‎コード: v4.3.0 実行時にツイートIDを指定するように変更して、使いやすくしました。あと投票も整形できるようにしました)
11行目: 11行目:
"""Twitter自動収集スクリプト
"""Twitter自動収集スクリプト


ver4.2.0 2024/2/16恒心
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`` オプションを付けたときに使用する設定値。
         """
         """
        tweet_url_prefix_default: Final[str] = '175'
        """Final[str]: ツイートURLの数字部分のうち、予め固定しておく部分。
        URLの数字部分がこの数字で始まるもののみをクロールする。
        Example:
            `tweet_url_prefix_default = 172`、`incremented_num_default = 3` の場合、
            `https://twitter.com/CallinShow/status/1723*` から
            `https://twitter.com/CallinShow/status/1729*` までを魚拓から検索する。
        See Also:
          :const:`~incremented_num_default`
        """
        incremented_num_default: Final[int] = 5
        """Final[int]: ツイートURLの数字部分うち、インクリメントする桁のデフォルト値。
        See Also:
            :const:`~tweet_url_prefix_default`
        """
         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._get_name(accessor)
             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._get_query()
             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._stop_word()
         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 _get_name(self, accessor: AccessorHandler) -> str | None:
     def _input_name(self, accessor: AccessorHandler) -> str | None:
         """ツイート収集するユーザー名を標準入力から取得する。
         """ツイート収集するユーザー名を標準入力から取得する。


1,277行目: 1,257行目:
                     return account_str
                     return account_str


     def _get_query(self) -> None:
     def _input_query(self) -> None:
         """検索クエリを標準入力から取得する。
         """検索クエリを標準入力から取得する。
         """
         """
1,307行目: 1,287行目:
             self._table_builder.dump_file()
             self._table_builder.dump_file()


     def _stop_word(self) -> str:
     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からツイートを収集するため引き続き利用可能。
         """
         """
        def signal_handler(signum: int, frame: FrameType | None) -> NoReturn:
            """ユーザがCtrl + Cでプログラムを止めたときのシグナルハンドラ。
            Args:
                signum (int): シグナル番号。想定されるのはSIGINTのみ。
                frame (FrameType | None): 現在のスタックフレーム。
            """
            logger.info('ユーザがプログラムを中止したなりを')
            self._table_builder.dump_file()
            sys.exit()


         # Seleniumドライバーを必ず終了するため、with文を利用する。
         # Seleniumドライバーを必ず終了するため、with文を利用する。
1,909行目: 1,891行目:


             # ツイートを取得し終えるまでループ
             # ツイートを取得し終えるまでループ
             signal.signal(signal.SIGINT, signal_handler)
             signal.signal(signal.SIGINT, self._signal_handler)
             try:
             try:
                 while True:
                 while True:
1,945行目: 1,927行目:
     Todo:
     Todo:
         * ちゃんとテストする。
         * ちゃんとテストする。
    """
    TWEET_URL_PREFIX_DEFAULT: Final[str] = \
        UserProperties.ArchiveCrawler.tweet_url_prefix_default
    """Final[str]: ツイートURLの数字部分のうち、予め固定しておく部分。
    :func:`~_next_url` の `tweet_url_prefix` のデフォルト値。
    """
    INCREMENTED_NUM_DEFAULT: Final[int] = \
        UserProperties.ArchiveCrawler.incremented_num_default
    """Final[int]: ツイートURLの数字部分うち、インクリメントする桁のデフォルト値。
    :const:`~TWEET_URL_PREFIX_DEFAULT` に続く桁をこの数字からインクリメントする。
    0から9の間の整数でなければならない。
    :func:`~_next_url` の `incremented_num` のデフォルト値。
     """
     """


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._get_name(accessor)
             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


    @override
     def _input_oldest_id(self) -> str:
     def _check_constants(self) -> None:
        """探索対象のうち、最古のツイートのIDを入力させる。
         super()._check_constants()
 
        assert (isinstance(self.INCREMENTED_NUM_DEFAULT, int)
        正しい形式のツイートIDまたは空白が入力されるまで繰り返す。
                 and 0 <= self.INCREMENTED_NUM_DEFAULT <= 9), \
 
             'INCREMENTED_NUM_DEFAULTは0から9の整数のみでふ'
        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` で始まるURLをすべて探索する場合
             `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` で始まるURLから
             `https://twitter.com/CallinShow/status/165` で始まるURLから最新まですべて探索する場合
            `https://twitter.com/CallinShow/status/169` で始まるURLまでをすべて探索する場合
             ::
             ::


               self._next_url(accessor, '16', 5)
               self._next_url(accessor, '16', 5)
        See Also:
            * :const:`~TWEET_URL_PREFIX_DEFAULT`: `tweet_url_prefix` のデフォルト値。
            * :const:`~INCREMENTED_NUM_DEFAULT`: `incremented_num` のデフォルト値。
         """
         """
         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 + str(incremented_num), 0)
                # 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'
                        '&nbsp; <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'
                        '&nbsp; <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&nbsp; <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) -> tuple[str, ...]:
             accessor: AccessorHandler) -> None:
         """魚拓からツイート本文を取得する。
         """魚拓からツイート本文を取得する。
        リツイートが `url_pairs` に残っていたら除く。


         Args:
         Args:
             url_pairs (list[UrlTuple]): URLとその魚拓URLのペアのリスト。
             url_pairs (list[UrlTuple]): URLとその魚拓URLのペアのリスト。
             accessor (AccessorHandler): アクセスハンドラ。
             accessor (AccessorHandler): アクセスハンドラ。
        Returns:
            tuple[str]: リツイートを除いたURLのタプル。
         """
         """
        filtered_urls: Final[list[str]] = []
         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:
                filtered_urls.append(url_pair.url)


                 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)


                     # TODO: 投票の処理
                     # 投票の処理
                    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)
                    tweet_callinshow_template,
                    TableBuilder.escape_wiki_reserved_words(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()
        return tuple(filtered_urls)


     @override
     @override
2,454行目: 2,495行目:


             # 未掲載のツイートのURLを取得する
             # 未掲載のツイートのURLを取得する
             self._next_url(accessor,
             self._next_url(accessor, '', self._latest_id_digit)
                          self.TWEET_URL_PREFIX_DEFAULT,
                          self.INCREMENTED_NUM_DEFAULT)
             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:
                 filtered_url_tuple: tuple[str, ...] = (
                 self._get_tweet_from_archive(self._url_list, accessor)
                    self._get_tweet_from_archive(self._url_list, accessor))
             except Exception:
             except Exception:
                 # エラーが起きたらURLのリストをそのまま返す
                 # エラーが起きたらURLのリストをそのまま返す
                 logger.exception('異常が起きたので終了するナリ。'
                 logger.exception(
                                'リツイートを含むURLのリストを返すので、それを元に自分でツイートを整形してほしいナリ')
                    '異常が起きたので終了するナリ。'
                filtered_url_tuple: tuple[str, ...] = tuple(
                     + self.URL_LIST_FILENAME + 'を元に自分でツイートを整形してほしいナリ')
                     map(lambda url_pair: url_pair.url, self._url_list))
 
            # URL一覧ファイルのダンプ
            with codecs.open(self.URL_LIST_FILENAME, 'w', 'utf-8') as f:
                for url in filtered_url_tuple:
                    f.write(url + '\n')
            logger.info('テキストファイル手に入ったやで〜')




2,514行目: 2,552行目:


== 実行例 ==
== 実行例 ==
20件での実行例。


=== 12月10日 ===
=== 2月17日 ===
{|class="wikitable" style="text-align: left;"
{|class="wikitable" style="text-align: left;"
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601539154256744449|2=https://archive.md/WjoU1}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758537053602816220|2=https://archive.vn/vEzcy}}
|-
|-
|
|
なお、ひめかファンのワタクシ、ブロックされております。<br>
オレの投稿に捨て垢で張り付く馬鹿はウケるな。<br>
<br>
<br>
ひめかちゃんは整形なんかじゃない。<br>
ブロックは楽しみ。
[[ファイル:FjnPLyyakAE4sD0.jpg|240px]]
|-
!{{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/1601542511302160384|2=https://archive.md/Fj5Fz}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758542361486094374|2=https://archive.vn/uYdF3}}
|-
|-
|
|
今日もお疲れ。<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br>
<br>
<br>
また明日な。<br>
ゼロプリ好き<br>
[[ファイル:FjnSWDPaMAA5QZM.jpg|240px]] [[ファイル:FjnSWDWakAAWrJa.jpg|240px]] [[ファイル:FjnSWDOakAAIcfE.jpg|240px]]
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601569138379718656|2=https://archive.md/iFdQ6}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758557889927737554|2=https://archive.vn/2HXQP}}
|-
|-
|
|
{{Archive|1=https://youtu.be/QR6Gj0MKcew|2=https://archive.md/KnShQ|3=https://youtu.be/QR6Gj0MKcew}}<br>
逮捕、送検情報を、テレビが報じるときって、警察が広報すると決めた案件なんだ。<br>
<br>
その中で被疑者の供述が出ている時は、警察からの情報提供を、記者クラブの記者が、記事にしているんだ。
直生人へ。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601572951463428096|2=https://archive.md/E5gO5}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758569972891316469|2=https://archive.vn/opwxx}}
|-
|-
|
|
菊地翔<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br>
<br>
エクシアポケモン図鑑名:キングオブポンジひめに夢中。キングオブポンジキャバ中毒の最終形。<br>
<br>
<br>
一人称:オレ。<br>
ジャボリーミッキーのお姉さんの権利関係が気になる。<br>
<br>
<br>
得意技:インスタライブでおらつく。<br>
ちゃんとお姉さんに収益が落ちて欲しいな。<br>
   ひめかに会いにいく。<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}
   逃げる。<br>
   ちょび髭を生やす。<br>
   写真を加工する。<br>
   アルファロメオに乗り換える。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601588268487041024|2=https://archive.md/5hWmc}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758570299287843130|2=https://archive.vn/3ZvbE}}
|-
|-
|
|
日曜阪神11R 阪神JF<br>
マ・ドンソク主演<br>
◎ブトンドール 4.5<br>
<br>
◯ミスヨコハマ 5<br>
このシリーズ最高。<br>
▲リバーラ 4.5<br>
{{Archive|1=@hanzaitoshi3のリツイートがあります|2=リツイートがあります}}
△リバティアイランド 4<br>
△アロマディオーサ 3.75<br>
△ラヴェル 3.5<br>
△ウインブライル 2.5<br>
△ドゥーラ 1.5<br>
△キタウイング 1.5
|-
|-
|}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758692104111542606|2=https://archive.vn/https%3A%2F%2Ftwitter.com%2FCallinShow%2Fstatus%2F1758692104111542606}}
 
=== 12月11日 ===
{|class="wikitable" style="text-align: left;"
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601859263928074240|2=https://archive.md/uLabW}}
|-
|-
|
|
とんでもない目にあったが、香港マイルで復活した。<br>
<nowiki>#</nowiki>NowReading<br>
{{Archive|1=https://twitter.com/CallinShow/status/1601588268487041024|2=https://archive.md/5hWmc|3=https://twitter.com/CallinShow/status/1601588268487041024}}<br>
[[ファイル:GGgg24Ja0AArevQ.jpg|240px]]
[[ファイル:FjrygSpVQAEdzwR.jpg|240px]]
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601870854669094912|2=https://archive.md/ZVWEm}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758702560230396318|2=https://archive.vn/Fe1hA}}
|-
|-
|
|
今日は日曜日だ。エクシア合同会社のことについてツイートするのはやめようかと思ったんだ。<br>
はい!<br>
<br>
{{Archive|1=@totonoijinseiのリツイートがあります|2=リツイートがあります}}
みんなどう思う?<br>
<br>
&nbsp; <span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgba(29, 155, 240, 0.58) 0 56%, transparent 56% 100%); font-weight: bold;">56% 菊地翔は詐欺師だからツイートして欲しい。</span><br>
&nbsp; <span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgb(207, 217, 222) 0 16%, transparent 16% 100%);">16% 関戸直生人が逃げているからツイートして欲しい。</span><br>
&nbsp; <span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgb(207, 217, 222) 0  5%, transparent  5% 100%);"> 5% 高橋佑佳は、いつも菊地の横にいるからツイートしろ。</span><br>
&nbsp; <span style="display: inline-block; width: 30em; background: linear-gradient(to right, rgb(207, 217, 222) 0 23%, transparent 23% 100%);">23% ひめかさんへの応援メッセージお願いします。</span><br>
&nbsp; <span style="font-size: small;">177 votes • Final results</span>
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601888790489886720|2=https://archive.md/cz02s}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758721953941291263|2=https://archive.vn/O4bfO}}
|-
|-
|
|
ジョーカー菊地<br>
どの株買ったって自分で口外して煽っているのって、馬鹿引っ掛ける手口だよな。<br>
[[ファイル:FjsNVgTUAAAeZJu.jpg|240px]]
<br>
胡散臭い仮想通貨売ってるときと変わりはない。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601889110507278339|2=https://archive.md/Ux2Mj}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758723779872870712|2=https://archive.vn/4dmB2}}
|-
|-
|
|
た、た、大変だーーーーー<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br>
<br>
ひめかさんの今年の売り上げ目標10億<br>
<br>
言っちゃうんだな。<br>
<br>
そこだよ。<br>
<br>
いいところ。<br>
<br>
<br>
今それ言えちゃう、鬼メンタル。<br>
有隣堂しか知らない世界<br>
<br>
<br>
{{Archive|1=https://news.yahoo.co.jp/articles/7d3609cb0e6c284d5fae6301f35bf2f27573ef35?page=1|2=https://archive.md/xGrou|3=https://news.yahoo.co.jp/articles/7d3609cb0e6c284d5fae6301f35bf2f27573ef35?page=1}}
ブッコローとか、この世界観天才。<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601890607848296448|2=https://archive.md/GPcmz}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758724192646930697|2=https://archive.vn/YSgQE}}
|-
|-
|
|
{{Archive|1=https://note.com/takahirokarasawa/n/na966edc6e6be|2=https://archive.md/XTXdU|3=https://note.com/takahirokarasawa/n/na966edc6e6be}}<br>
弁護士になっただけで逆転できる人生はない。<br>
<br>
<br>
エクシア合同会社は改めまして詐欺会社です。
何をしていくかだよ。<br>
{{Archive|1=@bengo4topicsのリツイートがあります|2=リツイートがあります}}
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601904473906982913|2=https://archive.md/FMbN1}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758724799046819840|2=https://archive.vn/mGxKa}}
|-
|-
|
|
いつも明るい菊地。<br>
昔こうだったから今◯◯になった、はい、立志伝<br>
[[ファイル:1601904473906982913_0.mp4|240px]]
<br>
みたいなのって職業に貴賎がある意識の表れがある気がするからオレは好きではない。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601915902538051584|2=https://archive.md/y4Fj1}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758814041370431746|2=https://archive.vn/hrSe6}}
|-
|-
|
|
依頼者から送られてきたけど、何の情報か教えて欲しいな。<br>
明日はフェブラリーステークス<br>
<br>
<br>
エクシアの関係者なんでしょ。<br>
勝つのは?<br>
[[ファイル:FjslywSUAAASvCt.jpg|240px]]
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601916198425309185|2=https://archive.md/CcEl6}}
|-
|
発信している情報は全部裏を取ってるんだよね。<br>
{{Archive|1=https://twitter.com/CallinShow/status/1601915902538051584|2=https://archive.md/y4Fj1|3=https://twitter.com/CallinShow/status/1601915902538051584}}
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601916490587901952|2=https://archive.md/FkDob}}
|-
|
いくつか大きなネタはあるんだけど、今はあたためてる。<br>
<br>
<br>
この事件は本当色んなことがあるんだ。
長岡騎手勝って欲しいな。<br>
<br><br>
&nbsp; <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>
&nbsp; <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>
&nbsp; <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>
&nbsp; <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>
&nbsp; <span style="font-size: small;">123 votes·Final results</span>
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601949819404066816|2=https://archive.md/5vbUl}}
|}
 
=== 2月18日 ===
{|class="wikitable" style="text-align: left;"
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758882444055588957|2=https://archive.vn/7iZ2B}}
|-
|-
|
|
菊地翔が夢に出てきた。<br>
<nowiki>#</nowiki>フェブラリーS<br>
<br>
<nowiki>#</nowiki>フェブラリーステークス<br>
オレも末期だな。<br>
[[ファイル:GGjOK1OaAAAh0_U.jpg|240px]]
<br>
なぜか、スナックで一緒にカラオケを歌ってた。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601954620204797953|2=https://archive.md/oJIT3}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1758882526012334134|2=https://archive.vn/uZuec}}
|-
|-
|
|
3枚目と4枚目は微妙に口の開き方が違う。3枚目は「ね」、4枚目は「い」を発音しているときの口の開き方だ。<br>
<nowiki>#</nowiki>小倉大賞典<br>
{{Archive|1=https://twitter.com/morumokoko/status/1601951502200774656|2=https://archive.md/Zgidz|3=https://twitter.com/morumokoko/status/1601951502200774656}}
[[ファイル:GGjOTBbbMAAX1du.jpg|240px]]
|-
|-
|}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759086217491276082|2=https://archive.vn/pFBxj}}
 
=== 12月12日 ===
{|class="wikitable" style="text-align: left;"
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601955353792430081|2=https://archive.md/UVKrV}}
|-
|-
|
|
エクシア合同会社のツイートの中に、岡ちゃんのツイートをリツイートしてしまってごめん。<br>
藤田会長に電磁波攻撃はあるのか聞いてみたい。<br>
<br>
{{Archive|1=@tabbataのリツイートがあります|2=リツイートがあります}}
本当に興味あるのは菊地翔じゃなくて岡ちゃんなんだ。<br>
{{Archive|1=https://twitter.com/CallinShow/status/1601954620204797953|2=https://archive.md/oJIT3|3=https://twitter.com/CallinShow/status/1601954620204797953}}
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601958600259371011|2=https://archive.md/EY6da}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759088039216009629|2=https://archive.vn/tBoZM}}
|-
|-
|
|
日曜日に誰もエクシア合同会社のことなんか、呟きたいなんて思わないのが普通だと思う。<br>
中島みゆきの夜会、やってるんだな。<br>
<br>
<br>
でも、エクシア合同会社が詐欺会社であり、皆んな許せないと思ってやってるんだ。<br>
昔、見て感動した。<br>
<br>
<br>
エクシア合同会社を許してはいけない、そんな気持ちで我々会ったことのない大人達はつながっているんだ。
チケット、リセールで手に入るかな。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601959896252981249|2=https://archive.md/Jid3z}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759205451168973134|2=https://archive.vn/bLTYB}}
|-
|-
|
|
ときにはふざけたツイートをしているように思うかもしれない。<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br>
<br>
でも、本気なんだよ。<br>
<br>
<br>
1人でも多くの人に知ってもらって、その声が大きくなって、これが社会問題化して、ちゃんと法の裁きを受ける。<br>
オレは今泣いている。<br>
<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}
その一心でいつもTwitterに臨んでいる。
|-
|-
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1601961096528613376|2=https://archive.md/V8jbu}}
!{{CallinShowLink|1=https://twitter.com/CallinShow/status/1759205706182590719|2=https://archive.vn/15dN0}}
|-
|-
|
|
エクシア合同会社と闘う大人たちがやっていることは、Twitterを使った世直し運動なんだ。<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}<br>
<br>
みんな正業があり、それぞれの生活の中で調べ、問題点を指摘し続けている。<br>
<br>
政治はまだこの問題を見過ごしているけれど、一万人700億円の話。<br>
<br>
これは人々が苦しんでいる話。<br>
<br>
<br>
だから皆んな立ち上がっている。
みんなこれを聞いてくれ。<br>
{{Archive|1=リンクがあります。重複に注意して下さい|2=リンクがあります}}
|-
|-
|}
|}
匿名利用者