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

→‎コード: v4.4.0 -u --krswを指定した時にも上限以内で引っかかった書き込みがあると自動停止する機能を追加
>Fet-Fe
(→‎コード: v4.3.8 動画の取得ロジックを修正)
>Fet-Fe
(→‎コード: v4.4.0 -u --krswを指定した時にも上限以内で引っかかった書き込みがあると自動停止する機能を追加)
11行目: 11行目:
"""Twitter自動収集スクリプト
"""Twitter自動収集スクリプト


ver4.3.8 2024/8/30恒心
ver4.4.0 2024/9/4恒心


当コードは恒心停止してしまった https://rentry.co/7298g の降臨ショーツイート自動収集スクリプトの復刻改善版です。
当コードは恒心停止してしまった https://rentry.co/7298g の降臨ショーツイート自動収集スクリプトの復刻改善版です。
250行目: 250行目:


     def __init__(self) -> None:
     def __init__(self) -> None:
        """コンストラクタ。"""
         # Torに必要なプロキシをセット
         # Torに必要なプロキシをセット
         self._cookies: dict[str, str] = {}
         self._cookies: dict[str, str] = {}
362行目: 361行目:
     @property
     @property
     def proxies(self) -> dict[str, str] | None:
     def proxies(self) -> dict[str, str] | None:
         """オブジェクトのプロキシ設定を返す。
         """dict[str, str] | None: プロキシ設定。"""
 
        Returns:
            dict[str, str] | None: プロキシ設定。
        """
         return self._proxies
         return self._proxies




