зеркало из
				https://github.com/viginum-datalab/twscrape.git
				synced 2025-10-31 05:56:24 +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
	 Vlad Pronsky
						Vlad Pronsky