Scweet

AltimisMIT活跃维护

无需 API Key 的 X/Twitter 数据采集库,支持抓取推文、用户资料、粉丝和关注列表,内置多账号池、代理和异步。

Twitter scraper for X: tweets, profiles, followers and following, no API key needed. Python library with multi-account pooling, proxy support and async, or a hosted version on Apify.

v5.8.2发布于 2026-10-22 14:41 UTC1,660star286fork同步于 2026-11-10 11:47 UTC

怎么上手

由 README 提炼,执行前请确认
pip install -U Scweet
from Scweet import Scweet
s = Scweet(auth_token="YOUR_AUTH_TOKEN")
tweets = s.search("bitcoin", limit=100)
硬件要求按 README 说明

README 未说明,纯 Python 库,无 GPU 或显存要求

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

无需官方 API 就能抓取 X 的推文、用户和关注关系,支持多账号池与代理

无需 API Key 的 X/Twitter 数据采集库,支持抓取推文、用户资料、粉丝和关注列表,内置多账号池、代理和异步。

核心能力

  • 用 auth_token 登录态直接抓取,无需申请官方 API key
  • 支持多账号池与每账号独立或轮换代理,降低封号风险
  • 读取 X 的限流响应头,在限额前 2 次请求主动停用该账号
  • 按日期区间并行拆分抓取,中断后可用 resume 续跑
  • 提供 12 种读取操作,同步与 async 双版本,内置 CSV/JSON 输出

容易踩的坑

