@the-convocation/twitter-scraper

repository·main·Indexed 20 days ago

https://github.com/the-convocation/twitter-scraper

A Node.js port of n0madic/twitter-scraper that reverse-engineers Twitter's frontend API to allow scraping tweets without official API tokens. It includes features for bypassing Cloudflare bot detection via CycleTLS, cookie-based authentication to avoid 'error 399' suspicious activity blocks, and support for custom RateLimitStrategy and fetch overrides for Edge runtimes. The library supports 2FA via TOTP secrets and provides utilities for CORS proxy integration in browser environments.

Tokens
10.9K
Snippets
42
Records
49
Agent score
70%

What's inside @the-convocation/twitter-scraper

  1. Authenticate using browser cookies

    main

    To avoid anti-bot protection or error 399 during login, you can use cookies exported from an authenticated browser session.

    1. Export Cookies:
      • Chrome/Edge: Copy all cookies from the Application tab in DevTools as a semicolon-separated string (name1=value1; name2=value2; ...).
      • Firefox: Find ct0 and auth_token in the Storage tab and construct the string: ct0=<value>; auth_token=<value>.
    2. Apply Cookies: Use scraper.setCookies(cookies) where cookies is an array of tough-cookie Cookie objects.
    import { Cookie } from 'tough-cookie';
    import { Scraper } from '@the-convocation/twitter-scraper';
    
    // Your cookie string from browser (name=value; name2=value2; ...)
    const cookieString = 'ct0=abc123; auth_token=xyz789; lang=en; ...';
    
    // Parse the cookie string
    const cookies = cookieString
      .split(';')
      .map((c) => Cookie.parse(c))
      .filter(Boolean);
    
    // Create scraper and set cookies
    const scraper = new Scraper();
    await scraper.setCookies(cookies);
    
    // Verify authentication works
    const isLoggedIn = await scraper.isLoggedIn();
    if (isLoggedIn) {
      console.log('✓ Successfully authenticated with cookies!');
      const profile = await scraper.getProfile('username');
    }
  2. Run the React integration example

    main

    To run the React integration example, you must first set up a CORS proxy because Twitter's CORS headers prevent direct API calls from external websites.

    1. Configure Environment Variables: Copy .env.example to .env.local and update the environment variables with your account credentials if necessary.
    2. Start the CORS Proxy: Navigate to the cors-proxy example folder and run:
      yarn start
    3. Start the React App: In the react-integration folder, start the Vite development server by running:
      yarn dev
    # In cors-proxy folder
    yarn start
    
    # In react-integration folder
    yarn dev
  3. Bypass Cloudflare bot detection using CycleTLS

    main

    When authenticating with Twitter, standard Node.js TLS handshakes may trigger 403 Forbidden errors because Cloudflare detects non-browser clients via TLS fingerprinting. To bypass this, you can use the @the-convocation/twitter-scraper/cycletls entrypoint, which provides a cycleTLSFetch implementation that mimics Chrome browser TLS fingerprints.

    import { Scraper } from '@the-convocation/twitter-scraper';
    import { cycleTLSFetch, cycleTLSExit } from '@the-convocation/twitter-scraper/cycletls';
    
    const scraper = new Scraper({
      fetch: cycleTLSFetch,
    });
  4. Use the Scraper in a browser environment with a CORS proxy

    main

    Because the Twitter API does not have permissive CORS headers, browser-based applications must use a CORS proxy. You can configure this by providing a transform.request function in the Scraper options to intercept and mutate requests.

    corsproxy.io is a recommended public proxy. Note that corsproxy.org is currently reported as not working with this package.

    const scraper = new Scraper({
      transform: {
        request(input: RequestInfo | URL, init?: RequestInit) {
          if (input instanceof URL) {
            const proxy = 'https://corsproxy.io/?' + encodeURIComponent(input.toString());
            return [proxy, init];
          } else if (typeof input === 'string') {
            const proxy = 'https://corsproxy.io/?' + encodeURIComponent(input);
            return [proxy, init];
          } else {
            throw new Error('Unexpected request input type');
          }
        },
      },
    });
  5. Configure custom fetch for Edge runtimes

    main

    Edge runtimes (like Cloudflare Workers) may have fetch implementations that differ from the web standard. You can override the scraper's fetch by passing a custom function in the Scraper options. If the custom fetch has incompatible types, you may need to wrap it in a shim to ensure it returns a web-compliant Response.

    // Basic override
    const scraper = new Scraper({
      fetch: fetch,
    });
    
    // Using a shim for type compatibility
    const scraper = new Scraper({
      fetch: (input, init) => {
        // Transform input and init into your function's expected types...
        return fetch(input, init).then((res) => {
          // Transform res into a web-compliant response...
          return res;
        });
      },
    });
  6. How the FlowSubtaskHandlerApi works

    main

    The FlowSubtaskHandlerApi is the interface provided to custom subtask handlers to allow them to progress the authentication flow. It contains two primary methods:

    1. sendFlowRequest(request: TwitterUserAuthFlowRequest): Sends a request to the Twitter API to move to the next step. The request must include the current flow_token and an array of subtask_inputs.
    2. getFlowToken(): Returns the current flow_token required for subsequent requests in the sequence.
  7. Understand the Profile data structure

    main

    The Profile interface represents the cleaned and parsed user data returned by the scraper. It abstracts away the complex and nested structure of the raw Twitter API response.

    Key Fields:

    • userId: The unique string identifier for the user.
    • username: The user's screen name.
    • name: The user's display name.
    • avatar: URL to the original size profile image (removes _normal suffix).
    • banner: URL to the profile banner image.
    • biography: The user's profile description.
    • joined: A Date object representing the account creation date.
    • followersCount, followingCount, friendsCount, mediaCount, statusesCount, likesCount, listedCount: Numeric counts for user engagement.
    • isPrivate: Boolean indicating if the account is protected.
    • isVerified: Boolean indicating if the account is verified.
    • isBlueVerified: Boolean indicating if the account has X Premium (Blue) verification.
    • location: The location string from the profile.
    • url: The profile's X.com URL.
    • website: The first expanded URL found in the user's profile entities.
    • canDm: Boolean indicating if the user allows direct messages.
  8. Authenticate using login or cookies

    main

    To access protected features like searching or viewing liked tweets, you must authenticate.

    Method 1: Username and Password

    Use login() to authenticate with credentials. This supports email confirmation and 2FA.

    await scraper.login('username', 'password', 'email@example.com', '2fa_secret');

    Method 2: Session Cookies

    Use setCookies() to inject an existing session. This is useful for bypassing login flows or using sessions exported from a browser.

    • You can pass an array of string (raw cookie strings) or Cookie objects (from tough-cookie).
    • Note: The auth_token cookie is mandatory for authenticated access. Since it is HttpOnly, you must export it manually via browser DevTools or an extension.
    await scraper.setCookies(['auth_token=value; domain=.x.com; ...']);

    Logout

    Use logout() to clear the current session and revert to guest authentication.

    await scraper.login('my_user', 'my_password');
    // or
    await scraper.setCookies(['auth_token=xyz123; domain=.x.com']);
  9. Configure custom RateLimitStrategy

    main

    By default, the scraper uses WaitingRateLimitStrategy, which waits for the rate-limiting period to expire. This can take a long time. You can implement the RateLimitStrategy interface to define custom behavior when a rate limit event occurs.

    Available built-in strategies:

    • WaitingRateLimitStrategy: The default (waits for expiry).
    • ErrorRateLimitStrategy: Throws an error immediately upon a rate-limit event.
    import { Scraper, RateLimitStrategy } from '@the-convocation/twitter-scraper';
    
    class CustomRateLimitStrategy implements RateLimitStrategy {
      async onRateLimit(event: RateLimitEvent): Promise<void> {
        // your own logic...
      }
    }
    
    const scraper = new Scraper({
      rateLimitStrategy: new CustomRateLimitStrategy(),
    });