puppeteer - 操做支付宝报“操做频繁”错误的思考

我这里想要实现的是经过转帐到支付宝的方式判断一个手机号是否注册过支付宝。但查询收款人的网络请求很复杂分析不出来,使用 puppteer 也是没有解决 “操做频繁” 的问题(应该还有不少其余的判断我没有考虑到),这里只是整理记录一下。html

问题分析

转帐时查询用户是否存在会报 “操做频繁”web

alipay-operating-frequency

感受可能检查了无头浏览器,但在 JS 里没有搜索到webdrive。而后经过内存快照找找发现真有webdrive。。(虽然不能百分百肯定无头浏览器,但以后进行了一些欺骗确实有点做用)chrome

alipay-find-webdrive-in-memory

尝试解决

用根据 It is not possible to detect and block Chrome headless 进行欺骗:(加了一条页面是否隐藏,感受这个也可能会检测)浏览器

// We'll use Puppeteer is our browser automation framework.
const puppeteer = require('puppeteer');

// This is where we'll put the code to get around the tests.
const preparePageForTests = async (page) => {
  // Pass the User-Agent Test.
  const userAgent = 'Mozilla/5.0 (X11; Linux x86_64)' +
    'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.39 Safari/537.36';
  await page.setUserAgent(userAgent);

  // Pass the Webdriver Test.
  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, 'webdriver', {
      get: () => false,
    });
  });

  // Pass the Chrome Test.
  await page.evaluateOnNewDocument(() => {
    // We can mock this in as much depth as we need for the test.
    window.navigator.chrome = {
      runtime: {},
      // etc.
    };
  });

  // Pass the Permissions Test.
  await page.evaluateOnNewDocument(() => {
    const originalQuery = window.navigator.permissions.query;
    return window.navigator.permissions.query = (parameters) => (
      parameters.name === 'notifications' ?
        Promise.resolve({ state: Notification.permission }) :
        originalQuery(parameters)
    );
  });

  // Pass the Plugins Length Test.
  await page.evaluateOnNewDocument(() => {
    // Overwrite the `plugins` property to use a custom getter.
    Object.defineProperty(navigator, 'plugins', {
      // This just needs to have `length > 0` for the current test,
      // but we could mock the plugins too if necessary.
      get: () => [1, 2, 3, 4, 5],
    });
  });

  // Pass the Languages Test.
  await page.evaluateOnNewDocument(() => {
    // Overwrite the `plugins` property to use a custom getter.
    Object.defineProperty(navigator, 'languages', {
      get: () => ['zh-CN', 'zh', 'en'],
    });
  });
    
  // 可能会检测页面是否隐藏,处理一下
  await page.evaluateOnNewDocument(() => {
    // Overwrite the `hidden` property
    Object.defineProperty(document, "hidden", {
      get: () => false
    });
    Object.defineProperty(document, "webkitHidden", {
      get: () => false
    });
  });
}

(async () => {
  // Launch the browser in headless mode and set up a page.
  const browser = await puppeteer.launch({
    args: ['--no-sandbox'],
    headless: true,
  });
  const page = await browser.newPage();

  // Prepare for the tests (not yet implemented).
  await preparePageForTests(page);

  // Navigate to the page that will perform the tests.
  const testUrl = 'https://intoli.com/blog/' +
    'not-possible-to-block-chrome-headless/chrome-headless-test.html';
  await page.goto(testUrl);

  // Save a screenshot of the results.
  await page.screenshot({path: 'headless-test-result.png'});

  // Clean up.
  await browser.close()
})();

其余博客有说用下面这种方式去掉webdriver,但我测试很差使:网络

const browser = await puppeteer.launch({ignoreDefaultArgs: ["--enable-automation"]});

PS:到这里问题没有彻底解决,查询一两次就频繁了。最后使用 appium 操做支付宝的 APP 实现的(这种方式也有问题,由于用户能够设置不让经过手机号查找)。。app

拓展

navigator.webdriver属性

有兴趣的能够看看 Chrome 添加该属性的原因:https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/6GysDZCWwB8/rXbGoRohBgAJless

能够根据ChromeStatus得知该属性添加的版本为 63,但这属性须要到在 puppeteer v0.12.0(对应 chromium 版本为 64.0.3240.0 (r508693))中才开始有效,在 v0.11.0(63.0.3205.0 (r499413)) 中还未生效。async

参见:puppeteer 的 PDD 反爬经历 - xzysaber - 博客园测试

经过内存快照能够找到任何混淆过的字符串么?

并不能,必须待查找的内存没有释放(还有指向这个字符串的引用)才能找到。例如:ui

检测不到:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <script>
      (function test() {
        var fun_a = "\167\145\142\144\162\151\166\145\162";
      })();

      ("\167\145\142\144\162\151\166\145\162");

      var a = navigator["\167\145\142\144\162\151\166\145\162"];
      console.log(a);

      var window.b = "\167\145\142\144\162\151\166\145\162";
      delete window.b;
    </script>
  </body>
</html>

能够检测到:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <script>
      var a = "\167\145\142\144\162\151\166\145\162";

      function test() {
        var fun_a = "\167\145\142\144\162\151\166\145\162";
        return navigator[fun_a];
      }
      var b = test();

      //> [...'webdriver'].map((d, i)=>String.fromCharCode(d.charCodeAt(0) + 1)).join('')
      //'xfcesjwfs'
      var other_1 = [..."xfcesjwfs"]
        .map((d, i) => String.fromCharCode(d.charCodeAt(0) - 1))
        .join("");
    </script>
  </body>
</html>