How to submit a form using PhantomJS

How to submit a form using PhantomJS

I’m trying to use phantomJS (what an awesome tool btw!) to submit a form for a page that I have login credentials for, and then output the content of the destination page to stdout. I’m able to access the form and set its values successfully using phantom, but I’m not quite sure what the right syntax is to submit the form and output the content of the subsequent page. What I have so far is:
var page = new WebPage();
var url = phantom.args[0];

page.open(url, function (status) {

if (status !== ‘success’) {
console.log(‘Unable to access network’);
} else {

console.log(page.evaluate(function () {

var arr = document.getElementsByClassName(“login-form”);
var i;

for (i=0; i < arr.length; i++) { if (arr[i].getAttribute('method') == "POST") { arr[i].elements["email"].value="mylogin@somedomain.com"; arr[i].elements["password"].value="mypassword"; // This part doesn't seem to work. It returns the content // of the current page, not the content of the page after // the submit has been executed. Am I correctly instrumenting // the submit in Phantom? arr[i].submit(); return document.querySelectorAll('html')[0].outerHTML; }

Related:  Find maximum possible time HH:MM by permuting four given digits
} return "failed :-("; })); } phantom.exit(); }

Solutions/Answers:

Solution 1:

I figured it out. Basically it’s an async issue. You can’t just submit and expect to render the subsequent page immediately. You have to wait until the onLoad event for the next page is triggered. My code is below:

var page = new WebPage(), testindex = 0, loadInProgress = false;

page.onConsoleMessage = function(msg) {
  console.log(msg);
};

page.onLoadStarted = function() {
  loadInProgress = true;
  console.log("load started");
};

page.onLoadFinished = function() {
  loadInProgress = false;
  console.log("load finished");
};

var steps = [
  function() {
    //Load Login Page
    page.open("https://website.com/theformpage/");
  },
  function() {
    //Enter Credentials
    page.evaluate(function() {

      var arr = document.getElementsByClassName("login-form");
      var i;

      for (i=0; i < arr.length; i++) { 
        if (arr[i].getAttribute('method') == "POST") {

          arr[i].elements["email"].value="mylogin";
          arr[i].elements["password"].value="mypassword";
          return;
        }
      }
    });
  }, 
  function() {
    //Login
    page.evaluate(function() {
      var arr = document.getElementsByClassName("login-form");
      var i;

      for (i=0; i < arr.length; i++) {
        if (arr[i].getAttribute('method') == "POST") {
          arr[i].submit();
          return;
        }
      }

    });
  }, 
  function() {
    // Output content of page to stdout after form has been submitted
    page.evaluate(function() {
      console.log(document.querySelectorAll('html')[0].outerHTML);
    });
  }
];


interval = setInterval(function() {
  if (!loadInProgress && typeof steps[testindex] == "function") {
    console.log("step " + (testindex + 1));
    steps[testindex]();
    testindex++;
  }
  if (typeof steps[testindex] != "function") {
    console.log("test complete!");
    phantom.exit();
  }
}, 50);
Related:  How do I set textarea scroll bar to bottom as a default?

Solution 2:

Also, CasperJS provides a nice high-level interface for navigation in PhantomJS, including clicking on links and filling out forms.

CasperJS

Updated to add July 28, 2015 article comparing PhantomJS and CasperJS.

(Thanks to commenter Mr. M!)

Solution 3:

Sending raw POST requests can be sometimes more convenient. Below you can see post.js original example from PhantomJS

// Example using HTTP POST operation

var page = require('webpage').create(),
    server = 'http://posttestserver.com/post.php?dump',
    data = 'universe=expanding&answer=42';

page.open(server, 'post', data, function (status) {
    if (status !== 'success') {
        console.log('Unable to post!');
    } else {
        console.log(page.content);
    }
    phantom.exit();
});

Solution 4:

As it was mentioned above CasperJS is the best tool to fill and send forms.
Simplest possible example of how to fill & submit form using fill() function:

casper.start("http://example.com/login", function() {
//searches and fills the form with id="loginForm"
  this.fill('form#loginForm', {
    'login':    'admin',
    'password':    '12345678'
   }, true);
  this.evaluate(function(){
    //trigger click event on submit button
    document.querySelector('input[type="submit"]').click();
  });
});