2017-05-17 3 views
1

Facebook의 Graph API은 개발자 계정에서 게시물을 작성하지 않는 한 개발자가 허용하지 않으므로 Facebook 그룹의 게시물을 삭제하는 스크립트를 작성하고 있습니다.CasperJS를 사용하여 Facebook 그룹 삭제를 자동화합니다.

지금까지 Facebook에 로그인 한 다음 원하는 그룹 페이지로 이동할 수있었습니다. 거기에서 각 글에 대한 XPath를 페이지에서 볼 수 있습니다 (셀렉터 a[data-testid='post_chevron_button'] 사용). 각 XPath 선택기에서 this.click()을 호출하는 동안 스크립트가 실패합니다. 스크립트가 this.click(x(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(); 

내가 오류 메시지가 TypeError: undefined is not a constructor (evaluating 'this.click(x(xpath))')를 얻을 다음과 같이

나의 현재 스크립트입니다. 배열을 생성하는 코드의 마지막 비트를 대체하고 this.click("a[data-testid='post_chevron_button']");으로 반복하면 스크립트에 문제가 없습니다.

CasperJS가 XPath 선택기로 click()을 호출하는 것에 대해 싫어하는 점을 알고있는 사람이 있습니까? XPath는 CasperJS의 docs에서 유효한 선택자 인 것으로 보입니다.

UPDATE는

좀 더 정확하게 원하는 결과를 설명하기 위해 질문의 제목을 업데이트했습니다.

dasmelch의 조언, 내가 스크립트를 조금 수정 된 대신 스크립트에이 비트를 통합 한 (casper.thenOpen 부분 후) : 지금이 오류가

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

: Cannot dispatch mousedown event on nonexistent selector: xpath selector: //*[@id="u_0_47"] .

지난 밤에, 나는 그것에 대해 조금 다르게하기로 결정했습니다. 원하는 최종 결과에 가깝지만 지금은 CasperJS 및/또는 PhantomJS가 post_chevron_button을 클릭 한 후 드롭 다운에있는 요소를 찾는 데 문제가 있습니다. 여기에 내가 함께 결국 무엇인가 (casper.thenOpen 전에 모든 것을 원래 표시된 스크립트에 동일하게 유지) : (내가 찾고 있어요 값과 ajaxify 속성을 포함하는 요소가 있어야 알고

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']"); 
    while (elements.length > 0) { 
     this.click("a[data-testid='post_chevron_button']"); 
     this.wait(1000, function() { 
      this.capture('chevron_click.png'); 
      console.log("chevron_click.png saved"); 
     }); 
     var chevronLinks = casper.getElementsInfo("a[ajaxify]") 
     console.log("Found " + chevronLinks.length + " elements with ajaxify attribute."); 
     var chevronLinksIndex = 1; 
     chevronLinks.forEach(function(element){ 
      var ajaxifyValue = element.attributes["ajaxify"]; 
      console.log(ajaxifyValue); 
      if (ajaxifyValue.indexOf("delete.php?group_id={group-id}") !== -1) { 
       this.click("a[ajaxify='"+ajaxifyValue+"']"); 
       this.wait(100, function(){ 
        this.capture('deletePost' + chevronLinksIndex); 
       }); 
       chevronLinksIndex++; 
      } 
     }); 
     if (chevronLinksIndex === 1) { 
      break; 
     } 
     elements = casper.getElementsInfo("a[data-testid='post_chevron_button']"); 
    } 
}); 

브라우저에서 직접 단계별로 실행하면 a[data-testid='post_chevron_button']을 클릭 한 후에 요소가 표시되지만 캐스퍼는 찾을 수 없기 때문입니다. 뿐만 아니라 내 chevron_click.png 이미지 파일은이 스크립트를 실행할 때마다 업데이트해야하지만 그렇지 않습니다.

일부 코드는 순서대로 실행되지 않습니다. 예를 들어 ajaxify 속성 값의 로깅은 chevron_click.png saved을보기 전에 콘솔에서 발생합니다. 이것은 예상 할 수 있지만 불행히도 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> 

답변

0

.NET 용 Selenium 2 API로 수행하려는 작업을 수행 할 수있었습니다.

솔루션의 코드는 다음과 같습니다 :

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

가 나는 요소 대신 Thread.Sleep()를 사용하여 볼 수 있도록 기다릴 셀레늄을 사용하여 같이하고 싶습니다 몇 가지 개선이 있지만, 그것은을 위해 잘 작동하고 내 목적.

0

당신은 XPath는 물건 올바른 수행하지만 대해 forEach이 작동하지 않는 방법을 보인다. 당신은 그런 자들을 더 쉽게 casper.getElementsAttribute 직접 while 루프를 던져 쉽게 반복 처리를 모든 요소의 id를 잡아 수 있습니다

... 
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) 링크 (요소)가있는 사이트로 이동합니다.

+0

불행히도 여전히 같은 오류가 발생합니다. 이 기술은 id 속성을 가져 오는데도 효과적입니다. –

+0

다른 측면에서 테스트 한 결과 효과가있었습니다. console.log (요소)를 사용해 볼 수 있습니까? 나는 그들이 당신의 사건에서 이드를 포함하고 있지 않다고 생각합니다. – dasmelch

+0

각 반복마다 콘솔에 요소를 로깅 중이며 id 값이 올바르게 표시됩니다. 나는 PhantomJS가 함수 호출의 정의를 알지 못할 때 발생하는 에러 메시지를 읽었다. 마치 '캐스퍼 (Casper)'와 같이'forEach' 블록에서 범위를 벗어나는 것과 같은 것입니다. –