-
Notifications
You must be signed in to change notification settings - Fork 9.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle XPath selectors #537
Comments
There have been no plans to add xpath since it hasn't been requested much. However, the #382 would make it possible to implement polyfils atop of Puppeteer API in just a few lines of code - would it be good enough for you? |
XPath can be really useful and more powerful in some situations (selecting an element by it's text content is just one of the many use cases). But the #382 could work, XPath will just not be "out-of-the-box" and users will still have to implement it themselves, I guess? |
+1 , I , would like too XPath selectors to be handled. XPath is so much more powerful than CSS selectors when it comes to query the DOM. As it has previously been said, it's a pain not being able to select an element based on its text content (which often a lot), or to select nodes based on ancestors predicates ( for instant //*[@Class='whateverclass'][not(ancestor::div[@id='abcdef'])] ) Home-made methods have to be implemented to handle XPaths, it does the job, but I'm pretty sure we are not the only ones with this need. |
XPath is really expressive, for example I'll highlight 2 examples why @leeroybrun implementation after being implemented, will not be the end of the story and will open more fixes / iterations needed, as users start to try out and report failing use cases with XPath.
I personally prefer XPath anytime over CSS selectors, however there are much more use cases it offers than can be easily implemented. Leeroy's implementation could be a great first iteration but I believe there'll be more work to come to fully let XPath implementation work across Puppeteer's API. |
My implementation was really simple, and only tested on one of my personnal projects. Regarding the point 2, the It's basically a copy of the Anyway, here it is, in case it would be useful to someone else : async $$XPath(selector) {
const remoteObject = await this._rawEvaluate(selector => {
let results = [];
let query = document.evaluate(selector, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i=0, length = query.snapshotLength; i < length; ++i) {
results.push(query.snapshotItem(i));
}
return results;
}, selector);
const response = await this._client.send('Runtime.getProperties', {
objectId: remoteObject.objectId,
ownProperties: true
});
const properties = response.result;
const result = [];
const releasePromises = [utils.releaseObject(this._client, remoteObject)];
for (const property of properties) {
if (property.enumerable && property.value.subtype === 'node')
result.push(new ElementHandle(this._client, property.value, this._mouse));
else
releasePromises.push(utils.releaseObject(this._client, property.value));
}
await Promise.all(releasePromises);
return result;
} In my opinion, having XPath implemented in the puppeteer API will be a big advantage. |
+1 for XPath |
1 similar comment
+1 for XPath |
+1 |
@aslushnikov Now that JSHandles are merged, any advice on the best way to implement xpath? I'm especially wondering about the best way to use it with waitForSelector, where visible is incredibly helpful. |
@sradu the following script works fine for me: async function xpath(page, path) {
const resultsHandle = await page.evaluateHandle(path => {
let results = [];
let query = document.evaluate(path, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i=0, length = query.snapshotLength; i < length; ++i) {
results.push(query.snapshotItem(i));
}
return results;
}, path);
const properties = await resultsHandle.getProperties();
const result = [];
const releasePromises = [];
for (const property of properties.values()) {
const element = property.asElement();
if (element)
result.push(element);
else
releasePromises.push(property.dispose());
}
await Promise.all(releasePromises);
return result;
}
const pptr = require('puppeteer');
(async () => {
const browser = await pptr.launch();
const page = await browser.newPage();
await page.setContent('<div>hello!</div><div>other!</div>');
const [handle1, handle2] = await xpath(page, '//div');
console.log(await page.evaluate(e => e.textContent, handle1));
console.log(await page.evaluate(e => e.textContent, handle2));
await page.close();
await browser.close();
})(); |
XPath support would be awesome. |
+1 to this. I'd also love to see a function that waits for an XPath to "disappear", i.e. XPath not valid any longer, e.g. a popup is closed. |
+1 |
+1 |
If someone comes up with a |
@jpap I've developed a whole bunch of XPath methods, such as this one which might help you (similar to the Frame.waitForSelector method):
|
+1 |
1 similar comment
+1 |
+1 we also implemented our own XPath selector for text as the first thing after installing Puppeteer |
+1 We, too, are in dire need of XPath in puppeteer, as we are dealing with a so-called "web application" whose elements are - for the vast majority - not addressable by CSS-selectors. |
At CodeceptJS we implemented XPath locators support for Puppeteer Anyway, native XPath support from Puppeteer would be appreciated ) |
#1620 is proposed and introduces:
⬇️ Mini-poll for the puppeteer community: ⬇️ Please 👍 this comment if you would use |
This patch adds xpath support with the following methods: - page.xpath - frame.xpath - elementHandle.xpath Fixes #537
Has this not been added to the documentation yet? I see there's |
@ctsstc the |
+1 |
1 similar comment
+1 |
+1 for xpath supporting |
As of Puppeteer v1.7.0, xpath is well-supported. Wait for nodes using xpath: |
It's not that usefull as casperjs's https://github.com/casperjs/casperjs/blob/master/modules/clientutils.js#L590
My 2 cents :) |
Why is it not that useful as casperjs's functions? page.$x(expression) does exactly the same thing. Puppeteer does even more, the waitForXPath feature is really useful :) |
A sample use case: __utils__.getElementsByXPath('//xpath/expression')
.forEach((node) => {
__utils__.getElementByXPath('//xpath/another_expression_on_specific_node', node)
}) How you do it on contextual |
@sputnick-dev the const handles = await page.$x('//xpath/expression');
for (const e of handles)
await e.$x('//xpath/another_expression_on_specific_node'); |
@aslushnikov i guess using count isn't supported
I get this error
|
I tried to use the below code to for waitForXpath(), but it didn't work out. Can someone tell me what wrong I am doing and pls provide code for waitForXpath(), if it has |
Tried with |
You are missing the right syntax: waitForXPath , with valid case, not
waitForXpath
…On Tue, 18 Jun 2019, 22:21 Subrato Sarkar, ***@***.***> wrote:
Tried with
await ***@***.*** <https://github.com/value>='Finish']",
{ timeout: 8000 }), but getting the below issue
TypeError: page.waitForXpath is not a function
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#537>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AACQQEDJCKAXKARWD6ISCF3P3E7VDANCNFSM4DYJNY5Q>
.
|
@xprudhomme : Yes it worked. Thanks much |
I was trying to implement a select function in puppeteer using xpath. |
The $x method returns a Promise which when resolved is an Array of ElementHandles. There do not seem to be any "select" method on ElementHandle objects... |
okay, then how to I select a value from dropdown, can you please share the code if you have it, it would be really helpful for me. |
Did anyone encounter an issue with using brackets? For example,
In my case is failing with |
Is it planned/wanted to implement XPath selectors?
This can be done quite easily, but I don't know if it's a choice to not handle them?
I've implemented two methods in my own code to handle them :
I can make a PR if you're interested.
The text was updated successfully, but these errors were encountered: