{"id":19088411,"url":"https://github.com/tsukumijima/tweepy-authlib","last_synced_at":"2025-04-06T23:18:14.901Z","repository":{"id":65709446,"uuid":"597859938","full_name":"tsukumijima/tweepy-authlib","owner":"tsukumijima","description":"Twitter Web App (Web 版公式クライアント) の内部 API を使い、Tweepy でスクリーンネームとパスワードで認証するためのライブラリ","archived":false,"fork":false,"pushed_at":"2024-04-30T19:33:52.000Z","size":83,"stargazers_count":160,"open_issues_count":1,"forks_count":11,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-05-12T00:22:02.360Z","etag":null,"topics":["api","python","reverse-engineering","tweepy","twitter"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/tweepy-authlib/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsukumijima.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"License.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-05T20:58:27.000Z","updated_at":"2024-05-17T23:20:05.820Z","dependencies_parsed_at":"2024-05-17T23:20:04.234Z","dependency_job_id":"31c79468-7132-4c73-9dce-d1f76c14debe","html_url":"https://github.com/tsukumijima/tweepy-authlib","commit_stats":{"total_commits":14,"total_committers":1,"mean_commits":14.0,"dds":0.0,"last_synced_commit":"656fe241fd81ae870468b5328662b72e17cf8357"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsukumijima%2Ftweepy-authlib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsukumijima%2Ftweepy-authlib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsukumijima%2Ftweepy-authlib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsukumijima%2Ftweepy-authlib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsukumijima","download_url":"https://codeload.github.com/tsukumijima/tweepy-authlib/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247563942,"owners_count":20958971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","python","reverse-engineering","tweepy","twitter"],"created_at":"2024-11-09T03:01:09.309Z","updated_at":"2025-04-06T23:18:14.883Z","avatar_url":"https://github.com/tsukumijima.png","language":"Python","funding_links":[],"categories":["package","Python"],"sub_categories":[],"readme":"\n# tweepy-authlib\n\n[![PyPI - Version](https://img.shields.io/pypi/v/tweepy-authlib.svg)](https://pypi.org/project/tweepy-authlib)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tweepy-authlib.svg)](https://pypi.org/project/tweepy-authlib)\n\n\u003e [!WARNING]  \n\u003e **旧 TweetDeck の完全廃止にともない、2023/09/14 頃から内部的に残存していた Twitter API v1.1 の段階的なシャットダウンが開始されています。**  \n\u003e **2024/04/30 時点では、下記 API が既に廃止されています ([参考](https://github.com/dimdenGD/OldTweetDeck/blob/main/src/interception.js)) 。**  \n\u003e 現時点では 2023/09 にサーバー負荷が高い API が一括廃止されて以降の動きはありません。ただし、リストにない API も既に廃止されている可能性があります。\n\u003e - `search/tweets` : ツイート検索\n\u003e - `search/universal` : ツイート検索 (旧 TweetDeck 独自 API)\n\u003e - `statuses/update` : ツイート投稿\n\u003e - `statuses/retweet/:id` : リツイート\n\u003e - `statuses/unretweet/:id` : リツイート取り消し\n\u003e - `statuses/show/:id` : ツイート詳細\n\u003e - `statuses/destroy/:id` : ツイート削除\n\u003e - `statuses/user_timeline` : ユーザータイムライン\n\u003e - `users/search` : ユーザー検索\n\u003e\n\u003e **現在 tweepy-authlib を利用して上記機能を実装するには、別途 GraphQL API (Twitter Web App の内部 API) クライアントを自作する必要があります。**  \n\u003e 私が [KonomiTV](https://github.com/tsukumijima/KonomiTV) 向けに開発した GraphQL API クライアントの実装が [こちら](https://github.com/tsukumijima/KonomiTV/blob/master/server/app/utils/TwitterGraphQLAPI.py) ([使用例](https://github.com/tsukumijima/KonomiTV/blob/master/server/app/routers/TwitterRouter.py)) にありますので、参考になれば幸いです。  \n\u003e また現時点で廃止されていない API を利用したサンプルコードが [example_json.py](example_json.py) と [example_pickle.py](example_pickle.py) にありますので、そちらもご一読ください。\n\n\u003e [!NOTE]  \n\u003e **tweepy-authlib v1.4.1 以降では、より厳密に Twitter Web App からの HTTP リクエストに偽装したり、一部の Twitter API v1.1 に再びアクセスできるようになるなど、様々な改善が行われています！**  \n\u003e 凍結やアカウントロックのリスクを下げるためにも、最新版の tweepy-authlib の利用をおすすめします。\n\n\u003e [!IMPORTANT]  \n\u003e **tweepy-authlib v1.5.0 にて、twitter.com の x.com への移行に対応しました。**  \n\u003e 2024/05/18 時点では twitter.com のままでも API にアクセスできますが、不審がられるリスクが上がるうえ、いつまでアクセスできるかも不透明なためです。  \n\u003e これにより、`CookieSessionUserHandler.get_graphql_api_headers()` で返される Origin / Referer ヘッダーの値が `twitter.com` から `x.com` に変更されています。  \n\u003e GraphQL API クライアントを自作されている場合は、更新と同時に GraphQL API クライアントのアクセス先 URL を `twitter.com/i/api/graphql` から `x.com/i/api/graphql` に変更することを推奨します。\n\n\u003e [!IMPORTANT]  \n\u003e 2024/05/18 時点では [tweepy-authlib が依存する js2py が Python 3.12 に対応していない](https://github.com/tsukumijima/tweepy-authlib/issues/5) ため、tweepy-authlib は Python 3.12 以降では動作しません。  \n\u003e [js2py](https://github.com/PiotrDabkowski/Js2Py) の Python 3.12 対応が完了するまで、Python 3.11 以下での利用をおすすめします。\n\n-----\n\n**Table of Contents**\n\n- [tweepy-authlib](#tweepy-authlib)\n  - [Description](#description)\n  - [Installation](#installation)\n  - [Usage](#usage)\n    - [With JSON](#with-json)\n    - [With Pickle](#with-pickle)\n  - [License](#license)\n\n## Description\n\nTwitter Web App (Web 版公式クライアント) の内部 API を使い、[Tweepy](https://github.com/tweepy/tweepy) でスクリーンネームとパスワードで認証するためのライブラリです。\n\nスクリーンネーム (ex: `@elonmusk`) とパスワードを指定して認証し、取得した Cookie などの認証情報で Twitter API v1.1 にアクセスできます。  \n毎回ログインしていては面倒 \u0026 不審なアクセス扱いされそうなので、Cookie をファイルなどに保存し、次回以降はその Cookie を使ってログインする機能もあります。\n\nTweepy を利用しているソースコードのうち、認証部分 (`tweepy.auth.OAuth1UserHandler`) を `tweepy_authlib.CookieSessionUserHandler` に置き換えるだけで、かんたんに Cookie ベースの認証に変更できます！  \n認証部分以外は OAuth API のときの実装がそのまま使えるので、ソースコードの変更も最小限に抑えられます。\n\n\u003e [!NOTE]  \n\u003e OAuth API と公式クライアント用の内部 API がほぼ共通だった v1.1 とは異なり、v2 では OAuth API と公式クライアント用の内部 API が大きく異なります。  \n\u003e そのため、`CookieSessionUserHandler` は Twitter API v2 には対応していません。  \n\u003e また、今のところ2段階認証にも対応していません (2段階認証に関しては技術的には実装可能だが、確認コードの送信周りの実装が面倒…) 。\n\n認証フローはブラウザ上で動作する Web 版公式クライアントの API アクセス動作や HTTP リクエストヘッダーを可能な限りエミュレートしています。  \nブラウザから抽出した Web 版公式クライアントのログイン済み Cookie を使うことでも認証が可能です。\n\n\u003e [!NOTE]  \n\u003e ブラウザから Cookie を抽出する場合、(不審なアクセス扱いされないために) できればすべての Cookie を抽出することが望ましいですが、実装上は Cookie 内の `auth_token` と `ct0` の2つの値だけあれば認証できます。  \n\u003e なお、ブラウザから取得した Cookie は事前に `requests.cookies.RequestsCookieJar` に変換してください。\n\nさらに API アクセス時は TweetDeck の HTTP リクエスト (Twitter API v1.1) をエミュレートしているため、レートリミットなどの制限は TweetDeck と同一です。  \n\n\u003e [!NOTE]  \n\u003e `CookieSessionUserHandler` で取得した認証情報を使うと、TweetDeck でしか利用できない search/universal などの内部 API にもアクセスできるようになります。  \n\u003e ただし、Tweepy はそうした内部 API をサポートしていないため、アクセスするには独自に `tweepy.API.request()` で HTTP リクエストを送る必要があります。\n\n\u003e [!WARNING]  \n\u003e このライブラリは、非公式かつ内部的な API をリバースエンジニアリングし、ブラウザとほぼ同じように API アクセスを行うことで、本来 Web 版公式クライアントでしか利用できない Cookie 認証での Twitter API v1.1 へのアクセスを可能にしています。  \n\u003e 可能な限りブラウザの挙動を模倣することでできるだけ Twitter 側に怪しまれないような実装を行っていますが、非公式な方法ゆえ、**このライブラリを利用して Twitter API にアクセスすると、最悪アカウント凍結やシャドウバンなどの制限が適用される可能性もあります。**  \n\u003e また、**Twitter API の仕様変更により、このライブラリが突然動作しなくなることも考えられます。**  \n\u003e このライブラリを利用して API アクセスを行うことによって生じたいかなる損害についても、著者は一切の責任を負いません。利用にあたっては十分ご注意ください。\n\n\u003e [!WARNING]  \n\u003e **スクリーンネームとパスワードを指定して認証する際は、できるだけログイン実績のある IP アドレスでの実行をおすすめします。**  \n\u003e このライブラリでの認証は、Web 版公式クライアントのログインと同じように行われるため、ログイン実績のない IP アドレスから認証すると、不審なログインとして扱われてしまう可能性があります。  \n\u003e また、実行毎に毎回認証を行うと、不審なログインとして扱われてしまう可能性が高くなります。  \n\u003e **初回の認証以降では、以前認証した際に保存した Cookie を使って認証することを強く推奨します。**\n\n## Installation\n\n```console\npip install tweepy-authlib\n```\n\n## Usage\n\n### With JSON\n\n[example_json.py](example_json.py)\n\n```python\nimport dotenv\nimport os\nimport json\nimport tweepy\nfrom pathlib import Path\nfrom pprint import pprint\nfrom requests.cookies import RequestsCookieJar\nfrom tweepy_authlib import CookieSessionUserHandler\n\ntry:\n    terminal_size = os.get_terminal_size().columns\nexcept OSError:\n    terminal_size = 80\n\n# ユーザー名とパスワードを環境変数から取得\ndotenv.load_dotenv()\nscreen_name = os.environ.get('TWITTER_SCREEN_NAME', 'your_screen_name')\npassword = os.environ.get('TWITTER_PASSWORD', 'your_password')\n\n# 保存した Cookie を使って認証\n## 毎回ログインすると不審なログインとして扱われる可能性が高くなるため、\n## できるだけ以前認証した際に保存した Cookie を使って認証することを推奨\nif Path('cookie.json').exists():\n\n    # 保存した Cookie を読み込む\n    with open('cookie.json', 'r') as f:\n        cookies_dict = json.load(f)\n\n    # RequestCookieJar オブジェクトに変換\n    cookies = RequestsCookieJar()\n    for key, value in cookies_dict.items():\n        cookies.set(key, value)\n\n    # 読み込んだ RequestCookieJar オブジェクトを CookieSessionUserHandler に渡す\n    auth_handler = CookieSessionUserHandler(cookies=cookies)\n\n# スクリーンネームとパスワードを指定して認証\nelse:\n\n    # スクリーンネームとパスワードを渡す\n    ## スクリーンネームとパスワードを指定する場合は初期化時に認証のための API リクエストが多数行われるため、完了まで数秒かかる\n    try:\n        auth_handler = CookieSessionUserHandler(screen_name=screen_name, password=password)\n    except tweepy.HTTPException as ex:\n        # パスワードが間違っているなどの理由で認証に失敗した場合\n        if len(ex.api_codes) \u003e 0 and len(ex.api_messages) \u003e 0:\n            error_message = f'Code: {ex.api_codes[0]}, Message: {ex.api_messages[0]}'\n        else:\n            error_message = 'Unknown Error'\n        raise Exception(f'Failed to authenticate with password ({error_message})')\n    except tweepy.TweepyException as ex:\n        # 認証フローの途中で予期せぬエラーが発生し、ログインに失敗した\n        error_message = f'Message: {ex}'\n        raise Exception(f'Unexpected error occurred while authenticate with password ({error_message})')\n\n    # 現在のログインセッションの Cookie を取得\n    cookies_dict = auth_handler.get_cookies_as_dict()\n\n    # Cookie を JSON ファイルに保存\n    with open('cookie.json', 'w') as f:\n        json.dump(cookies_dict, f, ensure_ascii=False, indent=4)\n\n# Tweepy で Twitter API v1.1 にアクセス\napi = tweepy.API(auth_handler)\n\nprint('=' * terminal_size)\nprint('Logged in user:')\nprint('-' * terminal_size)\nuser = api.verify_credentials()\nassert user.screen_name == os.environ['TWITTER_SCREEN_NAME']\npprint(user._json)\nprint('=' * terminal_size)\n\nprint('Followers (3 users):')\nprint('-' * terminal_size)\nfollowers = user.followers(count=3)\nfor follower in followers:\n    pprint(follower._json)\n    print('-' * terminal_size)\nprint('=' * terminal_size)\n\nprint('Following (3 users):')\nprint('-' * terminal_size)\nfriends = user.friends(count=3)\nfor friend in friends:\n    pprint(friend._json)\n    print('-' * terminal_size)\nprint('=' * terminal_size)\n\nprint('Home timeline (3 tweets):')\nprint('-' * terminal_size)\nhome_timeline = api.home_timeline(count=3)\nfor status in home_timeline:\n    pprint(status._json)\n    print('-' * terminal_size)\nprint('=' * terminal_size)\n\ntweet_id = home_timeline[0].id\nprint('Like tweet:')\nprint('-' * terminal_size)\npprint(api.create_favorite(tweet_id)._json)\nprint('=' * terminal_size)\n\nprint('Unlike tweet:')\nprint('-' * terminal_size)\npprint(api.destroy_favorite(tweet_id)._json)\nprint('=' * terminal_size)\n\n# 継続してログインしない場合は明示的にログアウト\n## 単に Cookie を消去するだけだと Twitter にセッションが残り続けてしまう\n## ログアウト後は、取得した Cookie は再利用できなくなる\nauth_handler.logout()\nos.unlink('cookie.json')\n```\n\n### With Pickle\n\n[example_pickle.py](example_pickle.py)\n\n```python\nimport dotenv\nimport os\nimport pickle\nimport tweepy\nfrom pathlib import Path\nfrom pprint import pprint\nfrom tweepy_authlib import CookieSessionUserHandler\n\ntry:\n    terminal_size = os.get_terminal_size().columns\nexcept OSError:\n    terminal_size = 80\n\n# ユーザー名とパスワードを環境変数から取得\ndotenv.load_dotenv()\nscreen_name = os.environ.get('TWITTER_SCREEN_NAME', 'your_screen_name')\npassword = os.environ.get('TWITTER_PASSWORD', 'your_password')\n\n# 保存した Cookie を使って認証\n## 毎回ログインすると不審なログインとして扱われる可能性が高くなるため、\n## できるだけ以前認証した際に保存した Cookie を使って認証することを推奨\nif Path('cookie.pickle').exists():\n\n    # 保存した Cookie を読み込む\n    with open('cookie.pickle', 'rb') as f:\n        cookies = pickle.load(f)\n\n    # 読み込んだ RequestCookieJar オブジェクトを CookieSessionUserHandler に渡す\n    auth_handler = CookieSessionUserHandler(cookies=cookies)\n\n# スクリーンネームとパスワードを指定して認証\nelse:\n\n    # スクリーンネームとパスワードを渡す\n    ## スクリーンネームとパスワードを指定する場合は初期化時に認証のための API リクエストが多数行われるため、完了まで数秒かかる\n    try:\n        auth_handler = CookieSessionUserHandler(screen_name=screen_name, password=password)\n    except tweepy.HTTPException as ex:\n        # パスワードが間違っているなどの理由で認証に失敗した場合\n        if len(ex.api_codes) \u003e 0 and len(ex.api_messages) \u003e 0:\n            error_message = f'Code: {ex.api_codes[0]}, Message: {ex.api_messages[0]}'\n        else:\n            error_message = 'Unknown Error'\n        raise Exception(f'Failed to authenticate with password ({error_message})')\n    except tweepy.TweepyException as ex:\n        # 認証フローの途中で予期せぬエラーが発生し、ログインに失敗した\n        error_message = f'Message: {ex}'\n        raise Exception(f'Unexpected error occurred while authenticate with password ({error_message})')\n\n    # 現在のログインセッションの Cookie を取得\n    cookies = auth_handler.get_cookies()\n\n    # Cookie を pickle 化して保存\n    with open('cookie.pickle', 'wb') as f:\n        pickle.dump(cookies, f)\n\n# Tweepy で Twitter API v1.1 にアクセス\napi = tweepy.API(auth_handler)\n\nprint('=' * terminal_size)\nprint('Logged in user:')\nprint('-' * terminal_size)\nuser = api.verify_credentials()\nassert user.screen_name == os.environ['TWITTER_SCREEN_NAME']\npprint(user._json)\nprint('=' * terminal_size)\n\nprint('Followers (3 users):')\nprint('-' * terminal_size)\nfollowers = user.followers(count=3)\nfor follower in followers:\n    pprint(follower._json)\n    print('-' * terminal_size)\nprint('=' * terminal_size)\n\nprint('Following (3 users):')\nprint('-' * terminal_size)\nfriends = user.friends(count=3)\nfor friend in friends:\n    pprint(friend._json)\n    print('-' * terminal_size)\nprint('=' * terminal_size)\n\nprint('Home timeline (3 tweets):')\nprint('-' * terminal_size)\nhome_timeline = api.home_timeline(count=3)\nfor status in home_timeline:\n    pprint(status._json)\n    print('-' * terminal_size)\nprint('=' * terminal_size)\n\ntweet_id = home_timeline[0].id\nprint('Like tweet:')\nprint('-' * terminal_size)\npprint(api.create_favorite(tweet_id)._json)\nprint('=' * terminal_size)\n\nprint('Unlike tweet:')\nprint('-' * terminal_size)\npprint(api.destroy_favorite(tweet_id)._json)\nprint('=' * terminal_size)\n\n# 継続してログインしない場合は明示的にログアウト\n## 単に Cookie を消去するだけだと Twitter にセッションが残り続けてしまう\n## ログアウト後は、取得した Cookie は再利用できなくなる\nauth_handler.logout()\nos.unlink('cookie.pickle')\n```\n\n## License\n\n[MIT License](License.txt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsukumijima%2Ftweepy-authlib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsukumijima%2Ftweepy-authlib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsukumijima%2Ftweepy-authlib/lists"}