Javascript 使用CasperJS自动删除Facebook群组帖子

Javascript 使用CasperJS自动删除Facebook群组帖子,javascript,phantomjs,casperjs,Javascript,Phantomjs,Casperjs,我正在写一个脚本来删除一个Facebook群组的帖子,因为Facebook不允许开发者这么做,除非帖子是从开发者的帐户中发布的 到目前为止,我已经能够登录Facebook,然后导航到所需的群组页面。从那里,我可以获得页面上每个帖子的XPath(使用选择器a[data testid='post\u chevron\u button'])。我的脚本在尝试调用this时失败。在每个XPath选择器上单击() 我当前的脚本如下: phantom.casperTest = true; var x = re

我正在写一个脚本来删除一个Facebook群组的帖子,因为Facebook不允许开发者这么做,除非帖子是从开发者的帐户中发布的

到目前为止,我已经能够登录Facebook,然后导航到所需的群组页面。从那里,我可以获得页面上每个帖子的XPath(使用选择器
a[data testid='post\u chevron\u button']
)。我的脚本在尝试调用
this时失败。在每个XPath选择器上单击()

我当前的脚本如下:

phantom.casperTest = true;
var x = require('casper').selectXPath;
var casper = require('casper').create({   
    verbose: true,
    pageSettings: {
         loadImages:  false,
         loadPlugins: false,
         userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4'
    }
});

// print out all the messages in the headless browser context
casper.on('remote.message', function(msg) {
    this.echo('remote message caught: ' + msg);
});

// print out all the messages in the headless browser context
casper.on("page.error", function(msg, trace) {
    this.echo("Page Error: " + msg, "ERROR");
});

var url = 'http://www.facebook.com/';

casper.start(url, function() {
    console.log("page loaded");
    this.test.assertExists('form#login_form', 'form is found');
    this.fill('form#login_form', { 
        email: '{email}',
        pass: '{password}'
    }, true);
    this.click('#u_0_q');
    this.wait(1000, function() {
        this.echo("Capturing image of page after login.");
        this.capture('loggedin.png');
    });
});

casper.thenOpen('https://www.facebook.com/groups/{group-id}/', function() {
    this.echo(this.getTitle());
    this.wait(1000, function() {
        this.capture('group.png');
    });

    var elements = casper.getElementsInfo("a[data-testid='post_chevron_button']");

    var index = 1;
    elements.forEach(function(element){
        var xpath = '//*[@id="' + element.attributes["id"] + '"]';
        console.log(xpath);
        this.click(x(xpath));
        this.wait(100, function() {
            this.capture('chevronlink' + index + '.png');
        });
        index++;
    });
});