必须自己从浏览器 Cookie 里复制 auth_token,且要用专用小号而非个人主账号;不设 limit 会一直跑到账号当日上限;大批量抓取需自备代理,否则单 IP 高频请求易被限流或封号

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v5.8.2
  1. v5.8.2Scweet 5.8.2最新正式版2026-10-22 · 2026-10-22 14:41 UTC

    A proxy with {session} now replaces an exit IP that fails, so one dead exit no longer costs a profile its rows. Every change is additive: no method and no default of 5.8.1 changes, and one field is new.

    Fixed

    • With {session} in the proxy URL, a proxy timeout no longer ends a profile timeline, a follower list or a lookup with no rows. These paths send each request on one session of one account, and a request that failed in the network returned 0 rows at once. The profile timeline also reported the target as complete. A residential exit IP that times out stays dead for its session, so a plain repeat fails again. Now, when the proxy URL holds {session}, a request that fails in the network retries on the same account up to network_error_retries times (default 2). Each retry uses a new session token, so it leaves from a new exit IP, and the session keeps its cookies. A proxy URL without {session}, for example a static datacenter proxy, keeps the earlier behaviour.
  2. v5.8.12026-09-29 · 2026-09-29 13:34 UTC
    查看更新说明

    X now serves the page that the request signature needs only to a request with the cookies of an account that X accepts. This release follows that change. Every change is additive: no method, no field and no default of 5.8.0 changes.

    Fixed

    • A fresh auth_token works again when the state file also holds an old one (#197). The bootstrap of the x-client-transaction-id header read the first account row of the state file, with no check. A row with a revoked token stays in the file as unusable; X answers its login page to that row, the header was never built, and every request of the fresh account answered 404. The bootstrap now skips a row marked unusable, reads the newest usable account first, and tries the next account when X rejects one.
    • A rejected auth_token names itself (#197). X answers its login page to a token that it does not accept, and that page still sets cookies. The import counted those cookies as a success and stored the reason missing_csrf. The reason is now rejected_auth_token, the warning says what to do, and a run that fails on 404 without the header explains the cause in its summary instead of ending as an expired cookie.
    • manifest_scrape_on_init=True and refresh_manifest() give a manifest that X accepts (#197). The scrape opened a session with no cookies and no proxy, and X answers its login page to such a request. It also extracted the feature switches of each operation and then stored the defaults in their place; with a fresh query id, X answers 404 to a request whose switches are not the ones the operation declares. The scrape now sends the cookies of a usable account, uses the configured proxy, and keeps the switches of each operation.

    Full changelog: https://github.com/Altimis/Scweet/blob/master/CHANGELOG.md

  3. v5.8.02026-09-15 · 2026-09-15 13:34 UTC
    查看更新说明

    This release removes the silent failures a user could meet, makes the first run obvious, and adds the newest versions of Python. Every change is additive: no method, no field and no default of 5.7.x changes.

    Fixed

    • A run no longer fails silently when the request signature cannot build. X answers HTTP 404 for a GraphQL request without the x-client-transaction-id header, and that 404 describes the request, not the account. The build of that header needs a live page of X; a transient network failure of that page left the header absent, and the run then rested the accounts one by one until it ended with no clear cause. Now the build retries with a backoff, a 404 rebuilds the header and retries the request on the same account before any cooldown, and a request that still cannot carry the header logs the reason.
    • A wrong or expired auth_token now reports at once. Before, the constructor gave no warning and the first call failed with a message about cooldowns. Provisioning now counts the usable accounts, and the constructor warns when none is usable and names the likely cause.
    • A cookies_file accepts every shape that an inline cookies= accepts, including a file exported by a browser cookie extension. The two paths share one loader, so the same data gives the same accounts.
    • get_tweet_info, get_trending and get_user_info(user_ids=...) raise a clear error on a refusal. A missing or deleted item still gives no row and no error, but a transport failure or a non-200 status no longer returns a silent empty list.

    Added

    • Python 3.13 and 3.14 are supported and tested. The test suite runs on Python 3.9 through 3.14.
    • The package ships py.typed. A type checker of a project that uses Scweet now reads the type hints of the library.
    • New ScweetConfig fields for the request signature: transaction_init_attempts (3), transaction_init_backoff_s (1.5), and request_404_retries (1). A TransactionIdProvider.refresh() method rebuilds the header on demand.

    Changed

    • The README quickstart and the documentation teach the same first path: one auth_token, one call, one result. The quickstart code block runs as written, with no placeholder proxy. The example scripts open with that path and show the full read surface.

    Full changelog: https://github.com/Altimis/Scweet/blob/master/CHANGELOG.md

  4. v5.7.02026-09-14 · 2026-09-14 15:25 UTC
    查看更新说明

    The read surface grows from 6 operations to 13, and the query-id refresh now covers every one of them. Every change is additive: a script written against 5.6.x reads the same values from the same fields.

    Added

    • get_tweet_info(tweet_ids) — full tweet records for ids you already hold, up to 50 ids in one request. A deleted id gives no row and no error.
    • get_tweet_replies(tweet_id, limit=...) — the replies under one tweet, as full tweet records, without the focal tweet itself.
    • get_reposters(tweet_id, limit=...) — the accounts that reposted one tweet, as user records.
    • get_user_info(user_ids=[...]) — profile lookup by numeric id, up to 100 ids in one request; combines with lookup by username.
    • search_users(query, limit=...) — the "People" tab of a search, as user records.
    • get_trending() — the trends of the explore page: the name, the context line with the post count, and the raw item.
    • get_profile_media(users, limit=...) — only the tweets of a profile that carry an image or a video.
    • get_profile_tweets(..., include_replies=True) — the timeline with the replies of the profile included.
    • refresh_manifest() and the CLI command scweet refresh-manifest — read fresh GraphQL query ids from the live bundles of X at any moment, and report each id that rotated. A stale id answers 404 for every request, so call this when requests start to fail.
    • Every new method has an async twin and a CLI subcommand: tweet-info, tweet-replies, reposters, search-users, trending, profile-media, profile-tweets --include-replies, user-info --ids.

    Changed

    • The query-id scrape now reaches the operations that main.js does not carry. Some operations live in lazy chunk files. The scrape reads the chunk maps that the page of X embeds, sweeps the likely chunks first, and stops at the find — measured: the missing id was found within 9 small fetches. Before, an id outside main.js could never heal.
    • The bundled default ids were refreshed from the live bundle on 2026-09-13, and each new endpoint ships with its measured request allowance.

    Notes

    • X sometimes refuses the lookup by numeric id for a whole connection with HTTP 403. The call then raises an error that names the refusal and the username alternative, instead of returning an empty list.
    • A new test compares the CLI against the client, so a future method cannot ship without its subcommand.

    Full changelog: https://github.com/Altimis/Scweet/blob/master/CHANGELOG.md

  5. v5.6.02026-09-13 · 2026-09-13 19:18 UTC
    查看更新说明

    Every change below is additive. A script written against 5.5.x reads the same values from the same fields.

    Fixed

    • A profile reported 0 followers, 0 following and 0 tweets. X stopped sending the flat legacy object of a user and moved the counts into relationship_counts, tweet_counts and action_counts. The parser read only the old place, so get_user_info, get_followers, get_following and get_verified_followers returned a zero for every count. Captured 2026-09-13: one profile with 241,659,598 followers reported 0. The parser now reads the old place first and the new nodes after it, so an older answer of X keeps working. A real zero stays a zero.
    • A profile record now carries its banner image again, and the links inside the bio, for the same reason.

    Added

    • get_verified_followers() / aget_verified_followers() and the CLI command verified-followers. The engine already supported the endpoint of X for the verified followers of a profile, and no public method reached it.
    • A tweet record carries the fields that X already sends: views, quotes, bookmarks, lang, hashtags, mentions, urls, in_reply_to_tweet_id, in_reply_to_user, is_quote and is_retweet. No extra request: the data was in the raw payload of every row.
    • quoted_tweet and retweeted_tweet hold the embedded tweet as a full tweet record, one level deep. embedded_text keeps its old value, which is the truncated text that X sends, so it can be shorter than the text of the nested record.
    • media.video_links holds the highest-bitrate MP4 of each video. An m3u8 playlist is a stream index and not a file to save, so it is left out.
    • A user record carries identity_verified, pinned_tweet_ids and description_urls.

    Changed

    • A count that X does not send reads None instead of 0, so a caller can tell "unknown" from "zero". The counts likes, retweets and comments keep their old default of 0.

    Full changelog: https://github.com/Altimis/Scweet/blob/master/CHANGELOG.md

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Two ways to run it

    Both give you the same data. Hosted: you press a button and get the results. Self-hosted: you run the code, and you manage the accounts and the proxies.

    1. Hosted : nothing to manage ⭐

    Runs on Apify. You do not need X accounts, cookies or proxies.

    • Configure and run from your browser, no code needed, or drive it from code with the Python API
    • Search tweets, profile timelines, follower / following lists
    • Monitor accounts, a brand or a token on a schedule, with no server to run
    • Export in different formats (JSON, CSV or XLSX)
    • Free tier included; pay-as-you-go pricing is on the Actor page

    2. Self-hosted : full control

    Bring your own X accounts and proxies. Run it from Python or the CLI, inside your own infrastructure.

    from Scweet import Scweet
    
    s = Scweet(auth_token="YOUR_AUTH_TOKEN")
    tweets = s.search("bitcoin", limit=100)
    

    Scweet works with any proxy. A proxy is optional for a small run and recommended for real volume, because many requests from one IP raise the risk to an account. Put the values of your proxy in place of each value that starts with YOUR_:

    s = Scweet(auth_token="YOUR_AUTH_TOKEN",
               proxy="http://YOUR_LOGIN:YOUR_PASSWORD@YOUR_HOST:YOUR_PORT")
    

    With several accounts or a rotating proxy, see Multiple accounts & proxies.

    Our proxy sponsor, Proxy-Seller, gives 15% off residential, ISP and IPv4 proxies with the code SCWEET.


    What you can scrape

    • Tweets — by keyword, hashtag, user, date range, engagement filters, language, location
    • Profile timelines — a user's full tweet history, with replies and media tabs
    • Tweets by id — full data for known tweet ids, their replies, and who reposted them
    • Followers / Following — full account lists at scale, verified followers included
    • User profiles — bio, follower count, verification status, by handle or by id
    • People search & trends — find accounts by keyword, read what is trending now

    Python Quickstart

    Three steps from nothing to tweets. One account, one call, one result.

    1. Install

    pip install -U Scweet
    

    2. Get your auth_token

    Log into x.com → DevTools F12 → Application → Cookies → https://x.com → copy the auth_token value. That one value is all you need; Scweet builds the rest.

    Use a dedicated account, never your personal one.

    3. Scrape

    from Scweet import Scweet
    
    s = Scweet(auth_token="YOUR_AUTH_TOKEN")
    tweets = s.search("bitcoin", limit=100)
    
    for tweet in tweets:
        print(tweet["text"])
    

    That is the whole first run. On the next run, reuse the same accounts with no credentials:

    s = Scweet()                       # reads the state file scweet_state.db
    tweets = s.search("ethereum", limit=100, save=True)   # save=True writes a CSV
    

    Always set limit. Without it, a run continues until the daily cap of the account.

    Next: add proxies and several accounts for real volume, or read the full documentation.

    All methods have async variants: asearch(), aget_profile_tweets(), aget_followers(), etc.


    Common tasks

    Search tweets

    tweets = s.search(
        "AI agents",
        since="2026-01-01",
        from_users=["elonmusk"],
        min_likes=50,
        limit=200,
        save=True,
    )
    

    Profile timeline

    tweets = s.get_profile_tweets(["elonmusk"], limit=200)
    

    Followers / Following

    followers = s.get_followers(["elonmusk"], limit=1000)
    following = s.get_following(["elonmusk"], limit=1000)
    verified  = s.get_verified_followers(["elonmusk"], limit=1000)
    

    User profiles

    profiles = s.get_user_info(["githubstatus", "elonmusk"])
    profiles = s.get_user_info(user_ids=["44196397"])   # numeric ids work too
    

    Tweets by id, replies, reposters

    tweets    = s.get_tweet_info(["1866123456789", "1866123456790"])  # up to 50 per request
    replies   = s.get_tweet_replies("1866123456789", limit=100)
    reposters = s.get_reposters("1866123456789", limit=100)
    

    People search, trends, media

    people = s.search_users("python developer", limit=50)
    trends = s.get_trending()
    media  = s.get_profile_media(["nasa"], limit=100)
    tweets = s.get_profile_tweets(["nasa"], limit=100, include_replies=True)
    

    For the full list of supported search operators, see twitter-advanced-search.


    CLI

    # Search with proxy, save to CSV
    scweet --auth-token YOUR_AUTH_TOKEN --proxy http://YOUR_LOGIN:YOUR_PASSWORD@YOUR_HOST:YOUR_PORT \
      search "bitcoin" --since 2026-01-01 --limit 500 --save
    
    # Followers, saved as JSON
    scweet --auth-token YOUR_AUTH_TOKEN \
      followers elonmusk --limit 1000 --save --save-format json
    

    For multi-account runs, use --cookies-file cookies.json.


    Multiple accounts & proxies

    For higher throughput and reduced ban risk, use several dedicated accounts. List them in cookies.json, and replace each value that starts with YOUR_.

    A fixed proxy for each account, for example datacenter or ISP proxies:

    [
      { "username": "acct1", "cookies": { "auth_token": "YOUR_AUTH_TOKEN_1" },
        "proxy": "http://YOUR_LOGIN:YOUR_PASSWORD@YOUR_HOST_1:YOUR_PORT" },
      { "username": "acct2", "cookies": { "auth_token": "YOUR_AUTH_TOKEN_2" },
        "proxy": "http://YOUR_LOGIN:YOUR_PASSWORD@YOUR_HOST_2:YOUR_PORT" }
    ]
    
    s = Scweet(cookies_file="cookies.json")  # proxies are read from the file, one per account
    

    One rotating proxy for all accounts, for example residential proxies. Leave "proxy" out of cookies.json, and put {session} where your provider names a sticky session. Keep {session} exactly as it is:

    s = Scweet(cookies_file="cookies.json",
               proxy="http://YOUR_LOGIN-session-{session}:YOUR_PASSWORD@YOUR_HOST:YOUR_PORT")
    

    Scweet replaces {session} with the name of the account and 8 random characters, for example YOUR_LOGIN-session-acct1a3f9c2b7 for acct1. Each account therefore keeps its own IP, and a request that fails in the network retries from a new IP. Each provider writes the session in its own way: read Proxy modes.

    Never use your personal Twitter/X account for scraping. Use dedicated accounts only.


    Why Scweet?

    One page of X is easy to fetch. 10,000 tweets is not, because accounts get rate-limited, cookies expire, and X changes its API.

    Your run finishes

    • A wide date range is split into intervals that run in parallel. If X stops one interval early, the others still fill.
    • If X ends a page chain early, Scweet continues from the oldest tweet it received.
    • If a run stops, resume=True continues from the last saved point instead of starting again.

    Your accounts stay usable

    • Scweet reads the rate-limit headers of X and stops each account 2 requests before its limit, so X does not block it.
    • Scweet identifies the cause of each error. A bad request, an old query id and a dead session are handled differently, so a small error does not cost you an account.
    • Scweet records the state of each account (locked, suspended, rate-limited) and waits the needed time before it uses that account again.

    It keeps working when X changes

    • X changes the ids of its internal API without notice. Scweet reads the new ids from the JavaScript files of X, at startup or when you call refresh_manifest().

    It is easy to work with

    • 12 read operations, one consistent row shape, sync and async, a real CLI, and CSV/JSON output built in.
    • 25 fields per tweet, including view counts, quotes, bookmarks, language and the nested quoted or retweeted post.

    Or let us run it for you

    • The hosted version does all of the above on our accounts and proxies. Free trial included.

    Also on twint and snscrape: both are unmaintained and no longer work against X's current backend. If you are migrating from either, Scweet covers the same ground and more.


    Limitations

    • Only publicly visible content is accessible — private/protected accounts are not supported
    • Relies on undocumented X web endpoints; breakage is always possible after platform changes. Scweet self-heals by scraping fresh query IDs and feature flags from X's own bundles — at startup with manifest_scrape_on_init=True, or on demand at any time:
      s = Scweet(auth_token="...", manifest_scrape_on_init=True)
      changes = s.refresh_manifest()   # or: scweet refresh-manifest
      
    • Cookies can expire and may need periodic refreshing
    • A single account typically handles hundreds to a few thousand tweets per day before hitting rate limits; multi-account pooling scales proportionally
    • Proxies and dedicated accounts are strongly recommended for anything beyond light testing
    • Username/password login is not supported. X's anti-automation defenses make programmatic login unreliable and likely to trigger account locks. Scweet authenticates with browser cookies (auth_token) extracted from an active session instead

    Documentation

    Full API reference, config options, structured search filters, async patterns, resume, proxies, and troubleshooting.

    Read it as a site: altimis.github.io/Scweet — the same text, with search and a navigation menu.

    For advanced Twitter search operators, see twitter-advanced-search.


    Does it work without an official Twitter API key? Yes. Scweet calls X's internal GraphQL API — the same one the web app uses. No developer account or API key required.

    Is it a replacement for twint or snscrape? Yes. Both are broken as of 2024–2025. Scweet uses a different, currently-working approach: cookies + GraphQL instead of legacy unauthenticated endpoints.

    How many tweets can I scrape? A single account typically handles hundreds to a few thousand tweets per day before hitting rate limits. Multi-account pooling scales this proportionally. The hosted Apify actor manages accounts and rate limits automatically.

    Will my account get banned? Never use your personal account — use dedicated accounts only. To reduce risk further: use multiple accounts (distributes load) and pair each with a proxy (prevents all requests from one IP).

    Does it work for private accounts? No. Only publicly visible content is accessible.

    Does it still work in 2025 / 2026? Yes — last verified with the 5.8.2 release (2026-10-22) against X's current GraphQL API.


    Community

    Have a question or want to share what you built with Scweet? Open a thread in GitHub Discussions.

    Found it useful? Star the repo


    Contributing

    Bug reports, feature suggestions, and PRs are welcome. See CONTRIBUTING.md.


    MIT License