Post 通过控制台在 Facebook 页面上发表评论
Post comments on Facebook page via console
如图所示,Facebook 评论框没有提交按钮,当您写下内容并按下 Enter 按钮时,评论就会发布。
我想通过 JavaScript 在控制台中 运行 提交评论,但我试图触发 Enter 事件,提交 DOM 的事件。无法使其工作。
当前的评论框不是 <form>
内的传统 <textarea>
。他们在 div 上使用 contenteditable 属性。为了在这种情况下提交,您需要监听其中一个键盘事件(keydown
、keypress
、keyup
)并查找 Enter
键是键码 13.
在这种情况下,看起来 FB 正在监听 keydown
evt,所以当我 运行 这段代码时,我能够伪造提交评论:
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.querySelector('[role="combobox"]._54-z span span'));
有几点需要注意。 class ._54-z
是他们刚好在我的页面上使用的 class。你的旅费可能会改变。使用开发工具确保您获取了正确的元素(它应该具有 aria 角色 "combobox")。此外,如果您希望支持旧版浏览器,则必须调整上面的 fireEvent
函数代码。我只在最新的Chrome.
测试了上面的例子
最后,为了使您的事情复杂化,Facebook 正在使用 React 创建当前页面的虚拟 DOM 表示。如果您在组合框中手动输入字符,然后 运行 上面的代码,它将按预期工作。但是您将无法将组合框最里面的 <span>
的 innerHTML 设置为您要执行的操作然后触发 keydown
。您可能需要触发组合框上的 change
事件以确保您的消息持续到虚拟 DOM.
这应该让你开始了!希望对您有所帮助!
经过 3 周的试验(使用@Benjamin Solum 的 fireEvent
函数),这是一个可行的解决方案:
- 此版本 post 仅针对页面上的第一个 post 发表评论(使用
querySelector
方法)
此版本只能在您的个人墙上使用(除非您更改查询选择器)
function fireEvent(type, element, keyCode) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
if (keyCode !== undefined){
evt.keyCode = keyCode;
evt.which = keyCode;
}
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
// clicking the comment link - it reveals the combobox
document.querySelector(".fbTimelineSection .comment_link").click();
setTimeout(function(){
var combobox = document.querySelector(".fbTimelineSection [role='combobox']");
var spanWrapper = document.querySelector(".fbTimelineSection [role='combobox'] span");
// add text to the combobox
spanWrapper.innerHTML = "<span data-text='true'>Thank you!</span>";
var spanElement = document.querySelector(".fbTimelineSection [role='combobox'] span span");
fireEvent("blur", combobox);
fireEvent("focus", combobox);
fireEvent("input", combobox);
fireEvent("keydown", spanElement, 13); // pushing enter
},2000);
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.
几年后,这个 post 仍然相关,实际上是我发现的唯一一个与此相关的,而我正在尝试通过 JS 代码 post 到 FB 组(类似的任务到原来的问题)。
我终于破解了它 - 经过测试并有效:
setTimeout(() => {
document.querySelector('[placeholder^="Write something"]').click();
setTimeout(() => {
let postText = "I'm a Facebook post from Javascript!";
let dataDiv = document.querySelector('[contenteditable] [data-offset-key]');
let dataKey = dataDiv.attributes["data-offset-key"].value;
//Better to construct the span structure exactly in the form FB does it
let spanHTML = `<span data-offset-key="${dataKey}"><span data-text="true">${postText}</span></span>`;
dataDiv.innerHTML = spanHTML;
let eventType = "input";
//This can probably be optimized, no need to fire events for so many elements
let div = document.querySelectorAll('div[role=presentation]')[1].parentElement.parentElement;
let collection = div.getElementsByTagName("*");
[...collection].forEach(elem => {
let evt = document.createEvent("HTMLEvents");
evt.initEvent(eventType, true, true); //second "true" is for bubbling - might be important
elem.dispatchEvent(evt);
});
//Clicking the post button
setTimeout(()=>{
document.querySelector('.rfloat button[type=submit][value="1"]').click();
},2000);
}, 4000);
}, 7000);
故事是这样的,正如我从 post 之前的评论和深入研究 FB 的代码中了解到的那样。 FB 使用 React,因此对 DOM 的更改不会 "catch on",因为 React 使用虚拟 DOM。如果您在从 JS 更改 DOM 后单击 "Post",则不会 post 编辑文本。这就是为什么您必须按照此处的建议手动触发事件。
但是 - 为正确的元素触发正确的事件是一件棘手的事情,几乎让我无法成功。几个小时后,我发现这段代码有效,可能是因为它针对多个元素,从组 post 的父元素开始,向下钻取到所有子元素并为每个子元素触发事件(this是 [...collection].forEach(elem => {
位)。如所写,这显然可以优化以找到需要触发事件的正确元素。
至于要触发 哪个 事件,正如此处所讨论的,我已经试验了几种,发现 "input"
是其中之一。此外,在我将 initEvent
的第二个参数更改为 true
后代码开始工作 - 即 evt.initEvent(eventType, true, true)
。不确定这是否有所作为,但我已经有足够的时间摆弄这个,如果它有效,那对我来说就足够了。顺便说一句,setTimeout
当然可以玩。
(未成功)深挖FB的React数据结构
另一个关于我尝试走的不同路径但最终没有结果的说明:使用 React Dev Tools Chrome 扩展,您可以访问组件本身及其所有 props
和state
s 使用 $r
。令人惊讶的是,这在控制台 之外 也有效,因此使用 TamperMonkey 之类的东西到 运行 JS 代码也有效。我实际上找到了 FB 将 post 文本保存在状态中的位置。作为参考,它位于一个名为 ComposerStatusAttachmentMentionsInputContainer
的组件中,该组件负责 post 的编辑器部分,下面是访问它的代码。
$r
实际上提供了对很多 React 内容的访问,比如 setState
。从理论上讲,我相信我可以使用它来设置 React 中 post 文本的状态(如果你了解 React,你会同意 setState
将是触发会持续的更改的正确方法) .
然而,经过几个小时后,我发现这很难做到,因为 FB 在 React 之上使用了一个框架 ,称为 Draft.js,它处理所有post秒。这个框架有它自己的方法,类,数据结构等等,如果没有源代码,很难对来自 "outside" 的那些进行操作。
我还尝试手动触发附加到组件的 onchange
函数,但没有成功,因为我没有正确的参数,这些参数是 editorContent
和Draft.Js 中的 selectionContent
,需要使用我无法访问的 Draft.js 中的 Modifier
之类的方法仔细构造(你到底是怎么从外部访问静态文件的?来自库的方法纠缠在源代码中??我没有设法)。
无论如何,访问存储文本的状态变量的代码,前提是你有 React 开发工具并且你已经突出显示 ComposerStatusAttachmentMentionsInputContainer
:
let blockMap = $r["state"].activeEditorState[""].currentContent.blockMap;
let innerObj = JSON.parse(JSON.stringify(blockMap)); //this is needed to get the next property as it's not static or something
let id = Object.keys(innerObj)[0]; //get the id from the obj property
console.log(innerObj[id].text); //this is it!
但正如我所写,这几乎没用:-)
因为我无法通过 "normal" facebook 页面 post 发表评论,我记得他们也有移动版本,在 m.facebook 上。 com,在那里,他们仍然有提交按钮,所以根据您的需要,这可能是一个不错的选择
因此,您可以转到移动 facebook post(例如 https://m.facebook.com/${author}/posts/${postId})并执行
// Find the input element that saves the message to be posted
document.querySelector("input[name='comment_text']").value='MESSAGE TO POST';
// find the submit button, enable it and click it
const submitButton = document.querySelector("button[name='submit']");
submitButton.disabled = false;
submitButton.click();
为了解决您的问题,您可以看看这个 link,这里有一个示例如何“使用 JavaScript 在 Facebook post 上自动发表评论”
"步骤如下:
使用 m.facebook.com 转到 Facebook 页面
登录并打开任何 post。
按 Ctrl+Shift+I 在 Chrome 中打开开发者模式
导航到控制台。
现在,运行 下面的脚本。"
var count = 100;
var message = "Hi";
var loop = setInterval(function(){
var input = document.getElementsByName("comment_text")[0];
var submit = document.querySelector('button[type="submit"]');
submit.disabled = false;
input.value = message;
submit.click();
count -= 1;
if(count == 0)
{
clearInterval(loop);
}
}, 10000);
亲切的问候
参考:source page
如图所示,Facebook 评论框没有提交按钮,当您写下内容并按下 Enter 按钮时,评论就会发布。
我想通过 JavaScript 在控制台中 运行 提交评论,但我试图触发 Enter 事件,提交 DOM 的事件。无法使其工作。
当前的评论框不是 <form>
内的传统 <textarea>
。他们在 div 上使用 contenteditable 属性。为了在这种情况下提交,您需要监听其中一个键盘事件(keydown
、keypress
、keyup
)并查找 Enter
键是键码 13.
在这种情况下,看起来 FB 正在监听 keydown
evt,所以当我 运行 这段代码时,我能够伪造提交评论:
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.querySelector('[role="combobox"]._54-z span span'));
有几点需要注意。 class ._54-z
是他们刚好在我的页面上使用的 class。你的旅费可能会改变。使用开发工具确保您获取了正确的元素(它应该具有 aria 角色 "combobox")。此外,如果您希望支持旧版浏览器,则必须调整上面的 fireEvent
函数代码。我只在最新的Chrome.
最后,为了使您的事情复杂化,Facebook 正在使用 React 创建当前页面的虚拟 DOM 表示。如果您在组合框中手动输入字符,然后 运行 上面的代码,它将按预期工作。但是您将无法将组合框最里面的 <span>
的 innerHTML 设置为您要执行的操作然后触发 keydown
。您可能需要触发组合框上的 change
事件以确保您的消息持续到虚拟 DOM.
这应该让你开始了!希望对您有所帮助!
经过 3 周的试验(使用@Benjamin Solum 的 fireEvent
函数),这是一个可行的解决方案:
- 此版本 post 仅针对页面上的第一个 post 发表评论(使用
querySelector
方法) 此版本只能在您的个人墙上使用(除非您更改查询选择器)
function fireEvent(type, element, keyCode) { var evt; if(document.createEvent) { evt = document.createEvent("HTMLEvents"); evt.initEvent(type, true, true); } else { evt = document.createEventObject(); evt.eventType = type; } evt.eventName = type; if (keyCode !== undefined){ evt.keyCode = keyCode; evt.which = keyCode; } if(document.createEvent) { element.dispatchEvent(evt); } else { element.fireEvent("on" + evt.eventType, evt); } } // clicking the comment link - it reveals the combobox document.querySelector(".fbTimelineSection .comment_link").click(); setTimeout(function(){ var combobox = document.querySelector(".fbTimelineSection [role='combobox']"); var spanWrapper = document.querySelector(".fbTimelineSection [role='combobox'] span"); // add text to the combobox spanWrapper.innerHTML = "<span data-text='true'>Thank you!</span>"; var spanElement = document.querySelector(".fbTimelineSection [role='combobox'] span span"); fireEvent("blur", combobox); fireEvent("focus", combobox); fireEvent("input", combobox); fireEvent("keydown", spanElement, 13); // pushing enter },2000);
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.
几年后,这个 post 仍然相关,实际上是我发现的唯一一个与此相关的,而我正在尝试通过 JS 代码 post 到 FB 组(类似的任务到原来的问题)。
我终于破解了它 - 经过测试并有效:
setTimeout(() => {
document.querySelector('[placeholder^="Write something"]').click();
setTimeout(() => {
let postText = "I'm a Facebook post from Javascript!";
let dataDiv = document.querySelector('[contenteditable] [data-offset-key]');
let dataKey = dataDiv.attributes["data-offset-key"].value;
//Better to construct the span structure exactly in the form FB does it
let spanHTML = `<span data-offset-key="${dataKey}"><span data-text="true">${postText}</span></span>`;
dataDiv.innerHTML = spanHTML;
let eventType = "input";
//This can probably be optimized, no need to fire events for so many elements
let div = document.querySelectorAll('div[role=presentation]')[1].parentElement.parentElement;
let collection = div.getElementsByTagName("*");
[...collection].forEach(elem => {
let evt = document.createEvent("HTMLEvents");
evt.initEvent(eventType, true, true); //second "true" is for bubbling - might be important
elem.dispatchEvent(evt);
});
//Clicking the post button
setTimeout(()=>{
document.querySelector('.rfloat button[type=submit][value="1"]').click();
},2000);
}, 4000);
}, 7000);
故事是这样的,正如我从 post 之前的评论和深入研究 FB 的代码中了解到的那样。 FB 使用 React,因此对 DOM 的更改不会 "catch on",因为 React 使用虚拟 DOM。如果您在从 JS 更改 DOM 后单击 "Post",则不会 post 编辑文本。这就是为什么您必须按照此处的建议手动触发事件。
但是 - 为正确的元素触发正确的事件是一件棘手的事情,几乎让我无法成功。几个小时后,我发现这段代码有效,可能是因为它针对多个元素,从组 post 的父元素开始,向下钻取到所有子元素并为每个子元素触发事件(this是 [...collection].forEach(elem => {
位)。如所写,这显然可以优化以找到需要触发事件的正确元素。
至于要触发 哪个 事件,正如此处所讨论的,我已经试验了几种,发现 "input"
是其中之一。此外,在我将 initEvent
的第二个参数更改为 true
后代码开始工作 - 即 evt.initEvent(eventType, true, true)
。不确定这是否有所作为,但我已经有足够的时间摆弄这个,如果它有效,那对我来说就足够了。顺便说一句,setTimeout
当然可以玩。
(未成功)深挖FB的React数据结构
另一个关于我尝试走的不同路径但最终没有结果的说明:使用 React Dev Tools Chrome 扩展,您可以访问组件本身及其所有 props
和state
s 使用 $r
。令人惊讶的是,这在控制台 之外 也有效,因此使用 TamperMonkey 之类的东西到 运行 JS 代码也有效。我实际上找到了 FB 将 post 文本保存在状态中的位置。作为参考,它位于一个名为 ComposerStatusAttachmentMentionsInputContainer
的组件中,该组件负责 post 的编辑器部分,下面是访问它的代码。
$r
实际上提供了对很多 React 内容的访问,比如 setState
。从理论上讲,我相信我可以使用它来设置 React 中 post 文本的状态(如果你了解 React,你会同意 setState
将是触发会持续的更改的正确方法) .
然而,经过几个小时后,我发现这很难做到,因为 FB 在 React 之上使用了一个框架 ,称为 Draft.js,它处理所有post秒。这个框架有它自己的方法,类,数据结构等等,如果没有源代码,很难对来自 "outside" 的那些进行操作。
我还尝试手动触发附加到组件的 onchange
函数,但没有成功,因为我没有正确的参数,这些参数是 editorContent
和Draft.Js 中的 selectionContent
,需要使用我无法访问的 Draft.js 中的 Modifier
之类的方法仔细构造(你到底是怎么从外部访问静态文件的?来自库的方法纠缠在源代码中??我没有设法)。
无论如何,访问存储文本的状态变量的代码,前提是你有 React 开发工具并且你已经突出显示 ComposerStatusAttachmentMentionsInputContainer
:
let blockMap = $r["state"].activeEditorState[""].currentContent.blockMap;
let innerObj = JSON.parse(JSON.stringify(blockMap)); //this is needed to get the next property as it's not static or something
let id = Object.keys(innerObj)[0]; //get the id from the obj property
console.log(innerObj[id].text); //this is it!
但正如我所写,这几乎没用:-)
因为我无法通过 "normal" facebook 页面 post 发表评论,我记得他们也有移动版本,在 m.facebook 上。 com,在那里,他们仍然有提交按钮,所以根据您的需要,这可能是一个不错的选择
因此,您可以转到移动 facebook post(例如 https://m.facebook.com/${author}/posts/${postId})并执行
// Find the input element that saves the message to be posted
document.querySelector("input[name='comment_text']").value='MESSAGE TO POST';
// find the submit button, enable it and click it
const submitButton = document.querySelector("button[name='submit']");
submitButton.disabled = false;
submitButton.click();
为了解决您的问题,您可以看看这个 link,这里有一个示例如何“使用 JavaScript 在 Facebook post 上自动发表评论”
"步骤如下:
使用 m.facebook.com 转到 Facebook 页面 登录并打开任何 post。 按 Ctrl+Shift+I 在 Chrome 中打开开发者模式 导航到控制台。 现在,运行 下面的脚本。"
var count = 100;
var message = "Hi";
var loop = setInterval(function(){
var input = document.getElementsByName("comment_text")[0];
var submit = document.querySelector('button[type="submit"]');
submit.disabled = false;
input.value = message;
submit.click();
count -= 1;
if(count == 0)
{
clearInterval(loop);
}
}, 10000);
亲切的问候
参考:source page