await page.waitFor(5000); in this context causes a race condition. If the page doesn't load in 5 seconds, you can get a false negative. If the page loads quicker than 5 seconds, you've wasted time without reason. Only pick arbitrary delays as a last resort or if it's an intended part of the application logic.
A better approach is to use page.waitForSelector or page.waitForNavigation.
Secondly, I don't see results for the selector .rc .r. I'm not sure how stable Google's CSS selectors are, but .LC20lb seems safe on cursory glance for the time being.
Putting it together gives:
const puppeteer = require("puppeteer"); // ^16.2.0
let browser;
(async () => {
const searchQuery = "stack overflow";
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.goto("https://www.google.com/", {waitUntil: "domcontentloaded"});
await page.waitForSelector('input[aria-label="Search"]', {visible: true});
await page.type('input[aria-label="Search"]', searchQuery);
await Promise.all([
page.waitForNavigation({waitUntil: "domcontentloaded"}),
page.keyboard.press("Enter"),
]);
await page.waitForSelector(".LC20lb", {visible: true});
const searchResults = await page.$$eval(".LC20lb", els =>
els.map(e => ({title: e.innerText, link: e.parentNode.href}))
);
console.log(searchResults);
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
Output (yours may be different depending on what Google shows by the time you run the script):
[
{
title: 'Stack Overflow - Where Developers Learn, Share, & Build ...',
link: 'https://stackoverflow.com/'
},
{
title: 'Stack Overflow - Wikipedia',
link: 'https://en.wikipedia.org/wiki/Stack_Overflow'
},
{
title: 'Stack Overflow Blog - Essays, opinions, and advice on the act ...',
link: 'https://stackoverflow.blog/'
},
{
title: 'The Stack Overflow Podcast - Stack Overflow Blog',
link: 'https://stackoverflow.blog/podcast/'
},
{
title: 'Stack Overflow | LinkedIn',
link: 'https://www.linkedin.com/company/stack-overflow'
}
]
Another approach is to encode your search terms as a URL query parameter and navigate directly to https://www.google.com/search?q=your+query+here, saving a navigation and potential selector mishap.
As with many scraping tasks, since the goal is to grab simple hrefs from the document, you can speed up the script substantially by disabling JS, blocking resources or switching to fetch/cheerio and working with the static HTML. For example, the following script runs about 10x faster than Puppeteer:
const cheerio = require("cheerio"); // 1.0.0-rc.12
const query = "stack overflow";
const url = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
fetch(url, { // Node 18 or install node-fetch
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
}
})
.then(res => res.text())
.then(html => {
const $ = cheerio.load(html);
const searchResults = [...$(".yuRUbf a")].map(e => ({
title: $(e).text().trim(),
link: e.attribs.href,
}));
console.log(searchResults);
})
;
See also Click an element on first Google search result using Puppeteer.