puppeteer-extra icon indicating copy to clipboard operation
puppeteer-extra copied to clipboard

Tiktok detecting puppeteer-extra-stealth-plugin in headful

Open tzvc opened this issue 1 year ago • 5 comments

Hey there,

I'm exploring the possibility of automating video posting on TikTok by using the web version. I've created a puppeteer script that logs in, goes to the upload page, upload a video and then attempts to post it.

Everything works fine until I try to click the final "post" button. In a real browser environment, when I click this button, the post is sent successfully. However, when running in a browser controlled via Puppeteer, clicking this button doesn't do anything. This leads me to think that tiktok is detecting this as a bot and blocking the posting action.

Things to note, the upload form is embedded inside an iframe in the page.

Has anyone managed to trigger a post action on titkok using puppeteer? And if not, what could I do to further investigate the problem?

Here's my script:

import puppeteer from "puppeteer-extra";

// add stealth plugin and use defaults (all evasion techniques)
import stealthPlugin from "puppeteer-extra-plugin-stealth";
puppeteer.use(stealthPlugin());

const email = "a_username";
const password = "a_password";
const videoPath = "in.mp4";

// wait for post button to be clickable, then click it to post video
const sendPost = async (page, tryCount = 1) => {
  try {
    const sendPostSelector = ".btn-post:not(.disabled)";
    await page.waitForSelector(sendPostSelector);

    console.log(">>> load video success");
    console.log(">>> try send post: ", tryCount);
    // await page.click(sendPostSelector);
    await page.hover(sendPostSelector);
    console.log(">>> hover success");

    await page.evaluate(() => {
      function emulateButtonClick(selector) {
        // Get the button element by its ID
        var button = document.querySelector(selector);

        // Check if the button exists
        if (!button) {
          console.log(`No element with id ${selector} found.`);
          return;
        }

        // Create a new 'click' event
        var mouseEnterEvent = new MouseEvent("mouseover", {
          view: window,
          bubbles: true,
          cancelable: true,
        });
        var clickEvent = new MouseEvent("click", {
          view: window,
          bubbles: true,
          cancelable: true,
        });

        // Dispatch the event on the button
        button.dispatchEvent(mouseEnterEvent);
        button.dispatchEvent(clickEvent);
      }

      emulateButtonClick(".btn-post");
    });
    console.log(">>> click success");
  } catch (error) {
    console.log("error: ", error);
    if (tryCount === 4) {
      console.log(">>> skip profile");
      return;
    }
    await new Promise((resolve) => setTimeout(resolve, 1000));
    tryCount += 1;
    await sendPost(page, tryCount);
  }
};

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
  });
  const page = await browser.newPage();
  const timeout = 15000;
  page.setDefaultTimeout(timeout);

  await page.goto("https://www.tiktok.com/login/phone-or-email/email");
  await page.type("input[name=username]", email, { delay: 100 });
  await page.type("input[placeholder=Password]", password, { delay: 100 }); // log in w email and password

  await page.evaluate(() => {
    // @ts-ignore
    document.querySelector("button[type=submit]").click();
  }); // press login button

  await page.waitForNavigation(); // wait for page to load
  await page.goto("https://www.tiktok.com/upload?lang=en"); // go to upload page

  // Upload form is embedded in an iframe
  console.log("waiting for iframe with form to be ready.");
  const iframe = await page.waitForSelector("iframe");
  const formFrame = await iframe.contentFrame();
  console.log(formFrame);

  await new Promise((resolve) => setTimeout(resolve, 5000)); // wait for page to load
  await formFrame.click(".upload-card");
  const [fileChooser] = await Promise.all([
    page.waitForFileChooser(),
    formFrame.click(".upload-card"),
  ]);

  await fileChooser.accept([videoPath]);

  // send post
  await new Promise((resolve) => setTimeout(resolve, 10000)); // wait for page to load
  await sendPost(formFrame);

  await browser.close();
})().catch((err) => {
  console.error(err);
  process.exit(1);
});

Versions

System: OS: macOS 13.3 CPU: (8) arm64 Apple M2 Memory: 106.81 MB / 8.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 16.20.0 - /usr/local/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 9.6.6 - /usr/local/bin/npm npmPackages: puppeteer: ^20.3.0 => 20.3.0 puppeteer-extra: ^3.3.6 => 3.3.6 puppeteer-extra-plugin-stealth: ^2.11.2 => 2.11.2

tzvc avatar May 31 '23 08:05 tzvc

possible is chrome upgrade to 114

ganguoyin avatar Jun 04 '23 01:06 ganguoyin

@ganguoyin what to you mean?

tzvc avatar Jun 05 '23 16:06 tzvc

@ganguoyin what to you mean?

The user above meant that you should update your chromium to the latest version which is now 116 i believe

lindo8818 avatar Sep 16 '23 13:09 lindo8818

Solution: Use latest Chrome version and avoid using Chrome Test build which Puppeteer installs/uses by default.

sapn1s avatar Sep 22 '23 22:09 sapn1s

I'm having the exact same problem... Login and upload works fine until I try to click the "Post" button. Have you found a fix? Puppeteer is on version 117.0.5938.92, so that is not the issue...

ReinhardRanner avatar Oct 01 '23 20:10 ReinhardRanner