tweepy-authlib


Nametweepy-authlib JSON
Version 1.5.6 PyPI version JSON
download
home_pageNone
SummaryTwitter Web App (Web 版公式クライアント) の内部 API を使い、Tweepy でスクリーンネームとパスワードで認証するためのライブラリ
upload_time2024-12-07 20:20:26
maintainerNone
docs_urlNone
authortsukumi
requires_python>=3.8
licenseNone
keywords library tweepy twitter
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
# tweepy-authlib

[![PyPI - Version](https://img.shields.io/pypi/v/tweepy-authlib.svg)](https://pypi.org/project/tweepy-authlib)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tweepy-authlib.svg)](https://pypi.org/project/tweepy-authlib)

> [!WARNING]  
> **旧 TweetDeck の完全廃止にともない、2023/09/14 頃から内部的に残存していた Twitter API v1.1 の段階的なシャットダウンが開始されています。**  
> **2024/04/30 時点では、下記 API が既に廃止されています ([参考](https://github.com/dimdenGD/OldTweetDeck/blob/main/src/interception.js)) 。**  
> 現時点では 2023/09 にサーバー負荷が高い API が一括廃止されて以降の動きはありません。ただし、リストにない API も既に廃止されている可能性があります。
> - `search/tweets` : ツイート検索
> - `search/universal` : ツイート検索 (旧 TweetDeck 独自 API)
> - `statuses/update` : ツイート投稿
> - `statuses/retweet/:id` : リツイート
> - `statuses/unretweet/:id` : リツイート取り消し
> - `statuses/show/:id` : ツイート詳細
> - `statuses/destroy/:id` : ツイート削除
> - `statuses/user_timeline` : ユーザータイムライン
> - `users/search` : ユーザー検索
>
> **現在 tweepy-authlib を利用して上記機能を実装するには、別途 GraphQL API (Twitter Web App の内部 API) クライアントを自作する必要があります。**  
> 私が [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)) にありますので、参考になれば幸いです。  
> また現時点で廃止されていない API を利用したサンプルコードが [example_json.py](example_json.py) と [example_pickle.py](example_pickle.py) にありますので、そちらもご一読ください。

> [!NOTE]  
> **tweepy-authlib v1.4.1 以降では、より厳密に Twitter Web App からの HTTP リクエストに偽装したり、一部の Twitter API v1.1 に再びアクセスできるようになるなど、様々な改善が行われています!**  
> 凍結やアカウントロックのリスクを下げるためにも、最新版の tweepy-authlib の利用をおすすめします。

> [!IMPORTANT]  
> **tweepy-authlib v1.5.0 にて、twitter.com の x.com への移行に対応しました。**  
> 2024/05/18 時点では twitter.com のままでも API にアクセスできますが、不審がられるリスクが上がるうえ、いつまでアクセスできるかも不透明なためです。  
> これにより、`CookieSessionUserHandler.get_graphql_api_headers()` で返される Origin / Referer ヘッダーの値が `twitter.com` から `x.com` に変更されています。  
> GraphQL API クライアントを自作されている場合は、更新と同時に GraphQL API クライアントのアクセス先 URL を `twitter.com/i/api/graphql` から `x.com/i/api/graphql` に変更することを推奨します。

> [!IMPORTANT]  
> 2024/05/18 時点では [tweepy-authlib が依存する js2py が Python 3.12 に対応していない](https://github.com/tsukumijima/tweepy-authlib/issues/5) ため、tweepy-authlib は Python 3.12 以降では動作しません。  
> [js2py](https://github.com/PiotrDabkowski/Js2Py) の Python 3.12 対応が完了するまで、Python 3.11 以下での利用をおすすめします。

-----

**Table of Contents**

- [tweepy-authlib](#tweepy-authlib)
  - [Description](#description)
  - [Installation](#installation)
  - [Usage](#usage)
    - [With JSON](#with-json)
    - [With Pickle](#with-pickle)
  - [License](#license)

## Description

Twitter Web App (Web 版公式クライアント) の内部 API を使い、[Tweepy](https://github.com/tweepy/tweepy) でスクリーンネームとパスワードで認証するためのライブラリです。

スクリーンネーム (ex: `@elonmusk`) とパスワードを指定して認証し、取得した Cookie などの認証情報で Twitter API v1.1 にアクセスできます。  
毎回ログインしていては面倒 & 不審なアクセス扱いされそうなので、Cookie をファイルなどに保存し、次回以降はその Cookie を使ってログインする機能もあります。

Tweepy を利用しているソースコードのうち、認証部分 (`tweepy.auth.OAuth1UserHandler`) を `tweepy_authlib.CookieSessionUserHandler` に置き換えるだけで、かんたんに Cookie ベースの認証に変更できます!  
認証部分以外は OAuth API のときの実装がそのまま使えるので、ソースコードの変更も最小限に抑えられます。

> [!NOTE]  
> OAuth API と公式クライアント用の内部 API がほぼ共通だった v1.1 とは異なり、v2 では OAuth API と公式クライアント用の内部 API が大きく異なります。  
> そのため、`CookieSessionUserHandler` は Twitter API v2 には対応していません。  
> また、今のところ2段階認証にも対応していません (2段階認証に関しては技術的には実装可能だが、確認コードの送信周りの実装が面倒…) 。

認証フローはブラウザ上で動作する Web 版公式クライアントの API アクセス動作や HTTP リクエストヘッダーを可能な限りエミュレートしています。  
ブラウザから抽出した Web 版公式クライアントのログイン済み Cookie を使うことでも認証が可能です。

> [!NOTE]  
> ブラウザから Cookie を抽出する場合、(不審なアクセス扱いされないために) できればすべての Cookie を抽出することが望ましいですが、実装上は Cookie 内の `auth_token` と `ct0` の2つの値だけあれば認証できます。  
> なお、ブラウザから取得した Cookie は事前に `requests.cookies.RequestsCookieJar` に変換してください。

さらに API アクセス時は TweetDeck の HTTP リクエスト (Twitter API v1.1) をエミュレートしているため、レートリミットなどの制限は TweetDeck と同一です。  

> [!NOTE]  
> `CookieSessionUserHandler` で取得した認証情報を使うと、TweetDeck でしか利用できない search/universal などの内部 API にもアクセスできるようになります。  
> ただし、Tweepy はそうした内部 API をサポートしていないため、アクセスするには独自に `tweepy.API.request()` で HTTP リクエストを送る必要があります。

> [!WARNING]  
> このライブラリは、非公式かつ内部的な API をリバースエンジニアリングし、ブラウザとほぼ同じように API アクセスを行うことで、本来 Web 版公式クライアントでしか利用できない Cookie 認証での Twitter API v1.1 へのアクセスを可能にしています。  
> 可能な限りブラウザの挙動を模倣することでできるだけ Twitter 側に怪しまれないような実装を行っていますが、非公式な方法ゆえ、**このライブラリを利用して Twitter API にアクセスすると、最悪アカウント凍結やシャドウバンなどの制限が適用される可能性もあります。**  
> また、**Twitter API の仕様変更により、このライブラリが突然動作しなくなることも考えられます。**  
> このライブラリを利用して API アクセスを行うことによって生じたいかなる損害についても、著者は一切の責任を負いません。利用にあたっては十分ご注意ください。

> [!WARNING]  
> **スクリーンネームとパスワードを指定して認証する際は、できるだけログイン実績のある IP アドレスでの実行をおすすめします。**  
> このライブラリでの認証は、Web 版公式クライアントのログインと同じように行われるため、ログイン実績のない IP アドレスから認証すると、不審なログインとして扱われてしまう可能性があります。  
> また、実行毎に毎回認証を行うと、不審なログインとして扱われてしまう可能性が高くなります。  
> **初回の認証以降では、以前認証した際に保存した Cookie を使って認証することを強く推奨します。**

## Installation

```console
pip install tweepy-authlib
```

## Usage

### With JSON

[example_json.py](example_json.py)

```python
import dotenv
import os
import json
import tweepy
from pathlib import Path
from pprint import pprint
from requests.cookies import RequestsCookieJar
from tweepy_authlib import CookieSessionUserHandler

try:
    terminal_size = os.get_terminal_size().columns
except OSError:
    terminal_size = 80

# ユーザー名とパスワードを環境変数から取得
dotenv.load_dotenv()
screen_name = os.environ.get('TWITTER_SCREEN_NAME', 'your_screen_name')
password = os.environ.get('TWITTER_PASSWORD', 'your_password')

# 保存した Cookie を使って認証
## 毎回ログインすると不審なログインとして扱われる可能性が高くなるため、
## できるだけ以前認証した際に保存した Cookie を使って認証することを推奨
if Path('cookie.json').exists():

    # 保存した Cookie を読み込む
    with open('cookie.json', 'r') as f:
        cookies_dict = json.load(f)

    # RequestCookieJar オブジェクトに変換
    cookies = RequestsCookieJar()
    for key, value in cookies_dict.items():
        cookies.set(key, value)

    # 読み込んだ RequestCookieJar オブジェクトを CookieSessionUserHandler に渡す
    auth_handler = CookieSessionUserHandler(cookies=cookies)

# スクリーンネームとパスワードを指定して認証
else:

    # スクリーンネームとパスワードを渡す
    ## スクリーンネームとパスワードを指定する場合は初期化時に認証のための API リクエストが多数行われるため、完了まで数秒かかる
    try:
        auth_handler = CookieSessionUserHandler(screen_name=screen_name, password=password)
    except tweepy.HTTPException as ex:
        # パスワードが間違っているなどの理由で認証に失敗した場合
        if len(ex.api_codes) > 0 and len(ex.api_messages) > 0:
            error_message = f'Code: {ex.api_codes[0]}, Message: {ex.api_messages[0]}'
        else:
            error_message = 'Unknown Error'
        raise Exception(f'Failed to authenticate with password ({error_message})')
    except tweepy.TweepyException as ex:
        # 認証フローの途中で予期せぬエラーが発生し、ログインに失敗した
        error_message = f'Message: {ex}'
        raise Exception(f'Unexpected error occurred while authenticate with password ({error_message})')

    # 現在のログインセッションの Cookie を取得
    cookies_dict = auth_handler.get_cookies_as_dict()

    # Cookie を JSON ファイルに保存
    with open('cookie.json', 'w') as f:
        json.dump(cookies_dict, f, ensure_ascii=False, indent=4)

# Tweepy で Twitter API v1.1 にアクセス
api = tweepy.API(auth_handler)

print('=' * terminal_size)
print('Logged in user:')
print('-' * terminal_size)
user = api.verify_credentials()
assert user.screen_name == os.environ['TWITTER_SCREEN_NAME']
pprint(user._json)
print('=' * terminal_size)

print('Followers (3 users):')
print('-' * terminal_size)
followers = user.followers(count=3)
for follower in followers:
    pprint(follower._json)
    print('-' * terminal_size)
print('=' * terminal_size)

print('Following (3 users):')
print('-' * terminal_size)
friends = user.friends(count=3)
for friend in friends:
    pprint(friend._json)
    print('-' * terminal_size)
print('=' * terminal_size)

print('Home timeline (3 tweets):')
print('-' * terminal_size)
home_timeline = api.home_timeline(count=3)
for status in home_timeline:
    pprint(status._json)
    print('-' * terminal_size)
print('=' * terminal_size)

tweet_id = home_timeline[0].id
print('Like tweet:')
print('-' * terminal_size)
pprint(api.create_favorite(tweet_id)._json)
print('=' * terminal_size)

print('Unlike tweet:')
print('-' * terminal_size)
pprint(api.destroy_favorite(tweet_id)._json)
print('=' * terminal_size)

# 継続してログインしない場合は明示的にログアウト
## 単に Cookie を消去するだけだと Twitter にセッションが残り続けてしまう
## ログアウト後は、取得した Cookie は再利用できなくなる
auth_handler.logout()
os.unlink('cookie.json')
```

### With Pickle

[example_pickle.py](example_pickle.py)

```python
import dotenv
import os
import pickle
import tweepy
from pathlib import Path
from pprint import pprint
from tweepy_authlib import CookieSessionUserHandler

try:
    terminal_size = os.get_terminal_size().columns
except OSError:
    terminal_size = 80

# ユーザー名とパスワードを環境変数から取得
dotenv.load_dotenv()
screen_name = os.environ.get('TWITTER_SCREEN_NAME', 'your_screen_name')
password = os.environ.get('TWITTER_PASSWORD', 'your_password')

# 保存した Cookie を使って認証
## 毎回ログインすると不審なログインとして扱われる可能性が高くなるため、
## できるだけ以前認証した際に保存した Cookie を使って認証することを推奨
if Path('cookie.pickle').exists():

    # 保存した Cookie を読み込む
    with open('cookie.pickle', 'rb') as f:
        cookies = pickle.load(f)

    # 読み込んだ RequestCookieJar オブジェクトを CookieSessionUserHandler に渡す
    auth_handler = CookieSessionUserHandler(cookies=cookies)

# スクリーンネームとパスワードを指定して認証
else:

    # スクリーンネームとパスワードを渡す
    ## スクリーンネームとパスワードを指定する場合は初期化時に認証のための API リクエストが多数行われるため、完了まで数秒かかる
    try:
        auth_handler = CookieSessionUserHandler(screen_name=screen_name, password=password)
    except tweepy.HTTPException as ex:
        # パスワードが間違っているなどの理由で認証に失敗した場合
        if len(ex.api_codes) > 0 and len(ex.api_messages) > 0:
            error_message = f'Code: {ex.api_codes[0]}, Message: {ex.api_messages[0]}'
        else:
            error_message = 'Unknown Error'
        raise Exception(f'Failed to authenticate with password ({error_message})')
    except tweepy.TweepyException as ex:
        # 認証フローの途中で予期せぬエラーが発生し、ログインに失敗した
        error_message = f'Message: {ex}'
        raise Exception(f'Unexpected error occurred while authenticate with password ({error_message})')

    # 現在のログインセッションの Cookie を取得
    cookies = auth_handler.get_cookies()

    # Cookie を pickle 化して保存
    with open('cookie.pickle', 'wb') as f:
        pickle.dump(cookies, f)

# Tweepy で Twitter API v1.1 にアクセス
api = tweepy.API(auth_handler)

print('=' * terminal_size)
print('Logged in user:')
print('-' * terminal_size)
user = api.verify_credentials()
assert user.screen_name == os.environ['TWITTER_SCREEN_NAME']
pprint(user._json)
print('=' * terminal_size)

print('Followers (3 users):')
print('-' * terminal_size)
followers = user.followers(count=3)
for follower in followers:
    pprint(follower._json)
    print('-' * terminal_size)
print('=' * terminal_size)

print('Following (3 users):')
print('-' * terminal_size)
friends = user.friends(count=3)
for friend in friends:
    pprint(friend._json)
    print('-' * terminal_size)
print('=' * terminal_size)

print('Home timeline (3 tweets):')
print('-' * terminal_size)
home_timeline = api.home_timeline(count=3)
for status in home_timeline:
    pprint(status._json)
    print('-' * terminal_size)
print('=' * terminal_size)

tweet_id = home_timeline[0].id
print('Like tweet:')
print('-' * terminal_size)
pprint(api.create_favorite(tweet_id)._json)
print('=' * terminal_size)

print('Unlike tweet:')
print('-' * terminal_size)
pprint(api.destroy_favorite(tweet_id)._json)
print('=' * terminal_size)

# 継続してログインしない場合は明示的にログアウト
## 単に Cookie を消去するだけだと Twitter にセッションが残り続けてしまう
## ログアウト後は、取得した Cookie は再利用できなくなる
auth_handler.logout()
os.unlink('cookie.pickle')
```

## License

[MIT License](License.txt)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "tweepy-authlib",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "Library, Tweepy, Twitter",
    "author": "tsukumi",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/e9/ab/92ddfcee967a408ed77efdde46bb29676933db3d2a912775fd26fec197c6/tweepy_authlib-1.5.6.tar.gz",
    "platform": null,
    "description": "\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> [!WARNING]  \n> **\u65e7 TweetDeck \u306e\u5b8c\u5168\u5ec3\u6b62\u306b\u3068\u3082\u306a\u3044\u30012023/09/14 \u9803\u304b\u3089\u5185\u90e8\u7684\u306b\u6b8b\u5b58\u3057\u3066\u3044\u305f Twitter API v1.1 \u306e\u6bb5\u968e\u7684\u306a\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3\u304c\u958b\u59cb\u3055\u308c\u3066\u3044\u307e\u3059\u3002**  \n> **2024/04/30 \u6642\u70b9\u3067\u306f\u3001\u4e0b\u8a18 API \u304c\u65e2\u306b\u5ec3\u6b62\u3055\u308c\u3066\u3044\u307e\u3059 ([\u53c2\u8003](https://github.com/dimdenGD/OldTweetDeck/blob/main/src/interception.js)) \u3002**  \n> \u73fe\u6642\u70b9\u3067\u306f 2023/09 \u306b\u30b5\u30fc\u30d0\u30fc\u8ca0\u8377\u304c\u9ad8\u3044 API \u304c\u4e00\u62ec\u5ec3\u6b62\u3055\u308c\u3066\u4ee5\u964d\u306e\u52d5\u304d\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u305f\u3060\u3057\u3001\u30ea\u30b9\u30c8\u306b\u306a\u3044 API \u3082\u65e2\u306b\u5ec3\u6b62\u3055\u308c\u3066\u3044\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\n> - `search/tweets` : \u30c4\u30a4\u30fc\u30c8\u691c\u7d22\n> - `search/universal` : \u30c4\u30a4\u30fc\u30c8\u691c\u7d22 (\u65e7 TweetDeck \u72ec\u81ea API)\n> - `statuses/update` : \u30c4\u30a4\u30fc\u30c8\u6295\u7a3f\n> - `statuses/retweet/:id` : \u30ea\u30c4\u30a4\u30fc\u30c8\n> - `statuses/unretweet/:id` : \u30ea\u30c4\u30a4\u30fc\u30c8\u53d6\u308a\u6d88\u3057\n> - `statuses/show/:id` : \u30c4\u30a4\u30fc\u30c8\u8a73\u7d30\n> - `statuses/destroy/:id` : \u30c4\u30a4\u30fc\u30c8\u524a\u9664\n> - `statuses/user_timeline` : \u30e6\u30fc\u30b6\u30fc\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\n> - `users/search` : \u30e6\u30fc\u30b6\u30fc\u691c\u7d22\n>\n> **\u73fe\u5728 tweepy-authlib \u3092\u5229\u7528\u3057\u3066\u4e0a\u8a18\u6a5f\u80fd\u3092\u5b9f\u88c5\u3059\u308b\u306b\u306f\u3001\u5225\u9014 GraphQL API (Twitter Web App \u306e\u5185\u90e8 API) \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u81ea\u4f5c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002**  \n> \u79c1\u304c [KonomiTV](https://github.com/tsukumijima/KonomiTV) \u5411\u3051\u306b\u958b\u767a\u3057\u305f GraphQL API \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u5b9f\u88c5\u304c [\u3053\u3061\u3089](https://github.com/tsukumijima/KonomiTV/blob/master/server/app/utils/TwitterGraphQLAPI.py) ([\u4f7f\u7528\u4f8b](https://github.com/tsukumijima/KonomiTV/blob/master/server/app/routers/TwitterRouter.py)) \u306b\u3042\u308a\u307e\u3059\u306e\u3067\u3001\u53c2\u8003\u306b\u306a\u308c\u3070\u5e78\u3044\u3067\u3059\u3002  \n> \u307e\u305f\u73fe\u6642\u70b9\u3067\u5ec3\u6b62\u3055\u308c\u3066\u3044\u306a\u3044 API \u3092\u5229\u7528\u3057\u305f\u30b5\u30f3\u30d7\u30eb\u30b3\u30fc\u30c9\u304c [example_json.py](example_json.py) \u3068 [example_pickle.py](example_pickle.py) \u306b\u3042\u308a\u307e\u3059\u306e\u3067\u3001\u305d\u3061\u3089\u3082\u3054\u4e00\u8aad\u304f\u3060\u3055\u3044\u3002\n\n> [!NOTE]  \n> **tweepy-authlib v1.4.1 \u4ee5\u964d\u3067\u306f\u3001\u3088\u308a\u53b3\u5bc6\u306b Twitter Web App \u304b\u3089\u306e HTTP \u30ea\u30af\u30a8\u30b9\u30c8\u306b\u507d\u88c5\u3057\u305f\u308a\u3001\u4e00\u90e8\u306e Twitter API v1.1 \u306b\u518d\u3073\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u306a\u3069\u3001\u69d8\u3005\u306a\u6539\u5584\u304c\u884c\u308f\u308c\u3066\u3044\u307e\u3059\uff01**  \n> \u51cd\u7d50\u3084\u30a2\u30ab\u30a6\u30f3\u30c8\u30ed\u30c3\u30af\u306e\u30ea\u30b9\u30af\u3092\u4e0b\u3052\u308b\u305f\u3081\u306b\u3082\u3001\u6700\u65b0\u7248\u306e tweepy-authlib \u306e\u5229\u7528\u3092\u304a\u3059\u3059\u3081\u3057\u307e\u3059\u3002\n\n> [!IMPORTANT]  \n> **tweepy-authlib v1.5.0 \u306b\u3066\u3001twitter.com \u306e x.com \u3078\u306e\u79fb\u884c\u306b\u5bfe\u5fdc\u3057\u307e\u3057\u305f\u3002**  \n> 2024/05/18 \u6642\u70b9\u3067\u306f twitter.com \u306e\u307e\u307e\u3067\u3082 API \u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u3059\u304c\u3001\u4e0d\u5be9\u304c\u3089\u308c\u308b\u30ea\u30b9\u30af\u304c\u4e0a\u304c\u308b\u3046\u3048\u3001\u3044\u3064\u307e\u3067\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u304b\u3082\u4e0d\u900f\u660e\u306a\u305f\u3081\u3067\u3059\u3002  \n> \u3053\u308c\u306b\u3088\u308a\u3001`CookieSessionUserHandler.get_graphql_api_headers()` \u3067\u8fd4\u3055\u308c\u308b Origin / Referer \u30d8\u30c3\u30c0\u30fc\u306e\u5024\u304c `twitter.com` \u304b\u3089 `x.com` \u306b\u5909\u66f4\u3055\u308c\u3066\u3044\u307e\u3059\u3002  \n> GraphQL API \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u81ea\u4f5c\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u66f4\u65b0\u3068\u540c\u6642\u306b GraphQL API \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u30a2\u30af\u30bb\u30b9\u5148 URL \u3092 `twitter.com/i/api/graphql` \u304b\u3089 `x.com/i/api/graphql` \u306b\u5909\u66f4\u3059\u308b\u3053\u3068\u3092\u63a8\u5968\u3057\u307e\u3059\u3002\n\n> [!IMPORTANT]  \n> 2024/05/18 \u6642\u70b9\u3067\u306f [tweepy-authlib \u304c\u4f9d\u5b58\u3059\u308b js2py \u304c Python 3.12 \u306b\u5bfe\u5fdc\u3057\u3066\u3044\u306a\u3044](https://github.com/tsukumijima/tweepy-authlib/issues/5) \u305f\u3081\u3001tweepy-authlib \u306f Python 3.12 \u4ee5\u964d\u3067\u306f\u52d5\u4f5c\u3057\u307e\u305b\u3093\u3002  \n> [js2py](https://github.com/PiotrDabkowski/Js2Py) \u306e Python 3.12 \u5bfe\u5fdc\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u3001Python 3.11 \u4ee5\u4e0b\u3067\u306e\u5229\u7528\u3092\u304a\u3059\u3059\u3081\u3057\u307e\u3059\u3002\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 \u7248\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8) \u306e\u5185\u90e8 API \u3092\u4f7f\u3044\u3001[Tweepy](https://github.com/tweepy/tweepy) \u3067\u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u8a8d\u8a3c\u3059\u308b\u305f\u3081\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u3067\u3059\u3002\n\n\u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0 (ex: `@elonmusk`) \u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6307\u5b9a\u3057\u3066\u8a8d\u8a3c\u3057\u3001\u53d6\u5f97\u3057\u305f Cookie \u306a\u3069\u306e\u8a8d\u8a3c\u60c5\u5831\u3067 Twitter API v1.1 \u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u3059\u3002  \n\u6bce\u56de\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u3044\u3066\u306f\u9762\u5012 & \u4e0d\u5be9\u306a\u30a2\u30af\u30bb\u30b9\u6271\u3044\u3055\u308c\u305d\u3046\u306a\u306e\u3067\u3001Cookie \u3092\u30d5\u30a1\u30a4\u30eb\u306a\u3069\u306b\u4fdd\u5b58\u3057\u3001\u6b21\u56de\u4ee5\u964d\u306f\u305d\u306e Cookie \u3092\u4f7f\u3063\u3066\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u6a5f\u80fd\u3082\u3042\u308a\u307e\u3059\u3002\n\nTweepy \u3092\u5229\u7528\u3057\u3066\u3044\u308b\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\u306e\u3046\u3061\u3001\u8a8d\u8a3c\u90e8\u5206 (`tweepy.auth.OAuth1UserHandler`) \u3092 `tweepy_authlib.CookieSessionUserHandler` \u306b\u7f6e\u304d\u63db\u3048\u308b\u3060\u3051\u3067\u3001\u304b\u3093\u305f\u3093\u306b Cookie \u30d9\u30fc\u30b9\u306e\u8a8d\u8a3c\u306b\u5909\u66f4\u3067\u304d\u307e\u3059\uff01  \n\u8a8d\u8a3c\u90e8\u5206\u4ee5\u5916\u306f OAuth API \u306e\u3068\u304d\u306e\u5b9f\u88c5\u304c\u305d\u306e\u307e\u307e\u4f7f\u3048\u308b\u306e\u3067\u3001\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\u306e\u5909\u66f4\u3082\u6700\u5c0f\u9650\u306b\u6291\u3048\u3089\u308c\u307e\u3059\u3002\n\n> [!NOTE]  \n> OAuth API \u3068\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u7528\u306e\u5185\u90e8 API \u304c\u307b\u307c\u5171\u901a\u3060\u3063\u305f v1.1 \u3068\u306f\u7570\u306a\u308a\u3001v2 \u3067\u306f OAuth API \u3068\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u7528\u306e\u5185\u90e8 API \u304c\u5927\u304d\u304f\u7570\u306a\u308a\u307e\u3059\u3002  \n> \u305d\u306e\u305f\u3081\u3001`CookieSessionUserHandler` \u306f Twitter API v2 \u306b\u306f\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u305b\u3093\u3002  \n> \u307e\u305f\u3001\u4eca\u306e\u3068\u3053\u308d2\u6bb5\u968e\u8a8d\u8a3c\u306b\u3082\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u305b\u3093 (2\u6bb5\u968e\u8a8d\u8a3c\u306b\u95a2\u3057\u3066\u306f\u6280\u8853\u7684\u306b\u306f\u5b9f\u88c5\u53ef\u80fd\u3060\u304c\u3001\u78ba\u8a8d\u30b3\u30fc\u30c9\u306e\u9001\u4fe1\u5468\u308a\u306e\u5b9f\u88c5\u304c\u9762\u5012\u2026) \u3002\n\n\u8a8d\u8a3c\u30d5\u30ed\u30fc\u306f\u30d6\u30e9\u30a6\u30b6\u4e0a\u3067\u52d5\u4f5c\u3059\u308b Web \u7248\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e API \u30a2\u30af\u30bb\u30b9\u52d5\u4f5c\u3084 HTTP \u30ea\u30af\u30a8\u30b9\u30c8\u30d8\u30c3\u30c0\u30fc\u3092\u53ef\u80fd\u306a\u9650\u308a\u30a8\u30df\u30e5\u30ec\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u3002  \n\u30d6\u30e9\u30a6\u30b6\u304b\u3089\u62bd\u51fa\u3057\u305f Web \u7248\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u30ed\u30b0\u30a4\u30f3\u6e08\u307f Cookie \u3092\u4f7f\u3046\u3053\u3068\u3067\u3082\u8a8d\u8a3c\u304c\u53ef\u80fd\u3067\u3059\u3002\n\n> [!NOTE]  \n> \u30d6\u30e9\u30a6\u30b6\u304b\u3089 Cookie \u3092\u62bd\u51fa\u3059\u308b\u5834\u5408\u3001(\u4e0d\u5be9\u306a\u30a2\u30af\u30bb\u30b9\u6271\u3044\u3055\u308c\u306a\u3044\u305f\u3081\u306b) \u3067\u304d\u308c\u3070\u3059\u3079\u3066\u306e Cookie \u3092\u62bd\u51fa\u3059\u308b\u3053\u3068\u304c\u671b\u307e\u3057\u3044\u3067\u3059\u304c\u3001\u5b9f\u88c5\u4e0a\u306f Cookie \u5185\u306e `auth_token` \u3068 `ct0` \u306e2\u3064\u306e\u5024\u3060\u3051\u3042\u308c\u3070\u8a8d\u8a3c\u3067\u304d\u307e\u3059\u3002  \n> \u306a\u304a\u3001\u30d6\u30e9\u30a6\u30b6\u304b\u3089\u53d6\u5f97\u3057\u305f Cookie \u306f\u4e8b\u524d\u306b `requests.cookies.RequestsCookieJar` \u306b\u5909\u63db\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n\u3055\u3089\u306b API \u30a2\u30af\u30bb\u30b9\u6642\u306f TweetDeck \u306e HTTP \u30ea\u30af\u30a8\u30b9\u30c8 (Twitter API v1.1) \u3092\u30a8\u30df\u30e5\u30ec\u30fc\u30c8\u3057\u3066\u3044\u308b\u305f\u3081\u3001\u30ec\u30fc\u30c8\u30ea\u30df\u30c3\u30c8\u306a\u3069\u306e\u5236\u9650\u306f TweetDeck \u3068\u540c\u4e00\u3067\u3059\u3002  \n\n> [!NOTE]  \n> `CookieSessionUserHandler` \u3067\u53d6\u5f97\u3057\u305f\u8a8d\u8a3c\u60c5\u5831\u3092\u4f7f\u3046\u3068\u3001TweetDeck \u3067\u3057\u304b\u5229\u7528\u3067\u304d\u306a\u3044 search/universal \u306a\u3069\u306e\u5185\u90e8 API \u306b\u3082\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002  \n> \u305f\u3060\u3057\u3001Tweepy \u306f\u305d\u3046\u3057\u305f\u5185\u90e8 API \u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u306a\u3044\u305f\u3081\u3001\u30a2\u30af\u30bb\u30b9\u3059\u308b\u306b\u306f\u72ec\u81ea\u306b `tweepy.API.request()` \u3067 HTTP \u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n> [!WARNING]  \n> \u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u306f\u3001\u975e\u516c\u5f0f\u304b\u3064\u5185\u90e8\u7684\u306a API \u3092\u30ea\u30d0\u30fc\u30b9\u30a8\u30f3\u30b8\u30cb\u30a2\u30ea\u30f3\u30b0\u3057\u3001\u30d6\u30e9\u30a6\u30b6\u3068\u307b\u307c\u540c\u3058\u3088\u3046\u306b API \u30a2\u30af\u30bb\u30b9\u3092\u884c\u3046\u3053\u3068\u3067\u3001\u672c\u6765 Web \u7248\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3067\u3057\u304b\u5229\u7528\u3067\u304d\u306a\u3044 Cookie \u8a8d\u8a3c\u3067\u306e Twitter API v1.1 \u3078\u306e\u30a2\u30af\u30bb\u30b9\u3092\u53ef\u80fd\u306b\u3057\u3066\u3044\u307e\u3059\u3002  \n> \u53ef\u80fd\u306a\u9650\u308a\u30d6\u30e9\u30a6\u30b6\u306e\u6319\u52d5\u3092\u6a21\u5023\u3059\u308b\u3053\u3068\u3067\u3067\u304d\u308b\u3060\u3051 Twitter \u5074\u306b\u602a\u3057\u307e\u308c\u306a\u3044\u3088\u3046\u306a\u5b9f\u88c5\u3092\u884c\u3063\u3066\u3044\u307e\u3059\u304c\u3001\u975e\u516c\u5f0f\u306a\u65b9\u6cd5\u3086\u3048\u3001**\u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u5229\u7528\u3057\u3066 Twitter API \u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3068\u3001\u6700\u60aa\u30a2\u30ab\u30a6\u30f3\u30c8\u51cd\u7d50\u3084\u30b7\u30e3\u30c9\u30a6\u30d0\u30f3\u306a\u3069\u306e\u5236\u9650\u304c\u9069\u7528\u3055\u308c\u308b\u53ef\u80fd\u6027\u3082\u3042\u308a\u307e\u3059\u3002**  \n> \u307e\u305f\u3001**Twitter API \u306e\u4ed5\u69d8\u5909\u66f4\u306b\u3088\u308a\u3001\u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u304c\u7a81\u7136\u52d5\u4f5c\u3057\u306a\u304f\u306a\u308b\u3053\u3068\u3082\u8003\u3048\u3089\u308c\u307e\u3059\u3002**  \n> \u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u5229\u7528\u3057\u3066 API \u30a2\u30af\u30bb\u30b9\u3092\u884c\u3046\u3053\u3068\u306b\u3088\u3063\u3066\u751f\u3058\u305f\u3044\u304b\u306a\u308b\u640d\u5bb3\u306b\u3064\u3044\u3066\u3082\u3001\u8457\u8005\u306f\u4e00\u5207\u306e\u8cac\u4efb\u3092\u8ca0\u3044\u307e\u305b\u3093\u3002\u5229\u7528\u306b\u3042\u305f\u3063\u3066\u306f\u5341\u5206\u3054\u6ce8\u610f\u304f\u3060\u3055\u3044\u3002\n\n> [!WARNING]  \n> **\u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6307\u5b9a\u3057\u3066\u8a8d\u8a3c\u3059\u308b\u969b\u306f\u3001\u3067\u304d\u308b\u3060\u3051\u30ed\u30b0\u30a4\u30f3\u5b9f\u7e3e\u306e\u3042\u308b IP \u30a2\u30c9\u30ec\u30b9\u3067\u306e\u5b9f\u884c\u3092\u304a\u3059\u3059\u3081\u3057\u307e\u3059\u3002**  \n> \u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u3067\u306e\u8a8d\u8a3c\u306f\u3001Web \u7248\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u30ed\u30b0\u30a4\u30f3\u3068\u540c\u3058\u3088\u3046\u306b\u884c\u308f\u308c\u308b\u305f\u3081\u3001\u30ed\u30b0\u30a4\u30f3\u5b9f\u7e3e\u306e\u306a\u3044 IP \u30a2\u30c9\u30ec\u30b9\u304b\u3089\u8a8d\u8a3c\u3059\u308b\u3068\u3001\u4e0d\u5be9\u306a\u30ed\u30b0\u30a4\u30f3\u3068\u3057\u3066\u6271\u308f\u308c\u3066\u3057\u307e\u3046\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002  \n> \u307e\u305f\u3001\u5b9f\u884c\u6bce\u306b\u6bce\u56de\u8a8d\u8a3c\u3092\u884c\u3046\u3068\u3001\u4e0d\u5be9\u306a\u30ed\u30b0\u30a4\u30f3\u3068\u3057\u3066\u6271\u308f\u308c\u3066\u3057\u307e\u3046\u53ef\u80fd\u6027\u304c\u9ad8\u304f\u306a\u308a\u307e\u3059\u3002  \n> **\u521d\u56de\u306e\u8a8d\u8a3c\u4ee5\u964d\u3067\u306f\u3001\u4ee5\u524d\u8a8d\u8a3c\u3057\u305f\u969b\u306b\u4fdd\u5b58\u3057\u305f Cookie \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\u3059\u308b\u3053\u3068\u3092\u5f37\u304f\u63a8\u5968\u3057\u307e\u3059\u3002**\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# \u30e6\u30fc\u30b6\u30fc\u540d\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u74b0\u5883\u5909\u6570\u304b\u3089\u53d6\u5f97\ndotenv.load_dotenv()\nscreen_name = os.environ.get('TWITTER_SCREEN_NAME', 'your_screen_name')\npassword = os.environ.get('TWITTER_PASSWORD', 'your_password')\n\n# \u4fdd\u5b58\u3057\u305f Cookie \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\n## \u6bce\u56de\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u3068\u4e0d\u5be9\u306a\u30ed\u30b0\u30a4\u30f3\u3068\u3057\u3066\u6271\u308f\u308c\u308b\u53ef\u80fd\u6027\u304c\u9ad8\u304f\u306a\u308b\u305f\u3081\u3001\n## \u3067\u304d\u308b\u3060\u3051\u4ee5\u524d\u8a8d\u8a3c\u3057\u305f\u969b\u306b\u4fdd\u5b58\u3057\u305f Cookie \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\u3059\u308b\u3053\u3068\u3092\u63a8\u5968\nif Path('cookie.json').exists():\n\n    # \u4fdd\u5b58\u3057\u305f Cookie \u3092\u8aad\u307f\u8fbc\u3080\n    with open('cookie.json', 'r') as f:\n        cookies_dict = json.load(f)\n\n    # RequestCookieJar \u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306b\u5909\u63db\n    cookies = RequestsCookieJar()\n    for key, value in cookies_dict.items():\n        cookies.set(key, value)\n\n    # \u8aad\u307f\u8fbc\u3093\u3060 RequestCookieJar \u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092 CookieSessionUserHandler \u306b\u6e21\u3059\n    auth_handler = CookieSessionUserHandler(cookies=cookies)\n\n# \u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6307\u5b9a\u3057\u3066\u8a8d\u8a3c\nelse:\n\n    # \u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6e21\u3059\n    ## \u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\u306f\u521d\u671f\u5316\u6642\u306b\u8a8d\u8a3c\u306e\u305f\u3081\u306e API \u30ea\u30af\u30a8\u30b9\u30c8\u304c\u591a\u6570\u884c\u308f\u308c\u308b\u305f\u3081\u3001\u5b8c\u4e86\u307e\u3067\u6570\u79d2\u304b\u304b\u308b\n    try:\n        auth_handler = CookieSessionUserHandler(screen_name=screen_name, password=password)\n    except tweepy.HTTPException as ex:\n        # \u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9593\u9055\u3063\u3066\u3044\u308b\u306a\u3069\u306e\u7406\u7531\u3067\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u305f\u5834\u5408\n        if len(ex.api_codes) > 0 and len(ex.api_messages) > 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        # \u8a8d\u8a3c\u30d5\u30ed\u30fc\u306e\u9014\u4e2d\u3067\u4e88\u671f\u305b\u306c\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u3001\u30ed\u30b0\u30a4\u30f3\u306b\u5931\u6557\u3057\u305f\n        error_message = f'Message: {ex}'\n        raise Exception(f'Unexpected error occurred while authenticate with password ({error_message})')\n\n    # \u73fe\u5728\u306e\u30ed\u30b0\u30a4\u30f3\u30bb\u30c3\u30b7\u30e7\u30f3\u306e Cookie \u3092\u53d6\u5f97\n    cookies_dict = auth_handler.get_cookies_as_dict()\n\n    # Cookie \u3092 JSON \u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\n    with open('cookie.json', 'w') as f:\n        json.dump(cookies_dict, f, ensure_ascii=False, indent=4)\n\n# Tweepy \u3067 Twitter API v1.1 \u306b\u30a2\u30af\u30bb\u30b9\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# \u7d99\u7d9a\u3057\u3066\u30ed\u30b0\u30a4\u30f3\u3057\u306a\u3044\u5834\u5408\u306f\u660e\u793a\u7684\u306b\u30ed\u30b0\u30a2\u30a6\u30c8\n## \u5358\u306b Cookie \u3092\u6d88\u53bb\u3059\u308b\u3060\u3051\u3060\u3068 Twitter \u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u6b8b\u308a\u7d9a\u3051\u3066\u3057\u307e\u3046\n## \u30ed\u30b0\u30a2\u30a6\u30c8\u5f8c\u306f\u3001\u53d6\u5f97\u3057\u305f Cookie \u306f\u518d\u5229\u7528\u3067\u304d\u306a\u304f\u306a\u308b\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# \u30e6\u30fc\u30b6\u30fc\u540d\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u74b0\u5883\u5909\u6570\u304b\u3089\u53d6\u5f97\ndotenv.load_dotenv()\nscreen_name = os.environ.get('TWITTER_SCREEN_NAME', 'your_screen_name')\npassword = os.environ.get('TWITTER_PASSWORD', 'your_password')\n\n# \u4fdd\u5b58\u3057\u305f Cookie \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\n## \u6bce\u56de\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u3068\u4e0d\u5be9\u306a\u30ed\u30b0\u30a4\u30f3\u3068\u3057\u3066\u6271\u308f\u308c\u308b\u53ef\u80fd\u6027\u304c\u9ad8\u304f\u306a\u308b\u305f\u3081\u3001\n## \u3067\u304d\u308b\u3060\u3051\u4ee5\u524d\u8a8d\u8a3c\u3057\u305f\u969b\u306b\u4fdd\u5b58\u3057\u305f Cookie \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\u3059\u308b\u3053\u3068\u3092\u63a8\u5968\nif Path('cookie.pickle').exists():\n\n    # \u4fdd\u5b58\u3057\u305f Cookie \u3092\u8aad\u307f\u8fbc\u3080\n    with open('cookie.pickle', 'rb') as f:\n        cookies = pickle.load(f)\n\n    # \u8aad\u307f\u8fbc\u3093\u3060 RequestCookieJar \u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092 CookieSessionUserHandler \u306b\u6e21\u3059\n    auth_handler = CookieSessionUserHandler(cookies=cookies)\n\n# \u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6307\u5b9a\u3057\u3066\u8a8d\u8a3c\nelse:\n\n    # \u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6e21\u3059\n    ## \u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\u306f\u521d\u671f\u5316\u6642\u306b\u8a8d\u8a3c\u306e\u305f\u3081\u306e API \u30ea\u30af\u30a8\u30b9\u30c8\u304c\u591a\u6570\u884c\u308f\u308c\u308b\u305f\u3081\u3001\u5b8c\u4e86\u307e\u3067\u6570\u79d2\u304b\u304b\u308b\n    try:\n        auth_handler = CookieSessionUserHandler(screen_name=screen_name, password=password)\n    except tweepy.HTTPException as ex:\n        # \u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9593\u9055\u3063\u3066\u3044\u308b\u306a\u3069\u306e\u7406\u7531\u3067\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u305f\u5834\u5408\n        if len(ex.api_codes) > 0 and len(ex.api_messages) > 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        # \u8a8d\u8a3c\u30d5\u30ed\u30fc\u306e\u9014\u4e2d\u3067\u4e88\u671f\u305b\u306c\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u3001\u30ed\u30b0\u30a4\u30f3\u306b\u5931\u6557\u3057\u305f\n        error_message = f'Message: {ex}'\n        raise Exception(f'Unexpected error occurred while authenticate with password ({error_message})')\n\n    # \u73fe\u5728\u306e\u30ed\u30b0\u30a4\u30f3\u30bb\u30c3\u30b7\u30e7\u30f3\u306e Cookie \u3092\u53d6\u5f97\n    cookies = auth_handler.get_cookies()\n\n    # Cookie \u3092 pickle \u5316\u3057\u3066\u4fdd\u5b58\n    with open('cookie.pickle', 'wb') as f:\n        pickle.dump(cookies, f)\n\n# Tweepy \u3067 Twitter API v1.1 \u306b\u30a2\u30af\u30bb\u30b9\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# \u7d99\u7d9a\u3057\u3066\u30ed\u30b0\u30a4\u30f3\u3057\u306a\u3044\u5834\u5408\u306f\u660e\u793a\u7684\u306b\u30ed\u30b0\u30a2\u30a6\u30c8\n## \u5358\u306b Cookie \u3092\u6d88\u53bb\u3059\u308b\u3060\u3051\u3060\u3068 Twitter \u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u6b8b\u308a\u7d9a\u3051\u3066\u3057\u307e\u3046\n## \u30ed\u30b0\u30a2\u30a6\u30c8\u5f8c\u306f\u3001\u53d6\u5f97\u3057\u305f Cookie \u306f\u518d\u5229\u7528\u3067\u304d\u306a\u304f\u306a\u308b\nauth_handler.logout()\nos.unlink('cookie.pickle')\n```\n\n## License\n\n[MIT License](License.txt)\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Twitter Web App (Web \u7248\u516c\u5f0f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8) \u306e\u5185\u90e8 API \u3092\u4f7f\u3044\u3001Tweepy \u3067\u30b9\u30af\u30ea\u30fc\u30f3\u30cd\u30fc\u30e0\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u8a8d\u8a3c\u3059\u308b\u305f\u3081\u306e\u30e9\u30a4\u30d6\u30e9\u30ea",
    "version": "1.5.6",
    "project_urls": {
        "Documentation": "https://github.com/tsukumijima/tweepy-authlib",
        "Issues": "https://github.com/tsukumijima/tweepy-authlib/issues",
        "Source": "https://github.com/tsukumijima/tweepy-authlib"
    },
    "split_keywords": [
        "library",
        " tweepy",
        " twitter"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "939ac089955bd703a777079a87189f8d5aab8ebf5b71dbfb0a2e7fa7680daba7",
                "md5": "a2991ec5c94caa175787b0e4d8802cca",
                "sha256": "8dbc29e5bad18e73c1d402369419ce7c159812a765e3ebd29e71cd8c7b2c0204"
            },
            "downloads": -1,
            "filename": "tweepy_authlib-1.5.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a2991ec5c94caa175787b0e4d8802cca",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 15431,
            "upload_time": "2024-12-07T20:20:27",
            "upload_time_iso_8601": "2024-12-07T20:20:27.721129Z",
            "url": "https://files.pythonhosted.org/packages/93/9a/c089955bd703a777079a87189f8d5aab8ebf5b71dbfb0a2e7fa7680daba7/tweepy_authlib-1.5.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e9ab92ddfcee967a408ed77efdde46bb29676933db3d2a912775fd26fec197c6",
                "md5": "f12793fd2d2197baf0a4b4d2ebcc571b",
                "sha256": "3d6dfbf8940f44137507a443512cfe0f85989fd7bed4065a8653d0ea84d9a9f3"
            },
            "downloads": -1,
            "filename": "tweepy_authlib-1.5.6.tar.gz",
            "has_sig": false,
            "md5_digest": "f12793fd2d2197baf0a4b4d2ebcc571b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 19046,
            "upload_time": "2024-12-07T20:20:26",
            "upload_time_iso_8601": "2024-12-07T20:20:26.481088Z",
            "url": "https://files.pythonhosted.org/packages/e9/ab/92ddfcee967a408ed77efdde46bb29676933db3d2a912775fd26fec197c6/tweepy_authlib-1.5.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-07 20:20:26",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "tsukumijima",
    "github_project": "tweepy-authlib",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "tweepy-authlib"
}
        
Elapsed time: 0.42331s