async function main() {
const browser = await puppeteer.launch({ headless: false});
const page = await browser.newPage();
await page.goto(url);
console.log(await page.title());
await page.click('a.search-name');
}
<div class="search-results-content-container">
<div class="search-results-image-container">
</div>
<div class="search-results-details-container">
<h4 class="search-name">Doe, Johm</h4>
<p class="search-title">
center of art
</p>
</div>
Hi I am using puppeteer to automate some tasks and I am moving along until it is time to click a h4 link that has a class called 'search-name'. I have tried adding await page.click('a.search-name') but that does not work. I also tried await page.click('h4.search-name') but no luck either. Can someone tell me how to make it click a link with an interchangeable url. It is the reason I want to specifically click h4 with a class of search-name.
The below code is what works!
await page.waitForSelector('.search-name')
await page.click('.search-name');
Related
My Html code has Button-tags that have same id "hoge".
If you get the selector from the Chrome Dev Tool, it will be the same for both "#hoge".
<html>
<body>
<button id="hoge">Hoge</button>
<div class="shadow">
#shadow-root (open)
<button id="hoge">Hoge</button>
</div>
</body>
</html>
I want to get element of button-tag in shadow dom with puppeteer.
But, my javascript code gets element of 1st button.
const element = page.waitForSelector("pierce/#hoge");
This is not what I want.
I'm guessing it's because you didn't specify a unique selector, but i don't know what is unique selector for puppeteer.
If you know how to solve this problem, please let me know.
Long story short
I work with puppeteer a lot and wanted this knowlegde to be in my bag. One way to select a shadow Element is by accessing the parent DOM Node's shadowRoot property. The answer is based on this article.
Accessing Shadow Root property
For your html example this does the trick:
const button = document.querySelector('.shadow').shadowRoot.querySelector('#hoge')
waiting
Waiting though is a little more complicated but can be acquired using page.waitForFunction().
Working Sandbox
I wrote this full working sandbox example on how to wait for a certain shadowRoot element.
index.html (located in same directory as app.js)
<html>
<head>
<script>
// attach shadowRoot after 6 seconds for emulating waiting..
setTimeout(() => {
const btn = document.getElementById('hoge')
const container = document.getElementsByClassName('shadow')[0]
const shadowRoot = container.attachShadow({
mode: 'open'
})
shadowRoot.innerHTML = `<button id="hoge" onClick="doStuff()">hoge2</button>`
console.log('attached!.')
}, 6000)
function doStuff() {
alert('shadow button clicked!')
}
</script>
</head>
<body>
<button id="hoge">Hoge</button>
<div class="shadow">
</div>
</body>
</html>
app.js (located in same directory as index.html)
var express = require('express')
var { join } = require('path')
var puppeteer = require('puppeteer')
//utility..
const wait = (seconds) => {
console.log('waiting', seconds, 'seconds')
return new Promise((res, rej) => {
setTimeout(res, seconds * 1000)
})
}
const runPuppeteer = async() => {
const browser = await puppeteer.launch({
defaultViewport: null,
headless: false
})
const page = await browser.newPage()
await page.goto('http://127.0.0.1:5000')
await wait(3)
console.log('page opened..')
// only execute this function within a page context!.
// for example in page.evaluate() OR page.waitForFunction etc.
// don't forget to pass the selector args to the page context function!
const selectShadowElement = (containerSelector, elementSelector) => {
try {
// get the container
const container = document.querySelector(containerSelector)
// Here's the important part, select the shadow by the parentnode of the
// actual shadow root and search within the shadowroot which is like another DOM!,
return container.shadowRoot.querySelector(elementSelector)
} catch (err) {
return null
}
}
console.log('waiting for shadow elemetn now.')
const containerSelector = '.shadow'
const elementSelector = '#hoge'
const result = await page.waitForFunction(selectShadowElement, { timeout: 15 * 1000 }, containerSelector, elementSelector)
if (!result) {
console.error('Shadow element not found..')
return
}
// since waiting succeeded we can get the elemtn now.
const element = await page.evaluateHandle(selectShadowElement, containerSelector, elementSelector)
try {
// click the element.
await element.click()
console.log('clicked')
} catch (err) {
console.log('failed to click..')
}
await wait(10)
}
var app = express()
app.get('/', (req, res) => {
res.sendFile(join(__dirname, 'index.html'))
})
app.listen(5000, '127.0.0.1', () => {
console.log('listening!')
runPuppeteer()
})
Start example
$ npm i express puppeteer
$ node app.js
Make sure to use headless:false option to see what's happening.
The application does this:
start a small express server only serving index.html on /
open puppeteer after server has started and wait for the shadow root element to appear.
Once it appeared, it gets clicked and an alert() is shown. => success!
Browser Support
Tested with chrome.
Cheers ' ^^
I'm new to puppeeteer I have this html https://jsitor.com/c0rM-YohL
<input type="input" class="test-element Input">
<select class="test-element Dropdown">
<option>test1</option>
<option>test2</option>
</select>
I tried with this chrome extension https://chrome.google.com/webstore/detail/puppeteer-ide/ilehdekjacappgghkgmmlbhgbnlkgoid?hl=en-US
await page.type(".test-element .Input", "new-test-app");
await page.select(".test-element .Dropdown", "option1");
but it doesn't work with
await page.goto('https://jsitor.com/c0rM-YohL', {
waitUntil: 'networkidle0',
});
await page.type(".test-element.Input", "new-test-app");
await page.select(".test-element.Dropdown", "option1");
remove the extra space from the selectors:
await page.type(".test-element.Input", "new-test-app");
await page.select(".test-element.Dropdown", "option1");
EDIT:
it's within an iframe, so you need to get iframe first:
const iframeSelector = '.iframe-container iframe';
await page.waitForSelector(iframeSelector, {
visible: true
});
const frameHandle = await page.$(iframeSelector);
const frame = await frameHandle.contentFrame();
await frame.type(".test-element.Input", "new-test-app");
let selected = await frame.select(".test-element.Dropdown", "test2");
console.log('selected', selected);
option1 is an element inside the <select> object. You need to type your text, click on the dropdown, and only then click on the relevant option.
I'm using puppeteer to convert some HTML to PNG using the screenshot method.
First, I fetch some SVG, then I create a page, and set the SVG as page content.
fetch(url)
.then(data => data.text())
.then((svgText) => {
// res.set('Content-Type', 'text/html');
const $ = cheerio.load(svgText)
return $.html()
})
.then(async (html) => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.setContent(html)
const file = await page.screenshot()
res.set('Content-Type', 'image/png');
await browser.close()
res.send(file)
})
.catch((err) => {
console.log(err);
logger.log({
level: 'error', message: 'GET /product', err
})
})
})
The problem is, texts in my SVG includes a specific font. This font is loaded using the #import CSS tag. If I set my method to return the HTML, the fonts are loaded, then, after a slight delay, they get applied to my texts. Unfortunately, when using the screenshot method, my texts are not styled anymore. I suppose it is because the screenshot is taken before the fonts are loaded and applied, therefore rendering a text with a fallback font.
Is there a way to make sure that the page is completely rendered before taking the screenshot ?
I tried using the page.on('load') event listener, but this doesn't change anything the script just runs forever.
For some reason I'm not able to click on an element that appears on a screen with puppeteer js.
Here is the code:
const getAllElements = await page.$$('._1Nk0C');
for (let [i, link] of getAllElements.entries()) {
try {
await link.click();
await sleep.sleep(4);
await link.click('._1NHYN _3d86A Ddtb4');
} catch (e) {
console.error(e);
}
}
Here I find all elements with '._1Nk0C'
It then clicks on the element which as it enlarge in forefront. await link.click();
I then try to click the button on screen. I can confirm this is on the screen.
await link.click('._1NHYN _3d86A Ddtb4');
Nothing happens. It doesn't error out just doesn't click on element. Am I missing something?
elementHandle.click([options]) does not accept a selector as an argument. If you're trying to click on an element in the page based on its selector try:
await link.click();
await sleep.sleep(4);
await page.click(selector);
I have the following element in html.
<a title="Download photo" href="https://example.com/photos/GXqvtQh1N9A/download?force=true" rel="nofollow" download="" target="_blank" class="_1QwHQ _1l4Hh _1CBrG _1zIyn xLon9 _1Tfeo _2L6Ut _2Xklx"><svg class="Apljk _11dQc" version="1.1" viewBox="0 0 32 32" width="32" height="32" aria-hidden="false"></a>
From the console when Chromium is open.
I can query it like so:
document.querySelector('a[title="Download photo"]');
I can create a reference to it:
var link = document.querySelector('a[title="Download photo"]');
I then can click on it like so:
link.click();
I try the same exact thing in Puppeteer.js in code. Same page.
for (const handle of getAllElements) {
try {
await handle.click();
const downloadButton = await page.$('a[title="Download photo"]');
downloadButton.click();
await sleep.sleep(2000);
} catch (e) {
console.error(e);
}
}
The initial handle.click() works and it opens me to the page I'm discussing here.
But then downloadButton.click() doesn't function.
I've also tried page.click(downloadButton).
I've also tried:
const downloadButton = await page.$('a[title="Download photo"]');
await downloadButton.click();
To ensure I'm working with the same page I visually do it while the page is on the screen.
Any ideas what's gong on?
As you mentioned it opens a layer on top each time you click on the image. Also, a[title="Download photo"] needs to be relative to the handle not page. Here is the working code:
for (const handle of getAllElements) {
await handle.click();
await handle.$eval('a[title="Download photo"]', el => el.click());
//allow download
await page._client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: './'
});
await new Promise(resolve => setTimeout(resolve, 2000));
//click on X to close the layer
await page.click('._1NHYN');
}