从 ElementHandle 获取 属性
getting property from ElementHandle
我在 Node.js 模块中使用 Puppeteer。我使用 XPath 选择器检索 HTML 元素,需要提取文本 属性.
目前我使用:
// Get the element
let ele = await element.$x(`//div[@class="g"][${i}]/div/div/h3/a`);
// Get the text property
const title = await(await ele[0].getProperty('text')).jsonValue();
有什么办法可以做到不那么冗长吗?
我更喜欢使用 eval()
函数,这样我可以使用不那么冗长的代码:
page.eval(() => {
let element = document.querySelector('#mySelector')
return element.innerText
}).then(text => {
console.log(text)
})
您还可以传递您之前抓取的元素,例如您的 ele
var:
使用 Promise 语法
page.eval(element => {
return element.innerText
}, ele).then(text => {
// Do whatever you want with text
})
使用 async/await 语法
const text = await page.eval(element => element.innerText), ele)
// Do whatever you want with text
...或者写一个小辅助函数。
public async GetProperty(element: ElementHandle, property: string): Promise<string> {
return await (await element.getProperty(property)).jsonValue();
}
使用:
let inner = await GetProperty(ele, 'innerHTML');
我宁愿为缺少的方法扩展 ElementHandle,例如:
// puppeteer@1.9.0
let { ElementHandle } = require( "puppeteer/lib/ExecutionContext" );
// puppeteer@1.12
if ( ElementHandle === undefined ) {
ElementHandle = require( "puppeteer/lib/JSHandle" ).ElementHandle;
}
/**
* Set value on a select element
* @param {string} value
* @returns {Promise<Undefined>}
*/
ElementHandle.prototype.select = async function( value ) {
await this._page.evaluateHandle( ( el, value ) => {
const event = new Event( "change", { bubbles: true });
event.simulated = true;
el.querySelector( `option[value="${ value }"]` ).selected = true;
el.dispatchEvent( event );
}, this, value );
};
/**
* Check if element is visible in the DOM
* @returns {Promise<Boolean>}
**/
ElementHandle.prototype.isVisible = async function(){
return (await this.boundingBox() !== null);
};
/**
* Get element attribute
* @param {string} attr
* @returns {Promise<String>}
*/
ElementHandle.prototype.getAttr = async function( attr ){
const handle = await this._page.evaluateHandle( ( el, attr ) => el.getAttribute( attr ), this, attr );
return await handle.jsonValue();
};
/**
* Get element property
* @param {string} prop
* @returns {Promise<String>}
*/
ElementHandle.prototype.getProp = async function( prop ){
const handle = await this._page.evaluateHandle( ( el, prop ) => el[ prop ], this, prop );
return await handle.jsonValue();
};
一旦您在代码中导入此模块一次,您就可以按如下方式使用手柄:
const elh = await page.$( `#testTarget` );
console.log( await elh.isVisible() );
console.log( await elh.getAttr( "class" ) );
console.log( await elh.getProp( "innerHTML" ) );
我的方式
async function getVisibleHandle(selector, page) {
const elements = await page.$$(selector);
let hasVisibleElement = false,
visibleElement = '';
if (!elements.length) {
return [hasVisibleElement, visibleElement];
}
let i = 0;
for (let element of elements) {
const isVisibleHandle = await page.evaluateHandle((e) => {
const style = window.getComputedStyle(e);
return (style && style.display !== 'none' &&
style.visibility !== 'hidden' && style.opacity !== '0');
}, element);
var visible = await isVisibleHandle.jsonValue();
const box = await element.boxModel();
if (visible && box) {
hasVisibleElement = true;
visibleElement = elements[i];
break;
}
i++;
}
return [hasVisibleElement, visibleElement];
}
用法
let selector = "a[href='https://example.com/']";
let visibleHandle = await getVisibleHandle(selector, page);
if (visibleHandle[1]) {
await Promise.all([
visibleHandle[1].click(),
page.waitForNavigation()
]);
}
在接受的答案中提到了 page.eval()
,但是,对于 puppeteer 这样的方法从未存在过,我认为真正的意思实际上是 page.evaluate()。
但是,使用 page.evaluate()
需要您将操作分成两部分(一个用于获取元素,一个用于 select 值)。
Is there any way to do this not as verbose?
在这种情况下,page.$eval() 似乎更合适,因为它允许您直接传递 selector 作为参数,从而减少您需要引入的操作或变量的数量:
现在在您的特定情况下,您不仅要在整个页面上而且要在 ElementHandle
上执行 $eval
,这是可能的,因为 May 9, 2018 via elementHandle.$eval():
This method runs document.querySelector within the element and passes it as the first argument to pageFunction.
这转化为您的示例如下(此处使用 css select 或代替 xpath):
await elementHandle.$eval('/div/div/h3/a', el => el.text);
我在 Node.js 模块中使用 Puppeteer。我使用 XPath 选择器检索 HTML 元素,需要提取文本 属性.
目前我使用:
// Get the element
let ele = await element.$x(`//div[@class="g"][${i}]/div/div/h3/a`);
// Get the text property
const title = await(await ele[0].getProperty('text')).jsonValue();
有什么办法可以做到不那么冗长吗?
我更喜欢使用 eval()
函数,这样我可以使用不那么冗长的代码:
page.eval(() => {
let element = document.querySelector('#mySelector')
return element.innerText
}).then(text => {
console.log(text)
})
您还可以传递您之前抓取的元素,例如您的 ele
var:
使用 Promise 语法
page.eval(element => {
return element.innerText
}, ele).then(text => {
// Do whatever you want with text
})
使用 async/await 语法
const text = await page.eval(element => element.innerText), ele)
// Do whatever you want with text
...或者写一个小辅助函数。
public async GetProperty(element: ElementHandle, property: string): Promise<string> {
return await (await element.getProperty(property)).jsonValue();
}
使用:
let inner = await GetProperty(ele, 'innerHTML');
我宁愿为缺少的方法扩展 ElementHandle,例如:
// puppeteer@1.9.0
let { ElementHandle } = require( "puppeteer/lib/ExecutionContext" );
// puppeteer@1.12
if ( ElementHandle === undefined ) {
ElementHandle = require( "puppeteer/lib/JSHandle" ).ElementHandle;
}
/**
* Set value on a select element
* @param {string} value
* @returns {Promise<Undefined>}
*/
ElementHandle.prototype.select = async function( value ) {
await this._page.evaluateHandle( ( el, value ) => {
const event = new Event( "change", { bubbles: true });
event.simulated = true;
el.querySelector( `option[value="${ value }"]` ).selected = true;
el.dispatchEvent( event );
}, this, value );
};
/**
* Check if element is visible in the DOM
* @returns {Promise<Boolean>}
**/
ElementHandle.prototype.isVisible = async function(){
return (await this.boundingBox() !== null);
};
/**
* Get element attribute
* @param {string} attr
* @returns {Promise<String>}
*/
ElementHandle.prototype.getAttr = async function( attr ){
const handle = await this._page.evaluateHandle( ( el, attr ) => el.getAttribute( attr ), this, attr );
return await handle.jsonValue();
};
/**
* Get element property
* @param {string} prop
* @returns {Promise<String>}
*/
ElementHandle.prototype.getProp = async function( prop ){
const handle = await this._page.evaluateHandle( ( el, prop ) => el[ prop ], this, prop );
return await handle.jsonValue();
};
一旦您在代码中导入此模块一次,您就可以按如下方式使用手柄:
const elh = await page.$( `#testTarget` );
console.log( await elh.isVisible() );
console.log( await elh.getAttr( "class" ) );
console.log( await elh.getProp( "innerHTML" ) );
我的方式
async function getVisibleHandle(selector, page) {
const elements = await page.$$(selector);
let hasVisibleElement = false,
visibleElement = '';
if (!elements.length) {
return [hasVisibleElement, visibleElement];
}
let i = 0;
for (let element of elements) {
const isVisibleHandle = await page.evaluateHandle((e) => {
const style = window.getComputedStyle(e);
return (style && style.display !== 'none' &&
style.visibility !== 'hidden' && style.opacity !== '0');
}, element);
var visible = await isVisibleHandle.jsonValue();
const box = await element.boxModel();
if (visible && box) {
hasVisibleElement = true;
visibleElement = elements[i];
break;
}
i++;
}
return [hasVisibleElement, visibleElement];
}
用法
let selector = "a[href='https://example.com/']";
let visibleHandle = await getVisibleHandle(selector, page);
if (visibleHandle[1]) {
await Promise.all([
visibleHandle[1].click(),
page.waitForNavigation()
]);
}
在接受的答案中提到了 page.eval()
,但是,对于 puppeteer 这样的方法从未存在过,我认为真正的意思实际上是 page.evaluate()。
但是,使用 page.evaluate()
需要您将操作分成两部分(一个用于获取元素,一个用于 select 值)。
Is there any way to do this not as verbose?
在这种情况下,page.$eval() 似乎更合适,因为它允许您直接传递 selector 作为参数,从而减少您需要引入的操作或变量的数量:
现在在您的特定情况下,您不仅要在整个页面上而且要在 ElementHandle
上执行 $eval
,这是可能的,因为 May 9, 2018 via elementHandle.$eval():
This method runs document.querySelector within the element and passes it as the first argument to pageFunction.
这转化为您的示例如下(此处使用 css select 或代替 xpath):
await elementHandle.$eval('/div/div/h3/a', el => el.text);