casper.run();
当脚本到达
this时,单击(x(xpath))我收到错误消息
TypeError:undefined不是构造函数(计算'this.click(x(xpath)))
。如果我简单地用
this替换创建数组并对其进行迭代的最后一位代码,单击(“a[data testid='post\u chevron\u button']”,我的脚本没有问题

有人知道CasperJS不喜欢用XPath选择器调用
click()
?XPath似乎是CasperJS的有效选择器

更新

我已经更新了问题的标题,以便更准确地描述期望的结果。

根据Per的建议,我对脚本进行了一些修改,并将此部分合并到脚本中(在
casper.thenOpen
部分之后):

我现在遇到这个错误:
无法在不存在的选择器上调度mousedown事件:xpath选择器://*[@id=“u\u 0\u 47”]

昨天晚上,我决定改变一下。我离期望的最终结果越来越近了,但现在CasperJS和/或PhantomJS在单击
post\u chevron\u按钮后很难找到下拉列表中的元素。以下是我最后得到的结果(casper.thenOpen之前的所有内容在最初显示的脚本中保持不变):

我知道应该有一个元素包含一个带有我正在搜索的值的
ajaxify
属性(因为我自己在浏览器中通过它时,会在点击
a[data testid='post\u chevron\u button']
后显示该元素),但Casper找不到它。不仅如此,我的
chevron_click.png
图像文件在每次运行此脚本时都应该更新,但事实并非如此

有些代码执行不按顺序进行。例如,在控制台中记录
ajaxify
属性值,然后再查看保存的
chevron\u click.png
。这可能是意料之中的,但不幸的是我没有很多JS经验。这个执行顺序问题可以解释为什么我对必需元素的搜索没有返回我所期望的结果

以下是需要单击以删除帖子的元素的示例:

<a class="_54nc" href="#" rel="async-post" 
ajaxify="/ajax/groups/mall/delete.php?group_id={group-id}&amp;message_id=806608486110204&amp;story_dom_id=mall_post_806608486110204%3A6%3A0&amp;entstory_context=%7B%22last_view_time%22%3A1495072771%2C%22fbfeed_context%22%3Atrue%2C%22location_type%22%3A2%2C%22outer_object_element_id%22%3A%22mall_post_806608486110204%3A6%3A0%22%2C%22object_element_id%22%3A%22mall_post_806608486110204%3A6%3A0%22%2C%22is_ad_preview%22%3Afalse%2C%22is_editable%22%3Afalse%2C%22mall_how_many_post_comments%22%3A2%2C%22bump_reason%22%3A0%2C%22story_width%22%3A502%2C%22shimparams%22%3A%7B%22page_type%22%3A16%2C%22actor_id%22%3A664025626%2C%22story_id%22%3A806608486110204%2C%22ad_id%22%3A0%2C%22_ft_%22%3A%22%22%2C%22location%22%3A%22group%22%7D%2C%22story_id%22%3A%22u_0_21%22%2C%22caret_id%22%3A%22u_0_22%22%7D&amp;surface=group_post_chevron"
role="menuitem"><span><span class="_54nh"><div class="_41t5"><i
class="_41t7 img sp_gJvT8CoKHU- sx_0f12ae"></i><i class="_41t8 img
sp_s36yWP_7MD_ sx_7e9f7d"></i>Delete Post</div></span></span></a>

您正确地使用了xpath,但forEach方法似乎不适用于此。 您可以使用casper.getElementsAttribute直接获取所有这些元素的id,并使用while循环抛出进行简单的迭代,使它们更容易执行,如下所示:

...
casper.thenOpen('https://www.facebook.com/groups/{group-id}/', function() {
  this.echo(this.getTitle());
  this.wait(1000, function() {
    this.capture('group.png');
  });
});
// do a while loop with where you can use  every single element and jump back
casper.then(function() {
  var elements = casper.getElementsAttribute("a[data-testid='post_chevron_button']", 'id');
  while (elements.length > 0) {
    // get always the last element with target id
    element = elements.pop();
    (function(element) {
      var xpath = '//*[@id="' + element + '"]';
      console.log(xpath);
      // do it step by step
      casper.then(function() {
        this.click(x(xpath));
      });
      casper.then(function() {
        this.capture('chevronlink' + element + '.png');
      });
      // go back to the page with the links (if necessary)
      casper.then(function() {
        casper.back();
      });
    })(element);
  };
});
...

不看FB,我想你必须回到(casper.back)链接(元素)所在的站点

我能够完成我试图使用Selenium 2 API for.NET所做的事情

解决方案代码如下:

class Program
{
    static void Main(string[] args)
    {
        var options = new ChromeOptions();
        options.AddUserProfilePreference("profile.default_content_setting_values.notifications", 2);

        using (IWebDriver driver = new ChromeDriver(options))
        {
            // Maximize window
            driver.Manage().Window.Maximize();

            // Log into Facebook
            driver.Navigate().GoToUrl("http://www.facebook.com/");
            driver.FindElement(By.Id("email")).SendKeys("username");
            driver.FindElement(By.Id("pass")).SendKeys("password");
            driver.FindElement(By.Id("pass")).SendKeys(Keys.Enter);

            driver.Navigate().GoToUrl("https://www.facebook.com/groups/{group-id}/");
            var chevronPostLinks = driver.FindElements(By.XPath("//a[@data-testid='post_chevron_button']"));
            chevronPostLinks.FirstOrDefault().Click();
            Thread.Sleep(1000);
            var deletePostElements = driver.FindElements(By.XPath("//a[contains(@ajaxify,'delete.php?group_id={group-id}')]"));
            while (deletePostElements.Count > 0 && chevronPostLinks.Count > 0)
            {
                Thread.Sleep(1000);
                deletePostElements.Where(x => x.Displayed == true).FirstOrDefault().Click();
                Thread.Sleep(1000);
                driver.FindElement(By.ClassName("layerConfirm")).Click();

                Thread.Sleep(2000);
                chevronPostLinks = driver.FindElements(By.XPath("//a[@data-testid='post_chevron_button']"));
                if (chevronPostLinks.Count > 0)
                {
                    chevronPostLinks.FirstOrDefault().Click();
                }
                else
                {
                    driver.Navigate().GoToUrl("https://www.facebook.com/groups/{group-id}/");
                    chevronPostLinks = driver.FindElements(By.XPath("//a[@data-testid='post_chevron_button']"));
                    chevronPostLinks.FirstOrDefault().Click();
                }
                Thread.Sleep(1000);
                deletePostElements = driver.FindElements(By.XPath("//a[contains(@ajaxify,'delete.php?group_id={group-id}')]"));
            }
        }
    }
}

我想做一些改进,比如使用Selenium来等待元素可见,而不是使用
Thread.Sleep()
,但就我的目的而言,它工作得很好。

不幸的是,我仍然遇到同样的错误。不过,这项技术也适用于获取id属性。我在另一个方面测试了它,结果成功了。你能试试console.log(元素)吗?我猜在您的例子中它们不包含id。我在每次迭代时都将元素记录到控制台,并且id值显示正确。我已经读到,当PhantomJS不知道函数调用的定义时,会抛出我收到的错误消息。这就像是
这个
(在Casper中)超出了
forEach
块的范围。如果我解释的每件事都正确,你就不需要对你点击的每个站点截图。xpath的东西工作正常,但是for-each不是这样工作的。你必须用另一种方式来做。我用另一个选项更新我的帖子。谢谢你的持续帮助。现在,我在每个
函数调用上都遇到了不同的错误。click()
函数调用:
无法在不存在的选择器上调度mousedown事件:xpath选择器://*[@id=“u\u 0\u 21”]
。我注意到,一旦新代码执行,被捕获的屏幕截图就不再出现在组页面上。
...
casper.thenOpen('https://www.facebook.com/groups/{group-id}/', function() {
  this.echo(this.getTitle());
  this.wait(1000, function() {
    this.capture('group.png');
  });
});
// do a while loop with where you can use  every single element and jump back
casper.then(function() {
  var elements = casper.getElementsAttribute("a[data-testid='post_chevron_button']", 'id');
  while (elements.length > 0) {
    // get always the last element with target id
    element = elements.pop();
    (function(element) {
      var xpath = '//*[@id="' + element + '"]';
      console.log(xpath);
      // do it step by step
      casper.then(function() {
        this.click(x(xpath));
      });
      casper.then(function() {
        this.capture('chevronlink' + element + '.png');
      });
      // go back to the page with the links (if necessary)
      casper.then(function() {
        casper.back();
      });
    })(element);
  };
});
...
class Program
{
    static void Main(string[] args)
    {
        var options = new ChromeOptions();
        options.AddUserProfilePreference("profile.default_content_setting_values.notifications", 2);

        using (IWebDriver driver = new ChromeDriver(options))
        {
            // Maximize window
            driver.Manage().Window.Maximize();

            // Log into Facebook
            driver.Navigate().GoToUrl("http://www.facebook.com/");
            driver.FindElement(By.Id("email")).SendKeys("username");
            driver.FindElement(By.Id("pass")).SendKeys("password");
            driver.FindElement(By.Id("pass")).SendKeys(Keys.Enter);

            driver.Navigate().GoToUrl("https://www.facebook.com/groups/{group-id}/");
            var chevronPostLinks = driver.FindElements(By.XPath("//a[@data-testid='post_chevron_button']"));
            chevronPostLinks.FirstOrDefault().Click();
            Thread.Sleep(1000);
            var deletePostElements = driver.FindElements(By.XPath("//a[contains(@ajaxify,'delete.php?group_id={group-id}')]"));
            while (deletePostElements.Count > 0 && chevronPostLinks.Count > 0)
            {
                Thread.Sleep(1000);
                deletePostElements.Where(x => x.Displayed == true).FirstOrDefault().Click();
                Thread.Sleep(1000);
                driver.FindElement(By.ClassName("layerConfirm")).Click();

                Thread.Sleep(2000);
                chevronPostLinks = driver.FindElements(By.XPath("//a[@data-testid='post_chevron_button']"));
                if (chevronPostLinks.Count > 0)
                {
                    chevronPostLinks.FirstOrDefault().Click();
                }
                else
                {
                    driver.Navigate().GoToUrl("https://www.facebook.com/groups/{group-id}/");
                    chevronPostLinks = driver.FindElements(By.XPath("//a[@data-testid='post_chevron_button']"));
                    chevronPostLinks.FirstOrDefault().Click();
                }
                Thread.Sleep(1000);
                deletePostElements = driver.FindElements(By.XPath("//a[contains(@ajaxify,'delete.php?group_id={group-id}')]"));
            }
        }
    }
}