Mouse Clicks, Typing, etc...
NEW!
NEW!This Automation API was released in Aug 2019. While it is highly usable, certain things may change slightly over the next few months. We will do our best to ensure backwards compatibility, and all modifications will be listed here:
page.emulateTimezone() and update to latest Chrome version. No breaking changes.Click here to go back to the Standard JSON API Docs
This API is under active development. Please email Support@PhantomJsCloud.com and we will reply back ASAP, hopefully with a solution to your problem.
This api allows unparalleled control over your browser request. Using this Automation API you will be able to do the following things easily:
ES2018 javascript) execution inside your API call. This enables dynamic query/manipulation of the page. Full control over the browser.page object. You can not create more page objects.Page.on() events are limited to callbacks. No callback sub-function calls (like request.abort()) are implemented.await keyword.http://www.example.com)F12)Esc)$("h1"); )GET it is extremely cumbersome due to the need to urlEncode all non-alphanumeric characters. You really should should construct a JSON object and submit that to our API via the POST body.overseerScript based APIThe main PhantomJsCloud API is used by adding properties to your request's JSON (A Declarative API). This new Automation API is driven by a procedural "overseer" script. This script is executed in a special sandbox that has access to the API documented by this page.
To use this new Automation API, embed your script into the new pageRequest.overseerScript property of the Main Declarative API's REQUEST-JSON
Here is an example of what your REQUEST-JSON would look like:
//a IPageRequest you can submit to the PhantomJsCloud POST API.
{
//navigate to our automation test page
url:"http:/PhantomJsCloud.com/examples/corpus/automation.html",
//outputs a jpeg. Note the value of the Label in the screenshot
renderType:"jpeg",
//request will pause until the DOMReady event, then click the button which changes the label
overseerScript:'await page.waitForNavigation({waitUntil:"domcontentloaded"}); page.click("input#btn_id_1");',
}
You can try the above example by clicking here.
automation renderTypeThe new overseerScript offers full control over what page(s) are loaded, what to do on the page, and what results are stored.
To go with this flexibility, we added a new renderType:"automation". This will output a JSON containing only the results associated with your overseerScript.
If you do not choose to use the new "automation" renderType, your automation results are still available if you choose outputAsJson:true as described here. Automation results will be found under userResponse.pageResponses.automation.
Here is an example showing some of the capabilities of the new "automation" renderType:
//a IPageRequest you can submit to the PhantomJsCloud POST API.
{
//navigate to our automation test page
url:"http:/PhantomJsCloud.com/examples/corpus/automation.html",
//outputs automation results
renderType:"automation",
//request will pause until the DOMReady event, log the value of the label, click the button which changes the label, then log the label value again.
//then values are stored for the other automation JSON outputs: rendering, storage, and errorLogs.
overseerScript:'await page.waitForNavigation({waitUntil:"domcontentloaded"}); \
await page.meta.log(await page.content({selector:"label#lbl_id_1",type:"plainText"})); \
await page.click("input#btn_id_1"); \
await page.meta.log(await page.content({selector:"label#lbl_id_1",type:"plainText"})); \
//VERY IMPORTANT: comments need a newline character at the end, otherwise the following lines do not get executed. \n \
page.render.content(); \
page.meta.store.set("key1",{some:"json"}); \
page.meta.logError("test error",{no:1});',
}
This example shows how to render both a screenshot and page HTML in a single request. You can try this example by clicking here.
// JSON you can submit to the PhantomJsCloud POST API.
{
// navigate to example.com
url:"http://www.example.com",
// outputs JSON containing only automation results
renderType:"automation",
// request will pause until the page LOAD event, then render HTML and a JPEG.
overseerScript:'await page.waitForNavigation(); \n page.render.content(); \n await page.render.screenshot();',
// please note that the above overseerScript is a single string containing javascript.
}
Please note the use of await in front of page.render.screenshot(). This causes the overseerScript to pause until the screenshot is taken.
As the above example, this and all overseerScript examples follow a similar workflow, quite easy once you get used to it.
///a IPageRequest you can submit to the PhantomJsCloud POST API.
{
//no url is sent. instead, the overseerScript decides what URL and navigates to it.
//url:""
//after the overseerScript finishes executing, a JPEG will be rendered
renderType:"jpeg",
//request will pause until the DOMReady event, then render HTML and a JPEG.
overseerScript:' \n\
await page.goto("http:/PhantomJsCloud.com/examples/corpus/automation.html"); //nav to url, plus wait for it to complete \n\
await page.click("input[name=\'txt_1\']"); //click on the input box and wait till finished. \n\
await page.keyboard.press("Backspace",{times:5}); //delete 5 characters and wait till finished. \n\
await page.keyboard.type("TYPED!",{delay:10}); //type some text and wait till finish \n\
await page.waitForDelay(2000); //wait 2 seconds \n\
',
//please note that in the above overseerScript lines we add linebreak characters and then a "\" character before a linebreak.
//If this is difficult to understand, just don't use linebreaks, and don't use comments in your script and you should be fine.
}
This example injects a custom script and then instructs it to highlight the word "Example". Please take note that to execute javascript in the target page, we use the page.evaluate() function.
You can try this example by clicking here.
{
"renderType": "jpeg",
"overseerScript":'await page.goto("http://phantomjscloud.com/examples/corpus/example.com.html"); await page.addScriptTag({url:"http://PhantomJsCloud.com/examples/scripts/hilitor.js"}); await page.evaluate(()=>{let _hilitor=new Hilitor(); _hilitor.apply("Example");});',
}
Here are three ways to login and take a screenshot.
(For an example of logging into LinkedIn, check the Advanced Scenario Samples)
PLEASE NOTE: In this first example, when clicking the Login button we don't await, instead immediately move to the next line which waits for a page navigation.
///a IPageRequest you can submit to the PhantomJsCloud POST API.
{
//Normal API navigates to this target page
url:"http://PhantomJsCloud.com/examples/corpus/automation.html",
//after the overseerScript finishes executing, a JPEG will be rendered
renderType:"jpeg",
//will wait until the text input html element is available, then logs in, waiting for the following page to domReady before finishing.
overseerScript:' await page.waitForSelector("input[name=id]"); \
await page.type("input[name=id]","user",{delay:100}); \
await page.type("input[name=pass]","pass",{delay:100}); \
page.click("input[type=button][value=Login]"); \
await page.waitForNavigation(); ',
//please note that in the above overseerScript lines we add linebreak characters and then a "\" character before a linebreak.
//If this is difficult to understand, just don't use linebreaks, and don't use comments in your script and you should be fine.
}
Here's the same workflow as the above example but doing everything via the overseerScript (output is JSON)
///a IPageRequest you can submit to the PhantomJsCloud POST API.
{
//render a json of just the overseerScript output
renderType:"automation",
//will load the url, wait until the text input html element is available, then logs in, waiting for the following page to domReady before taking a screenshot.
overseerScript:' await page.goto("http://PhantomJsCloud.com/examples/corpus/automation.html"); \
await page.waitForSelector("input[name=id]"); \
await page.type("input[name=id]","user",{delay:100}); \
await page.type("input[name=pass]","pass",{delay:100}); \
page.click("input[type=button][value=Login]"); \
await page.waitForNavigation(); \
await page.render.content(); \
await page.render.screenshot();',
//please note that in the above overseerScript lines we add linebreak characters and then a "\" character before a linebreak.
//If this is difficult to understand, just don't use linebreaks, and don't use comments in your script and you should be fine.
}
And here we use Promise Chaining along with page.done() instead of pausing via await
///a IPageRequest you can submit to the PhantomJsCloud POST API.
{
//render a json of just the overseerScript output
renderType:"automation",
//will load the url, wait until the text input html element is available,
//then logs in, waiting for the following page to domReady before taking a screenshot.
overseerScript:' page.goto("http://PhantomJsCloud.com/examples/corpus/automation.html"); \
await page.waitForSelector("input[name=id]"); \
page.manualWait(); \
return page.type("input[name=id]","user",{delay:100}) \
.then(()=>{ return page.type("input[name=pass]","pass",{delay:100}); }) \
.then(async ()=>{ \
page.click("input[type=button][value=Login]"); \
await page.waitForNavigation(); \
await page.render.content(); \
await page.render.screenshot(); \
page.done(); \
}); ',
}
This example renders a jpeg as soon as the domReady event is triggered, resulting in a more than 2x speedup of the cnn.com website. Rendering at DomReady is much faster but depending on the site may be less accurate.
You can try this example by clicking here.
{
"url":"http://cnn.com",
"renderType": "jpeg",
"overseerScript":'await page.waitForNavigation({waitUntil:"domcontentloaded"}); page.done();',
}
For more examples, check the Advanced Scenario Samples
Besides the commands above, your overseerScript can use these Puppeteer commands. They work like their Puppeteer counterparts, with the differences listed here and on each command's page.
page.$(), page.$$() and page.evaluateHandle() return live references to objects in the page: ElementHandle:class for DOM elements, JSHandle:class for other values. Call their methods (click, type, boundingBox, screenshot, $eval, ...) or pass them as arguments to page.evaluate().
const items = await page.$$("ul.results li");
for (const item of items) {
await page.meta.log(await item.evaluate((el) => el.textContent));
}
handle.dispose().screenshot() returns base64 text; its path option is ignored.page.frames() returns every frame of the page as a Frame:class object, the main frame first; page.waitForFrame() waits for one. A Frame has the page-style commands (goto, evaluate, $, click, waitForSelector, locator, ...) for that frame.
const frame = await page.waitForFrame((f) => f.url().includes("/embedded/"));
await frame.click("button.play");
page.mainFrame() returns a frame summary, a plain object ({url, name, guid}), as before; use page.frames()[0] for the main frame's Frame object. A page.on() frame event handler gets an event object whose fields are methods (frame.url(), frame.name(), frame.guid()); frame.toJson() returns the plain {url, name, guid} summary.addScriptTag() / addStyleTag() take url or content; path is refused.page.locator() describes an element and acts on it once it is ready (attached, visible, enabled, stable), retrying until its timeout. See Locator:class.
await page.locator("#email").fill("me@example.com");
await page.locator("button[type=submit]").click();
await Locator.race([page.locator(".accept"), page.locator(".agree")]).click();
Locator.race nests at most 4 deep and holds at most 64 locators.page.exposeFunction(name, fn) adds window[name] to the page; the page calls it and your function runs in your overseerScript.
await page.exposeFunction("report", (text) => page.meta.log("page says", text));
await page.evaluate(() => window.report(document.title));
page.waitForRequest() and page.waitForResponse() also take a predicate function. The predicate receives each request or response as an object with methods ( HTTPRequest:class: url(), method(), headers(), ...; HTTPResponse:class: url(), status(), headers(), ...). The wait still returns the plain summary object.
const res = await page.waitForResponse((r) => r.url().includes("/api/") && r.status() === 200);
await page.meta.log(res.url, res.status);
waitForNetworkIdle, getDefaultTimeout, getDefaultNavigationTimeout, isClosed, isJavaScriptEnabled, setJavaScriptEnabled, setBypassCSP, setCacheEnabled, emulateNetworkConditions, emulateIdleState, emulateVisionDeficiency, emulateFocusedPage, emulateCPUThrottling, removeScriptToEvaluateOnNewDocument, setBypassServiceWorker, isServiceWorkerBypassed, setDragInterception, isDragInterceptionEnabled, and page.coverage ( Coverage).wheel, reset, drag, dragEnter, dragOver, drop, dragAndDrop. Drag data that names files is refused.Commands on the main frame wait until the page has finished loading the request's url (when you set one), so they act on that page instead of the blank start page. The waits waitForNavigation, waitForSelector, waitForXPath, waitForFunction, waitForRequest, waitForResponse, waitForFrame and a locator's wait() / waitHandle() do not wait for it: they start at once, so they can observe that navigation.
page.touchscreen.touchStart(x, y) starts a touch and returns a TouchHandle:class with move(x, y) and end(); page.touchscreen.touchMove(x, y) and touchEnd() act on the first active touch. Element handles have touchStart(), touchMove(touch?) and touchEnd(). page.touchscreen.tap(x, y) works as before.
const touch = await page.touchscreen.touchStart(100, 200);
await touch.move(100, 50);
await touch.end();
page.tracing.start(options) records a Chrome performance trace and page.tracing.stop() returns it as base64 text (decode it to get the trace JSON, which Chrome DevTools opens).
await page.tracing.start({ screenshots: true });
await page.goto("https://example.com");
const trace = await page.tracing.stop(); // base64 text
page.meta.store.set("trace", trace);
stop() fails and returns no trace. A busy page can record about 15 MB in 5 seconds (less while it sits behind pjsc's automation page); the base64 text is a third larger than the trace (up to about 27 MB).start() when stop() was not called yet. stop() still returns that trace later in the same render; a trace never read is dropped when the render ends.start() fails while a trace is recording or waiting for its stop(), and stop() with no trace fails. Several stop() calls at the same time all get the same trace.screenshots: true adds screenshots), and a categories option is refused. path is ignored: no file is written on the server.These long-standing commands keep their behavior; where it differs from Puppeteer:
selectFrame, commands such as click, type, evaluate, $eval, content and waitForSelector act on every frame of the page (Puppeteer: the main frame only). Pass { selectFrame: "main" } or use page.frames()[0] for the main frame.page.type() waits 50 ms between keys unless you pass delay (Puppeteer: 0).goBack(), goForward(), reload() and setContent() do not wait for the load event unless you pass waitUntil (Puppeteer: "load").goto(), goBack(), goForward() and reload() do not return Puppeteer's response object, and waitForNavigation() returns a frame summary; use page.waitForResponse() or a frame's goto() / waitForNavigation() (page.frames()[0]) for one.addScriptTag() and addStyleTag() return nothing, and waitForSelector() and waitForFunction() return frame summaries, not handles; page.mainFrame() returns a frame summary. The same commands on a Frame:class return handles.page.waitForFunction("window.ready === true")); a function predicate is not supported on page.waitForFunction() (it resolves at once), use page.frames()[0].waitForFunction(fn, options, ...args) instead.userAgentMetadata) is ignored; pass { userAgent, userAgentMetadata } as one object instead.auth), the credentials answer only the website's login prompts and the proxy's login stays; on other renders they answer every login prompt, as in Puppeteer. null clears them.text(), json(), buffer()) and no request interception methods.signal options are not supported (an AbortSignal cannot cross into your overseerScript); waits, locator actions and predicate waits end with the render instead.page.mouse.drag() with drag interception off never settles (as in Puppeteer); the render then ends at maxWait. Call page.setDragInterception(true) first.page.coverage never reports pjsc's own scripts on your page.showOpenFilePicker, showSaveFilePicker, showDirectoryPicker) can be neither exposed nor removed.These Puppeteer features are intentionally not provided:
setRequestInterception, request.abort() / continue() / respond(). Use the request settings of the standard API (requestSettings.resourceModifier) instead.createCDPSession and similar.page.browser(), browserContext(), target().browser.newPage(), popups as page objects, page.close().uploadFile, path options that read or write a file on the server (screenshots return base64 text), drag data with files.The best docs to start diving in is the Page documentation.
overseerScript behavioroutputAsJson:true or set your renderType:automation to check for automation errors.overseerScript end with a newline character (\n)await keyword to wait for a command to finish before executing the next line.page.done() to signal your script is done earlier.page.meta.log("here") to check how far in your script you execute, and use the log function to output debug state for troubleshootingwaitForNavigationawait page.waitForNavigation(); will wait forever if no page navigation occurs after it is called.
For example, this will stall:
//navigate to example.com and wait till done
await page.goto("http://www.example.com");
//wait for a navigation to complete
await page.waitForNavigation();
but this will not stall, because the navigation doesn't occur until after waitForNavigation() is called:
//start navigating to example.com and continue script execution immediately
page.goto("http://www.example.com");
//wait for a navigation to complete
await page.waitForNavigation();
pageRequest.url property if your overseerScript uses page.goto()await in your overseerScript when you need to wait for something to occur.page.manualWait() to control when the page finishes, and make sure you call page.done() when ready.There are a few ways to obtain error details from your API Response
pageResponse.automation.errors node.pageResponse.errors though you may need to look under pageResponses.events and/or meta.trace to see error details.