Scrape paginated results and the items behind a Load more button
To scrape a Load more button or a paginated website you need every item, not the first twenty. Catalogs, job boards, review pages, directories and search results all split their lists across pages or hide them behind a button. The Scraping API covers both: one request per page for numbered pagination, and a short function for lists that grow on click.
The first request only returns the first page of results
Your rules work, the array comes back, and it has exactly as many items as the first page shows. The rest sit behind a page 2 link, a ?page= parameter or a Load more button that fetches the next batch with JavaScript, and none of them are in the HTML you scraped.
Writing a crawler for that means a queue, a loop and a browser you keep alive between pages, plus logic to notice the end of the list. For Load more buttons it means Puppeteer code that clicks, waits for the new items and repeats, running on infrastructure you have to host and scale.
Split the problem by pagination type. Numbered pages have their own URLs, so each one is a normal extraction request and they run in parallel. A Load more button needs clicks inside one browser session, which is what the function parameter is for: your Puppeteer code runs on Microlink’s browser, clicks until the list is complete and returns the items.
How to scrape a paginated website and a Load more button
Use the lightest tool that reaches every item. Page URLs first, a function only when the list grows in place. The browser interaction guide documents the click-and-wait pattern.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const rules = {
quotes: {
selectorAll: '.quote',
attr: {
text: { selector: '.text', attr: 'text' },
author: { selector: '.author', attr: 'text' }
}
}
}
const pages = [1, 2, 3, 4, 5].map(n => `https://quotes.toscrape.com/page/${n}/`)
const results = await Promise.all(pages.map(url => microlink.extract(url, rules)))
const quotes = results.flatMap(result => result.quotes)Each page is one request with the same rules, and the calls run concurrently with no throttling. An empty quotes array on a page past the end tells you where to stop.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const loadAll = async ({ page, clicks }) => {
for (let i = 0; i < clicks; i++) {
const button = await page.$('button.load-more')
if (!button) break
const count = await page.$$eval('.results li', items => items.length)
await button.click()
await page.waitForFunction(
n => document.querySelectorAll('.results li').length > n,
{},
count
)
}
return page.$$eval('.results li', items => items.map(el => el.textContent.trim()))
}
const { isFulfilled, value } = await microlink.function(
'https://example.com/catalog',
loadAll,
{ clicks: 10 }
)The function clicks, waits until more items exist than before, and stops when the button disappears or after the number of clicks you pass. clicks is a custom option, forwarded to the function as a named argument.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const { items } = await microlink.extract(
'https://example.com/catalog',
{ items: { selectorAll: '.results li', attr: 'text' } },
{ click: 'button.show-all', waitForSelector: '.results li' }
)click runs before the rules without writing a function. It fits a single Show all toggle or an accordion; a button that must be pressed repeatedly needs step 2.
- function Runs your Puppeteer code on the page. 15 seconds on free, up to 60 seconds on Pro.
- click Clicks the elements matching a CSS selector before extraction.
- waitForSelector Waits for the list items before the rules read them.
- scroll Scrolls one element into view. It triggers lazy content once, it does not scroll forever.
- selectorAll Returns every item on the page as an array.
Size the function to its time budget: every click waits for a network round trip on the target, so ten clicks on a slow site can exceed the 15 second free limit and return a TimeoutError. The function troubleshooting guide covers timeouts and the other plan-aware errors.
Why one request per page beats a long-lived crawler
Independent requests fail independently, cache independently and run in parallel. Keep the browser session only where the site forces you to.
Each page uses the same nested rules as scraping tables and repeated lists, so one rule set covers the whole listing.
The same runtime handles any browser automation you would write locally: run Puppeteer without hosting Chrome covers the runtime and its limits.
When not to: infinite feeds with thousands of items do not fit in one call. Look for the JSON endpoint the page calls as you scroll and read it page by page as cached JSON instead.
FAQ
How do I scrape a Load more button?
Can I scrape a paginated website in parallel?
Why does scroll not load every item on an infinite scroll page?
How many times can a function click Load more before it times out?
Does each scraped page count as a separate request?
Solve the next problem with the same API
Tables and lists to JSON arrays
Puppeteer without hosting Chrome
Custom fields from JavaScript apps
Any website to JSON
Bulk Markdown conversion with caching
PDFs in bulk
Ready to scrape every page of the list?
Parallel requests for numbered pages, a function for Load more. Start on the free tier and get past page one today.