当试图用Puppeteer登录Twitter时,通常会得到纺车



大多数时候,当我尝试登录Twitter时,它允许我输入用户名或电子邮件,但然后显示一个永久的旋转轮子,而不是显示要求我输入密码的屏幕。我曾经用Facebook等其他社交媒体解决过复杂的问题,但这个问题难倒了我。以前针对这个问题的Stack Overflow解决方案已经过时,甚至没有为Twitter现在使用的两页登录设置。

我尝试了各种方法,包括在云端的Linux机器和我家里的Windows机器上运行这个程序。我在我的Windows电脑上比较幸运,可能是因为我的隐身例程中有一些缺陷,我在这里发布的绝大多数代码都是这样的。

当这个程序失败时,在twitter.output.png中显示纺车。如果你在屏幕截图中看到一个询问密码的问题,那么这个程序将处理twitter.good.output.png的最终输出。

const puppeteer = require('puppeteer-extra');
const pluginStealth = require('puppeteer-extra-plugin-stealth')
puppeteer.use(pluginStealth())
const fs = require('fs');
async function run()
{
browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled',
'--window-size=1920,2700', // Maybe 5400, 10800 is too big and sometimes the page doesn't fully load?
'--lang=en-US,en;q=0.9' // Fool https://news.ycombinator.com/item?id=20480915
],
userDataDir: "C:/Users/User/AppData/Local/Google/Chrome/User Data/selenium3"
});
page = await browser.newPage();
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36";
password = await fs.readFileSync('twitter.password.attached.to.email','utf8').trim();
email = await fs.readFileSync('twitter.email','utf8').trim();
username = await fs.readFileSync('twitter.username','utf8').trim();
await page.setUserAgent(user_agent);
const stealthFile = `// Pass the Webdriver Test from http://willless.com/puppeteer-%E6%97%A0%E5%A4%B4%E6%A8%A1%E5%BC%8F%E5%8F%8D%E5%8F%8D%E7%88%AC%E8%AE%BE%E7%BD%AE%E6%B1%87%E6%80%BB.html
const newProto = navigator.__proto__;
delete newProto.webdriver;
navigator.__proto__ = newProto;
// Add the window.chrome field and fill it with some values
window.chrome = {};
window.chrome.app = {
InstallState: 'hehe',
RunningState: 'haha',
getDetails: 'xixi',
getIsInstalled: 'ohno',
};
window.chrome.csi = function () {};
window.chrome.loadTimes = function () {};
window.chrome.runtime = function () {};
// Set up Permissions
const originalQuery = window.navigator.permissions.query; //notification??
window.navigator.permissions.query = (parameters) =>
parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters);
// WebGL Setup
const getParameter = WebGLRenderingContext.getParameter;
WebGLRenderingContext.prototype.getParameter = function (parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445) {
return 'Intel Inc.';
}
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) {
return 'Intel(R) Iris(TM) Graphics 6100';
}
return getParameter(parameter);
};
// Pass navigator.connection.rtt Test
Object.defineProperty(navigator.connection, 'rtt', {
get: () => 50
});
// Bypass hairline feature
// store the existing descriptor
const elementDescriptor = Object.getOwnPropertyDescriptor(
HTMLElement.prototype,
"offsetHeight"
);
// redefine the property with a patched descriptor
Object.defineProperty(HTMLDivElement.prototype, "offsetHeight", {
...elementDescriptor,
get: function() {
if (this.id === "modernizr") {
return 1;
}
// @ts-ignore
return elementDescriptor.get.apply(this);
},
});`;
await page.evaluateOnNewDocument(stealthFile);
console.log('Going to Twitter');
await page.goto('https://twitter.com');
await page.waitForTimeout(6000+Math.random() * 6000);
console.log('Clicking on login');
[buttonElement] = await page.$x('//a[@href="/login"]');
buttonElement.click();
await page.waitForTimeout(6000+Math.random() * 6000);
[inputElement] = await page.$x('//input[@autocomplete="username"]');
inputElement.click();
await page.waitForTimeout(3000+Math.random() * 3000);
await page.keyboard.type(email);
console.log('Sending email');
await page.waitForTimeout(6000+Math.random() * 6000);
[buttonElement] = await page.$x("//div[@role='button']/descendant::span[text()='Next']");
buttonElement.click();
console.log('Clicking on next button');
await page.waitForTimeout(6000+Math.random() * 6000);
// Sometimes Twitter wants an email and username
html = await page.content();
if (html.match(/please enter your phone number or username/i))
{
await page.keyboard.type(username+"n");
await page.waitForTimeout(6000+Math.random() * 6000);
}
console.log('Saving screenshot for Stack Overflow');
await page.screenshot({path: 'twitter.output.png'});
await page.waitForTimeout(6000+Math.random() * 6000);
// If things are O.K., keep going...
total = 30 + Math.random() * 10;
i = 0;
while (i++ < total)
{
await page.keyboard.press('Backspace');
}
await page.keyboard.type(password+"n");
await page.waitForTimeout(3000+Math.random() * 3000);
await page.screenshot({path: 'twitter.good.output.png'});
await page.waitForTimeout(6000+Math.random() * 6000);
browser.close(); process.exit();
}
run();

我终于有时间解决这个问题了!

解决方案是在命令提示符下输入npm i puppeteer@latest

在之前处理这个问题时,我没有意识到这段代码加载的模块puppeteer-extra本身也在加载模块puppeteer,而我的puppeteer模块已经过时了。

过了一会儿,有一个"答案"。有人在这里发帖说我的问题是程序内建的各种延迟。这些延迟只是使程序运行缓慢,看起来更人性化,它们不会导致故障。

现在,我将能够做一些事情,比如发布到我自己的提要。即将发布的新Twitter api非常可怕,即使是每月100美元的选项也无法满足我的基本需求。我读到很多抱怨,有人测试了100美元/月的API一个小时,然后发现他们已经达到了一个月的极限,非常惊讶。

最新更新