зеркало из
https://github.com/viginum-datalab/twscrape.git
synced 2025-10-29 21:16:25 +02:00
add login method to account_pool; update readme
Этот коммит содержится в:
родитель
410a1fb9a4
Коммит
01d59b50d6
46
readme.md
46
readme.md
@ -4,21 +4,51 @@ Twitter GraphQL and Search API implementation with [SNScrape](https://github.com
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
from twapi.client import UserClient
|
from twapi.account import Account
|
||||||
from twapi.pool import AccountsPool
|
from twapi.accounts_pool import AccountsPool
|
||||||
from twapi.search import Search
|
from twapi.api import API
|
||||||
|
from twapi.utils import gather
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
acc1 = UserClient("user1", "pass1", "user1@example.com", "email_pass1")
|
acc1 = Account("user1", "pass1", "user1@example.com", "email_pass1")
|
||||||
acc2 = UserClient("user2", "pass2", "user2@example.com", "email_pass2")
|
acc2 = Account("user2", "pass2", "user2@example.com", "email_pass2")
|
||||||
|
|
||||||
pool = AccountsPool()
|
pool = AccountsPool()
|
||||||
pool.add_account(acc1)
|
pool.add_account(acc1)
|
||||||
pool.add_account(acc2)
|
pool.add_account(acc2)
|
||||||
|
|
||||||
search = Search(pool)
|
# login all accounts if required (not account file found)
|
||||||
async for rep in search.query("elon musk"):
|
# session file will be saved to `accounts/{username}.json`
|
||||||
print(rep.status_code, rep.json())
|
await pool.login()
|
||||||
|
|
||||||
|
api = API(pool)
|
||||||
|
|
||||||
|
# search api
|
||||||
|
await gather(api.search("elon musk", limit=20)) # list[Tweet]
|
||||||
|
|
||||||
|
# graphql api
|
||||||
|
tweet_id = 20
|
||||||
|
user_id, user_login = 2244994945, "twitterdev"
|
||||||
|
|
||||||
|
await api.tweet_details(tweet_id) # Tweet
|
||||||
|
await gather(api.retweeters(tweet_id, limit=20)) # list[User]
|
||||||
|
await gather(api.favoriters(tweet_id, limit=20)) # list[User]
|
||||||
|
|
||||||
|
await api.user_by_id(user_id) # User
|
||||||
|
await api.user_by_login(user_login) # User
|
||||||
|
await gather(api.followers(user_id, limit=20)) # list[User]
|
||||||
|
await gather(api.following(user_id, limit=20)) # list[User]
|
||||||
|
await gather(api.user_tweets(user_id, limit=20)) # list[Tweet]
|
||||||
|
await gather(api.user_tweets_and_replies(user_id, limit=20)) # list[Tweet]
|
||||||
|
|
||||||
|
# note 1: limit is optional, default is -1 (no limit)
|
||||||
|
# note 2: all methods have `raw` version e.g.:
|
||||||
|
|
||||||
|
async for tweet in api.search("elon musk"):
|
||||||
|
print(tweet.id, tweet.user.username, tweet.rawContent) # tweet is `Tweet` object
|
||||||
|
|
||||||
|
async for rep in api.search_raw("elon musk"):
|
||||||
|
print(rep.status_code, rep.json()) # rep is `httpx.Response` object
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import asyncio
|
|||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from .account import Account
|
from .account import Account, Status
|
||||||
|
|
||||||
|
|
||||||
class AccountsPool:
|
class AccountsPool:
|
||||||
@ -12,7 +12,16 @@ class AccountsPool:
|
|||||||
def add_account(self, account: Account):
|
def add_account(self, account: Account):
|
||||||
self.accounts.append(account)
|
self.accounts.append(account)
|
||||||
|
|
||||||
def get_login_by_token(self, auth_token: str) -> str:
|
async def login(self):
|
||||||
|
for x in self.accounts:
|
||||||
|
try:
|
||||||
|
if x.status == Status.NEW:
|
||||||
|
await x.login()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error logging in to {x.username}: {e}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_username_by_token(self, auth_token: str) -> str:
|
||||||
for x in self.accounts:
|
for x in self.accounts:
|
||||||
if x.client.cookies.get("auth_token") == auth_token:
|
if x.client.cookies.get("auth_token") == auth_token:
|
||||||
return x.username
|
return x.username
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from .models import Tweet, User
|
|||||||
from .utils import encode_params, find_item, to_old_obj, to_search_like
|
from .utils import encode_params, find_item, to_old_obj, to_search_like
|
||||||
|
|
||||||
|
|
||||||
class Search:
|
class API:
|
||||||
def __init__(self, pool: AccountsPool):
|
def __init__(self, pool: AccountsPool):
|
||||||
self.pool = pool
|
self.pool = pool
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ class Search:
|
|||||||
ll = rep.headers.get("x-rate-limit-limit", -1)
|
ll = rep.headers.get("x-rate-limit-limit", -1)
|
||||||
|
|
||||||
auth_token = rep.request.headers["cookie"].split("auth_token=")[1].split(";")[0]
|
auth_token = rep.request.headers["cookie"].split("auth_token=")[1].split(";")[0]
|
||||||
username = self.pool.get_login_by_token(auth_token)
|
username = self.pool.get_username_by_token(auth_token)
|
||||||
|
|
||||||
return f"{username} {lr}/{ll}"
|
return f"{username} {lr}/{ll}"
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Any, TypeVar
|
from typing import Any, AsyncGenerator, TypeVar
|
||||||
|
|
||||||
from httpx import HTTPStatusError, Response
|
from httpx import HTTPStatusError, Response
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@ -8,6 +8,13 @@ from loguru import logger
|
|||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
async def gather(gen: AsyncGenerator[T, None]) -> list[T]:
|
||||||
|
items = []
|
||||||
|
async for x in gen:
|
||||||
|
items.append(x)
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
def raise_for_status(rep: Response, label: str):
|
def raise_for_status(rep: Response, label: str):
|
||||||
try:
|
try:
|
||||||
rep.raise_for_status()
|
rep.raise_for_status()
|
||||||
|
|||||||
Загрузка…
x
Ссылка в новой задаче
Block a user