使用Puppeteer和无头Chrome绕过CAPTCHA

59

谷歌发现我的浏览器被软件操纵/控制/自动化,因此我会遇到reCaptcha。当我手动启动Chromium并执行相同步骤时,就不会出现reCaptcha。

问题1)

在使用puppeteer时,是否有可能以编程方式解决验证码或摆脱它?有没有什么方法可以解决这个问题?

问题2)

这只发生在没有headless选项的情况下吗?即

const browser = await puppeteer.launch({
  headless: false
})

这是我们必须接受并继续前进的事实吗?


请查看这篇博客文章,它与您的情况非常接近。https://medium.com/@jsoverson/bypassing-captchas-with-headless-chrome-93f294518337 - Paula Livingstone
我已经看过那篇博客了。他使用的是 2captcha,这不是免费的 :P - rinold simon
2
你所接受的答案是来自2captcha.com的付费服务。如果你想支付,那么为什么要使用Headless Chrome + Puppeteer呢?为什么不直接使用CURL呢? - Cyborg
4个回答

75

尝试使用这个npm包生成随机用户代理。通常可以解决基于用户代理的保护。

在Puppeteer页面中,可以使用 page.setUserAgent 覆盖浏览器用户代理。

var userAgent = require('user-agents');
...
await page.setUserAgent(userAgent.random().toString())

此外,您可以添加这两个额外的插件:

puppeteer-extra-plugin-recaptcha - 使用单行代码 page.solveRecaptchas() 自动解决 reCAPTCHA。

注意: puppeteer-extra-plugin-recaptcha 使用付费服务 2captcha.

puppeteer-extra-plugin-stealth - 应用各种规避技术来使 headless puppeteer 更难被检测到。


4
是的,这不仅仅是一行代码。您还需要注册该服务并为每个解决的验证码付费。 - Tim Kozak

59
这是我绕过验证码和类似拦截的一些方法清单:
  • 启用隐身模式(通过puppeteer-extra-plugin-stealth)
  • 随机化用户代理或设置有效的用户代理(通过random-useragent)
  • 随机化视口大小
  • 跳过图像/样式/字体加载以提高性能
  • 通过"WebDriver检查"
  • 通过"Chrome检查"
  • 通过"通知检查"
  • 通过"插件检查"
  • 通过"语言检查"

完整代码链接在这里

const randomUseragent = require('random-useragent');

//Enable stealth mode
const puppeteer = require('puppeteer-extra')
const StealthPlugin = require('puppeteer-extra-plugin-stealth')
puppeteer.use(StealthPlugin())

const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36';

async function createPage (browser,url) {

    //Randomize User agent or Set a valid one
    const userAgent = randomUseragent.getRandom();
    const UA = userAgent || USER_AGENT;
    const page = await browser.newPage();

    //Randomize viewport size
    await page.setViewport({
        width: 1920 + Math.floor(Math.random() * 100),
        height: 3000 + Math.floor(Math.random() * 100),
        deviceScaleFactor: 1,
        hasTouch: false,
        isLandscape: false,
        isMobile: false,
    });

    await page.setUserAgent(UA);
    await page.setJavaScriptEnabled(true);
    await page.setDefaultNavigationTimeout(0);

    //Skip images/styles/fonts loading for performance
    await page.setRequestInterception(true);
    page.on('request', (req) => {
        if(req.resourceType() == 'stylesheet' || req.resourceType() == 'font' || req.resourceType() == 'image'){
            req.abort();
        } else {
            req.continue();
        }
    });

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

    await page.evaluateOnNewDocument(() => {
        // Pass chrome check
        window.chrome = {
            runtime: {},
            // etc.
        };
    });

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

    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],
        });
    });

    await page.evaluateOnNewDocument(() => {
        // Overwrite the `languages` property to use a custom getter.
        Object.defineProperty(navigator, 'languages', {
            get: () => ['en-US', 'en'],
        });
    });

    await page.goto(url, { waitUntil: 'networkidle2',timeout: 0 } );
    return page;
}

2
这个配置给了我这个信息: “请升级到支持的浏览器以获取reCAPTCHA挑战。” 并且不允许登录。 - Kapitula Alexey
尝试将 const UA = userAgent || USER_AGENT; 更改为 const UA = USER_AGENT; - Tim Kozak
1
谢谢您的回复,我认为其中一些功能已经包含在了 puppeteer-extra-plugin-stealth 中。 - Kevin Danikowski

10

你尝试过设置浏览器代理吗?

await page.setUserAgent('5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36');

不会。但是如果使用相同的UserAgent会发生什么?UserAgent不应该是随机的吗?您能简要说明一下吗? - rinold simon
使用代理作为默认值意味着您正在使用Puppeteer,因此将其设置为Chrome(如上所示)可使您通过基本测试。但是,您最终仍会遇到验证码。如果您登录,这也可能有助于让您的爬虫工作一段时间。 - Hellonearthis
7
即使我们设置了特定的用户代理,某些登录后仍然会遇到验证码。因此我尝试使用npm包(https://www.npmjs.com/package/random-useragent)每次生成随机用户代理。现在它运行良好。 - rinold simon
1
这个回答很好,但是用户代理有点老(69.0.3497在我写这篇答案的时候已经过了几个月),现在最新的是这个:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Brave Chrome/84.0.4147.89 Safari/537.36https://developers.whatismybrowser.com/useragents/explore/software_name/brave/ 如果你想要其他版本。 - Dr4kk0nnys

8

经过几次测试,我发现有几个软件包可以帮助我避免reCAPTCHA:

//const puppeteer = require('puppeteer');
const puppeteerExtra = require('puppeteer-extra');
const pluginStealth = require('puppeteer-extra-plugin-stealth');
const randomUseragent = require('random-useragent');

class PuppeteerService {

    constructor() {
        this.browser = null;
        this.page = null;
        this.pageOptions = null;
        this.waitForFunction = null;
        this.isLinkCrawlTest = null;
    }

    async initiate(countsLimitsData, isLinkCrawlTest) {
        this.pageOptions = {
            waitUntil: 'networkidle2',
            timeout: countsLimitsData.millisecondsTimeoutSourceRequestCount
        };
        this.waitForFunction = 'document.querySelector("body")';
        puppeteerExtra.use(pluginStealth());
        //const browser = await puppeteerExtra.launch({ headless: false });
        this.browser = await puppeteerExtra.launch({ headless: false });
        this.page = await this.browser.newPage();
        await this.page.setRequestInterception(true);
        this.page.on('request', (request) => {
            if (['image', 'stylesheet', 'font', 'script'].indexOf(request.resourceType()) !== -1) {
                request.abort();
            } else {
                request.continue();
            }
        });
        this.isLinkCrawlTest = isLinkCrawlTest;
    }

    async crawl(link) {
        const userAgent = randomUseragent.getRandom();
        const crawlResults = { isValidPage: true, pageSource: null };
        try {
            await this.page.setUserAgent(userAgent);
            await this.page.goto(link, this.pageOptions);
            await this.page.waitForFunction(this.waitForFunction);
            crawlResults.pageSource = await this.page.content();
        }
        catch (error) {
            crawlResults.isValidPage = false;
        }
        if (this.isLinkCrawlTest) {
            this.close();
        }
        return crawlResults;
    }

    close() {
        if (!this.browser) {
            this.browser.close();
        }
    }
}

const puppeteerService = new PuppeteerService();
module.exports = puppeteerService;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接