Finds the pages on a site from its sitemap or front page, reads each one, and adds a row per page to your sheet.
Uses
Google Sheets
Jina AI
OpenAI
Read a whole site into spreadsheet rows
Give it one web address and run it. Its sitemap is read to find the pages, or, with no usable sitemap, the front page is read once and its links taken instead. Each page found is read as clean text, an AI step pulls out the details you asked for, and one row per page goes into your Google Sheet.
Each appended row holds the page address, the page title, then your details in the order you listed, with 'n/a' wherever the page does not say. Links on those pages are never followed, and every address must sit under the site you named, so a link to another site or a look-alike domain is dropped. One run reads a bounded number of pages, and any address already in the sheet's first column is skipped, so a second run picks up where the last one stopped. A page that will not load is named in the run log and the rest carry on.
Before the first run you need a sheet headed 'Page address', 'Page title' and then your fields in the same order, since the columns cannot be named for you.
siteUrl to blank.Set siteUrl to blank.fieldsToExtract to blank.Set fieldsToExtract to blank.spreadsheetUrl to blank.Set spreadsheetUrl to blank.sheetName to Sheet1.Set sheetName to Sheet1.sitemapUrl to blank.Set sitemapUrl to blank.maxPages to 20.Set maxPages to 20.pageCeiling to 50.Set pageCeiling to 50.maxPageCharacters to 12000.Set maxPageCharacters to 12000.columnSeparator to |||.Set columnSeparator to |||.siteUrl split by :// and save it as siteScheme.Get the first item of siteUrl split by :// and save it as siteScheme.siteUrl split by :// and save it as siteHostAndPath.Get the last item of siteUrl split by :// and save it as siteHostAndPath.siteHostAndPath split by / and save it as siteHost.Get the first item of siteHostAndPath split by / and save it as siteHost.siteHost is emptyIf siteHost is emptysiteRoot to siteScheme://siteHost/.Set siteRoot to siteScheme://siteHost/.sitemapUrl is emptyIf sitemapUrl is emptysitemapUrl to siteRootsitemap.xml.Change sitemapUrl to siteRootsitemap.xml.maxPages multiplied by 1 to the nearest integer and save it as pageLimit.Round maxPages multiplied by 1 to the nearest integer and save it as pageLimit.pageLimit is less than 1If pageLimit is less than 1pageLimit is greater than pageCeilingIf pageLimit is greater than pageCeilingpageLimit to pageCeiling.Change pageLimit to pageCeiling.pageCeiling as text pages, so 'maxPages' was treated as pageCeiling as text. Run it again to pick up where it left off.Show in the output: One run never reads more than pageCeiling as text pages, so 'maxPages' was treated as pageCeiling as text. Run it again to pick up where it left off.maxPageCharacters multiplied by 1 to the nearest integer and save it as pageCharacterLimit.Round maxPageCharacters multiplied by 1 to the nearest integer and save it as pageCharacterLimit.discovered to an empty list.Set discovered to an empty list.sitemapErrorIf a step below fails, save the error as sitemapErrorsitemapUrl and save it as sitemapResponse.Fetch sitemapUrl and save it as sitemapResponse.sitemapResponse.ok is yesIf sitemapResponse.ok is yessitemapResponse.body and save it as sitemapText.Trim sitemapResponse.body and save it as sitemapText.sitemapText in lowercase doesn't contain <sitemapindexIf sitemapText in lowercase doesn't contain <sitemapindexsitemapText and save it as discovered.Extract all web addresses from sitemapText and save it as discovered.discovered as text address(es) listed in it.Show in the output: Read the sitemap and found the length of discovered as text address(es) listed in it.The sitemap is an index of other sitemaps rather than a list of pages, so the links on the front page are used instead..Show in the output: The sitemap is an index of other sitemaps rather than a list of pages, so the links on the front page are used instead..sitemapError.message with sitemapUrl replaced by the sitemap address.Show in the output: Could not read the sitemap: sitemapError.message with sitemapUrl replaced by the sitemap address.discovered is emptyIf discovered is emptyfrontPageErrorIf a step below fails, save the error as frontPageErrorsiteUrl with Jina. Save it as frontPage.Read the page at siteUrl with Jina. Save it as frontPage.frontPageLink in frontPage.links:For each frontPageLink in frontPage.links:frontPageLink.url to the end of discovered and save it as discovered. Add frontPageLink.url to the end of discovered and save it as discovered.discovered as text link(s) on the front page.Show in the output: Found the length of discovered as text link(s) on the front page.frontPageError.message with siteUrl replaced by the site address.Show in the output: Could not read the front page to find its links: frontPageError.message with siteUrl replaced by the site address.discovered is emptyIf discovered is emptyalreadyInTheSheet to an empty list.Set alreadyInTheSheet to an empty list.sheetErrorIf a step below fails, save the error as sheetErrorsheetName in spreadsheetUrl. Save it as recordedPages.Read column 1 of sheetName in spreadsheetUrl. Save it as recordedPages.recordedPages.values into one list and save it as alreadyInTheSheet.Combine [] and recordedPages.values into one list and save it as alreadyInTheSheet.Could not read the page addresses already in the sheet, so every page found will be read this run..Show in the output: Could not read the page addresses already in the sheet, so every page found will be read this run..candidatePages to an empty list.Set candidatePages to an empty list.siteRoot] and discovered into one list and save it as everythingFound.Combine a list of [siteRoot] and discovered into one list and save it as everythingFound.foundLink in everythingFound with duplicates removed:For each foundLink in everythingFound with duplicates removed:linkErrorIf a step below fails, save the error as linkErrorfoundLink and save it as trimmedLink.Trim foundLink and save it as trimmedLink.trimmedLink split by # and save it as pageAddress.Get the first item of trimmedLink split by # and save it as pageAddress.pageAddress/ doesn't start with siteRoot (ignoring case)If pageAddress/ doesn't start with siteRoot (ignoring case)pageAddress ends with .xml (ignoring case) is yesIf pageAddress ends with .xml (ignoring case) is yesalreadyInTheSheet contains pageAddress is yesIf alreadyInTheSheet contains pageAddress is yescandidatePages contains pageAddress is yesIf candidatePages contains pageAddress is yespageAddress to the end of candidatePages and save it as candidatePages.Add pageAddress to the end of candidatePages and save it as candidatePages.Skipped one discovered link because it had no address..Show in the output: Skipped one discovered link because it had no address..candidatePages is emptyIf candidatePages is emptypageLimit items of candidatePages and save it as pagesToRead.Take the first pageLimit items of candidatePages and save it as pagesToRead.candidatePages as text page(s) on this site that are not in the sheet yet; reading the length of pagesToRead as text of them this run.Show in the output: Found the length of candidatePages as text page(s) on this site that are not in the sheet yet; reading the length of pagesToRead as text of them this run.rowsWritten to 0.Set rowsWritten to 0.pagesWithoutRows to an empty list.Set pagesWithoutRows to an empty list.pageAddress in pagesToRead:For each pageAddress in pagesToRead:pageErrorIf a step below fails, save the error as pageErrorpageAddress with Jina. Save it as page.Read the page at pageAddress with Jina. Save it as page.page.content and save it as pageText.Trim page.content and save it as pageText.pageTitle to blank.Set pageTitle to blank.titleErrorIf a step below fails, save the error as titleErrorpage.title and save it as pageTitle.Trim page.title and save it as pageTitle.pageTitle to blank.Change pageTitle to blank.pageText isn't emptyIf pageText isn't emptypromptTask to You are reading one page of a website and filling in one row of a spreadsheet.Set promptTask to You are reading one page of a website and filling in one row of a spreadsheet.
Pull out these details, in this exact order:
fieldsToExtract.promptRules to Set promptRules to
Reply with ONE line and nothing else. Separate the values with columnSeparator in the order listed above. Do not repeat the field names, do not add headings, bullets, numbering or any commentary, and do not wrap values in quotes. Write n/a for anything the page does not say.promptPage to Set promptPage to
Page address: pageAddress
Page title: pageTitle
Page content:
pageText truncated to pageCharacterLimit characters.prompt to promptTask plus promptRules plus promptPage.Set prompt to promptTask plus promptRules plus promptPage.prompt. Save it as extraction.Ask OpenAI: prompt. Save it as extraction.extraction.response_text and save it as answerLine.Trim extraction.response_text and save it as answerLine.pageAddress, pageTitle] and save it as row.Create a list of [pageAddress, pageTitle] and save it as row.extractedValue in answerLine split by columnSeparator:For each extractedValue in answerLine split by columnSeparator:extractedValue to the end of row and save it as row. Add extractedValue to the end of row and save it as row.sheetName in spreadsheetUrl.Add a row to sheetName in spreadsheetUrl.rowsWritten.Add 1 to rowsWritten.pageAddress to the end of pagesWithoutRows and save it as pagesWithoutRows.Add pageAddress to the end of pagesWithoutRows and save it as pagesWithoutRows.pageAddress to the end of pagesWithoutRows and save it as pagesWithoutRows.Add pageAddress to the end of pagesWithoutRows and save it as pagesWithoutRows.pageError.message.Show in the output: Could not read '@pageAddress': pageError.message.rowsWritten as text row(s) to '@sheetName'.Show in the output: Finished. Wrote rowsWritten as text row(s) to '@sheetName'.pagesWithoutRows isn't emptyIf pagesWithoutRows isn't emptypagesWithoutRows joined by , .Show in the output: No row was written for: pagesWithoutRows joined by , .