First things first: since Instagram is a heavy javascript-powered React application, the selectors you are after may not be available right after the page is loaded. So we should wait for them to appear in the DOM:
await page.waitForSelector('.v1Nh3.kIKUG._bz0w');
Now with page.evaluate we get the posts, but since you only want the links inside of those posts, let's grab them right away in the query:
const result = await page.evaluate(() => {
// Get elements into a NodeList
const elements = document.querySelectorAll('.v1Nh3.kIKUG._bz0w a');
...
}
But we cant convert the elements from Nodelist to an Array and just return them, because they're still DOM nodes, complex unserializable objects, and they need to be serializable to be able to return from page.evaluate. So instead of returning the complete nodes we'll just get what we need: urls from href attribute:
const result = await page.evaluate(() => {
// Get elements into a NodeList
const elements = document.querySelectorAll('.v1Nh3.kIKUG._bz0w a');
// Convert elements to an array,
// then for each item of that array only return the href attribute
const linksArr = Array.from(elements).map(link => link.href);
return linksArr;
});
Other ways to do it
In your question you mentioned page.$$ method. It is indeed applicable here to get handles of the objects we seek. But the code to iterate over them is not pretty:
const results = await page.$$('.v1Nh3.kIKUG._bz0w a')
for (const i in results)
{
console.log(await(await(await results[i]).getProperty("href")).jsonValue());
}
My favourite way to get those links though would be to use page.$$eval method:
const results = await page.$$eval('.v1Nh3.kIKUG._bz0w a', links => links.map(link => link.href))
It does exactly the same what we did in page.evaluate solution but in a much more concise way.