class SeleniumAccessor(AbstractAccessor):
class SeleniumAccessor(AbstractAccessor):
     """SeleniumでWebサイトに接続するためのクラス。"""
     """SeleniumでWebサイトに接続するためのクラス。
 
    Args:
        enable_javascript (bool): JavaScriptを有効にするかどうか。reCAPTCHA対策に必要。
    """


     TOR_BROWSER_PATHS: Final[MappingProxyType[str, str]] = MappingProxyType({
     TOR_BROWSER_PATHS: Final[MappingProxyType[str, str]] = MappingProxyType({
387行目: 386行目:


     def __init__(self, enable_javascript: bool) -> None:
     def __init__(self, enable_javascript: bool) -> None:
        """コンストラクタ。
        Tor Browserを自動操縦するためのSeleniumドライバを初期化する。
        Args:
            enable_javascript (bool): JavaScriptを有効にするかどうか。reCAPTCHA対策に必要。
        """
         self._options: Final[FirefoxOptions] = FirefoxOptions()
         self._options: Final[FirefoxOptions] = FirefoxOptions()
         self._options.binary_location = (
         self._options.binary_location = (
521行目: 513行目:


     RequestsとSeleniumのどちらかを選択して使用することができ、その違いを隠蔽する。
     RequestsとSeleniumのどちらかを選択して使用することができ、その違いを隠蔽する。
    Args:
        use_browser (bool): `True` ならSeleniumを利用する。`False` ならRequestsのみでアクセスする。
        enable_javascript (bool): SeleniumでJavaScriptを利用する場合は `True`。
     """
     """


530行目: 526行目:


     def __init__(self, use_browser: bool, enable_javascript: bool) -> None:
     def __init__(self, use_browser: bool, enable_javascript: bool) -> None:
        """コンストラクタ。
        Requestのみを利用するか、Seleniumも利用するか引数で選択して初期化する。
        Args:
            use_browser (bool): `True` ならSeleniumを利用する。\
                `False` ならRequestsのみでアクセスする。
            enable_javascript (bool): SeleniumでJavaScriptを利用する場合は `True`。
        """
         self._selenium_accessor: Final[SeleniumAccessor | None] = (
         self._selenium_accessor: Final[SeleniumAccessor | None] = (
             SeleniumAccessor(enable_javascript) if use_browser else None
             SeleniumAccessor(enable_javascript) if use_browser else None
546行目: 533行目:


     def __enter__(self) -> Self:
     def __enter__(self) -> Self:
        """`with` ブロックの開始時に実行する。
        Returns:
            Self: オブジェクト自身。
        """
         return self
         return self


557行目: 539行目:
                 exc_value: BaseException | None,
                 exc_value: BaseException | None,
                 traceback: TracebackType | None) -> None:
                 traceback: TracebackType | None) -> None:
        """`with` ブロックの終了時に実行する。
        Args:
            exc_type (type[BaseException] | None): コンテキスト内で例外を吐いた場合の例外タイプ。
            exc_value (BaseException | None): コンテキスト内で例外を吐いた場合の例外。
            traceback (TracebackType | None): コンテキスト内で例外を吐いた場合のトレースバック。
        """
         if self._selenium_accessor is not None:
         if self._selenium_accessor is not None:
             self._selenium_accessor.quit()
             self._selenium_accessor.quit()
688行目: 663行目:
     @property
     @property
     def last_url(self) -> str:
     def last_url(self) -> str:
         """最後にアクセスしたURLを返す。
         """str: 最後にアクセスしたURL。"""
 
        Returns:
            str: 最後にアクセスしたURL。
        """
         return self._last_url
         return self._last_url


     @property
     @property
     def proxies(self) -> dict[str, str] | None:
     def proxies(self) -> dict[str, str] | None:
         """RequestsAccessorオブジェクトのプロキシ設定を返す。
         """dict[str, str] | None: RequestsAccessorオブジェクトのプロキシ設定。"""
 
        Returns:
            dict[str, str] | None: RequestsAccessorオブジェクトのプロキシ設定。
        """
         return self._requests_accessor.proxies
         return self._requests_accessor.proxies




class TableBuilder:
class TableBuilder:
     """Wikiの表を組み立てるためのクラス。"""
     """Wikiの表を組み立てるためのクラス。


    Args:
        date (datetime | None, optional): 記録するツイートの最新日付。デフォルトは今日の日付。
    """
     FILENAME: Final[str] = UserProperties.filename
     FILENAME: Final[str] = UserProperties.filename
     """Final[str]: ツイートを保存するファイルの名前。"""
     """Final[str]: ツイートを保存するファイルの名前。"""


     def __init__(self, date: datetime | None = None) -> None:
     def __init__(self, date: datetime | None = None) -> None:
        """コンストラクタ。
        Args:
            date (datetime | None, optional): 記録するツイートの最新日付。デフォルトは今日の日付。
        """
         self._tables: Final[list[str]] = ['']
         self._tables: Final[list[str]] = ['']
         self._count: int = 0  # 記録数
         self._count: int = 0  # 記録数
723行目: 688行目:
     @property
     @property
     def count(self) -> int:
     def count(self) -> int:
         """表に追加したツイートの件数を返す。
         """int: 表に追加したツイートの件数。"""
 
        Returns:
            int: 表に追加したツイートの件数。
        """
         return self._count
         return self._count


1,013行目: 974行目:


     def __init__(self) -> None:
     def __init__(self) -> None:
        """コンストラクタ。"""
         self._check_constants()  # スラッシュが抜けてないかチェック
         self._check_constants()  # スラッシュが抜けてないかチェック
         self._has_ffmpeg: Final[bool] = self._check_ffmpeg()  # ffmpegがあるかチェック
         self._has_ffmpeg: Final[bool] = self._check_ffmpeg()  # ffmpegがあるかチェック
1,023行目: 983行目:
         self._url_query_pattern: Final[re.Pattern[str]] = re.compile(r'\?.*$')
         self._url_query_pattern: Final[re.Pattern[str]] = re.compile(r'\?.*$')


     def _set_queries(self, accessor: AccessorHandler, krsw: bool) -> bool:
     def _set_queries(
            self, accessor: AccessorHandler, krsw: str | None) -> bool:
         """検索条件を設定する。
         """検索条件を設定する。


1,031行目: 992行目:
         Args:
         Args:
             accessor (AccessorHandler): アクセスハンドラ
             accessor (AccessorHandler): アクセスハンドラ
             krsw (bool): Trueの場合、名前が :const:`~CALLINSHOW` になり、\
             krsw (str | None): `None` でない場合、名前が :const:`~CALLINSHOW` になり、\
                 クエリと終わりにするツイートが無しになる。
                 クエリと終わりにするツイートが無しになる。\
                更に空文字でもない場合、この引数が終わりにするツイートになる。


         Returns:
         Returns:
1,039行目: 1,001行目:


         # ユーザー名取得
         # ユーザー名取得
         if krsw:
         if krsw is not None:
             logger.info('名前は自動的に' + self.CALLINSHOW + 'にナリます')
             logger.info('名前は自動的に' + self.CALLINSHOW + 'にナリます')
             self._name: str = self.CALLINSHOW
             self._name: str = self.CALLINSHOW
1,051行目: 1,013行目:
         # 検索クエリとページ取得
         # 検索クエリとページ取得
         self._query_strs: list[str] = []
         self._query_strs: list[str] = []
         if krsw:
         if krsw is not None:
             logger.info('クエリは自動的になしにナリます')
             logger.info('クエリは自動的になしにナリます')
         else:
         else:
1,072行目: 1,034行目:


         # 終わりにするツイート取得
         # 終わりにするツイート取得
         if krsw:
         if krsw == '':
             logger.info('終わりにするツイートは自動的になしにナリます')
             logger.info('終わりにするツイートは自動的になしにナリます')
        self._stop: str = '' if krsw else self._input_stop_word()
            self._stop: str = ''
        elif krsw is not None:
            logger.info(f'終わりにするツイートは自動的に"{krsw}"にナリます')
            self._stop: str = krsw
        else:
            self._stop: str = self._input_stop_word()


         logger.info(
         logger.info(
1,855行目: 1,822行目:
                 'See also: https://nitter.cz'
                 'See also: https://nitter.cz'
                 '\033[0m')
                 '\033[0m')
     def execute(self, krsw: bool = False, use_browser: bool = True,
     def execute(self, krsw: str | None = None, use_browser: bool = True,
                 enable_javascript: bool = True) -> None:
                 enable_javascript: bool = True) -> None:
         """通信が必要な部分のロジック。
         """通信が必要な部分のロジック。


         Args:
         Args:
             krsw (bool, optional): `True` の場合、名前が自動で :const:`~CALLINSHOW` になり、\
             krsw (str | None, optional): `None` でない場合、名前が自動で \
                 クエリと終わりにするツイートが自動で無しになる。
                :const:`~CALLINSHOW` になり、クエリと終わりにするツイートが自動で無しになる。\
                 更に空文字でもない場合、この引数が終わりにするツイートになる。
             use_browser (bool, optional): `True` ならSeleniumを利用する。\
             use_browser (bool, optional): `True` ならSeleniumを利用する。\
                 `False` ならRequestsのみでアクセスする。
                 `False` ならRequestsのみでアクセスする。
1,932行目: 1,900行目:


     @override
     @override
     def _set_queries(self, accessor: AccessorHandler, krsw: bool) -> bool:
     def _set_queries(
            self, accessor: AccessorHandler, krsw: str | None) -> bool:
         """検索条件を設定する。
         """検索条件を設定する。


1,940行目: 1,909行目:
         Args:
         Args:
             accessor (AccessorHandler): アクセスハンドラ
             accessor (AccessorHandler): アクセスハンドラ
             krsw (bool): `True` の場合、名前が :const:`~CALLINSHOW` になる。
             krsw (str | None): `None` でない場合、名前が :const:`~CALLINSHOW` になる。


         Returns:
         Returns:
1,947行目: 1,916行目:


         # ユーザー名取得
         # ユーザー名取得
         if krsw:
         if krsw is not None:
             logger.info('名前は自動的に' + self.CALLINSHOW + 'にナリます')
             logger.info('名前は自動的に' + self.CALLINSHOW + 'にナリます')
             self._name: str = self.CALLINSHOW
             self._name: str = self.CALLINSHOW
2,473行目: 2,442行目:


     @override
     @override
     def execute(self, krsw: bool = False, use_browser: bool = True,
     def execute(self, krsw: str | None = None, use_browser: bool = True,
                 enable_javascript: bool = True) -> None:
                 enable_javascript: bool = True) -> None:
         """通信が必要な部分のロジック。
         """通信が必要な部分のロジック。


         Args:
         Args:
             krsw (bool, optional): `True` の場合、名前が自動で :const:`~CALLINSHOW` になり、\
             krsw (str | None, optional): `None` でない場合、名前が自動で \
                クエリと終わりにするツイートが自動で無しになる。
                :const:`~CALLINSHOW` になる。
             use_browser (bool, optional): `True` ならSeleniumを利用する。\
             use_browser (bool, optional): `True` ならSeleniumを利用する。\
                 `False` ならRequestsのみでアクセスする。
                 `False` ならRequestsのみでアクセスする。
2,531行目: 2,500行目:
     parser.add_argument(
     parser.add_argument(
         '--krsw',
         '--krsw',
         action='store_true',
         type=str,
         help='指定すると、パカデブのツイートを取得上限数まで取得する。')
        nargs='?',
        const='',
        default=None,
         help=('指定すると、パカデブのツイートを取得上限数まで取得する。'
              '更に--search-unarchivedモードでこのオプションに引数を与えると、'
              'その文言が終わりにするツイートになる。'))
     parser.add_argument(
     parser.add_argument(
         '-n',
         '-n',
匿名利用者