GetGrid retrieves the matched selector data into an IGPALGrid
Duplicate-row detection is opt-in - call .WithDedupeData(true) on the grid to have GPAL skip duplicate rows. Duplicates are common in the wild and are not tied to infinite scroll: eBay, for example, returns duplicate search results on an ordinary paged run. It is off by default because it fingerprints every element to detect duplicates, which slows GetGrid down (noticeably on slower engines like Selenium); for large result sets, deduping the raw extracted data with another tool may be a better fit than deduping at the element level.
A single selector pointed at a table is read as a table: rows come from its rows, columns from the cells, and the header row names the columns. The header is a thead row, or a leading row whose cells are all th. It is span aware, so a cell spanning two columns repeats across both, and short rows are padded with GPAL.ErrorPlaceholder to keep every row the same width. Paging, WithAllThatMatch and duplicate-row detection work there as they do anywhere else. Point one selector at a set of tr rows instead of the table and the rows are read the same way, with no header. Browser.ColumnNames carries the names the table gave, and is empty on a column-per-selector read.
Two things to expect from it: each row is asked for its own cells, so a two hundred row table is two hundred requests, fast on Selenium and Puppeteer and slow on OttoMagic where every one is an HTTP round trip through the extension, so cap it with WithAllThatMatch when you only need the top of a table; and one selector aimed at a table now comes back as a grid of named columns rather than as a single column holding the table's text.
GetPageSource extracts the full HTML of the current page. GetSiteMap retrieves the sitemap XML and GetSiteMapUrls returns the individual URLs as a list. GetHydratedData extracts Next.js hydration data embedded in the page. GetLLMDigest produces a cleaned, structured summary of the page content optimized for use as LLM input. The Save variants (including SaveSiteMapUrls) write these outputs directly to files, and a sitemap walk that found nothing writes a result node saying so rather than an empty file. WithDefaultErrorPlaceholder sets the text written into a grid cell GetGrid could not read, so a missing value is visible rather than blank.
GetGrid's parameter is ref, not out. If you pass an existing grid it appends the results into that grid and leaves your reference intact - that is how you accumulate rows from several searches or pages into one master grid. Passing null hands you back GPAL's internal grid instead. Duplicate-row detection is opt-in via .WithDedupeData(true): use it when a site serves duplicate results and you want GPAL to remove them. Duplicates are not unique to infinite scroll - we saw eBay return duplicate results on a normal, non-infinite-scroll run. It is off by default and it slows GetGrid because it hashes each element's attributes to fingerprint every row; for big result sets, deduping the raw extracted data with another tool is often a better fit than deduping at the element level.
Not every site publishes a sitemap, and some answer a walk with a bot-check page instead of xml. GetSiteMapUrls hands back an empty list and publishes a WARNING naming the url it tried and what it read. SaveSiteMapUrls writes the file all the same, carrying one row of result and message in place of the url nodes, so nothing downstream has to tell an empty file apart from a run that broke. The result is no-sitemap-found when the site published none, and bot-check-page when an interstitial answered the walk, which reads as "try a signed-in profile" rather than "this site has no sitemap". In yaml that is "result: no-sitemap-found" with a "message:" beside it, in json an object with those two keys, in xml a row element holding both, and in csv a result,message header and one line. Read the result node first: a file with no url nodes in it has one.
//GetGrid adds rows into the grid you pass by ref (one row per matched element, columns per selector) - reuse a grid to accumulate across calls, or pass null for a fresh one. .WithDedupeData(true) makes GPAL drop duplicate rows (sites like eBay serve them); it is off by default because fingerprinting each element slows GetGrid.
// Extract a table into a grid (pass by ref)
var grid = GPAL
GPAL
// Accumulate the rows from several searches into ONE grid
var master = GPAL
foreach (var term in searchTerms)
GPAL
// Opt in to duplicate-row detection - some sites (e
var deduped = GPAL
GPAL
// Get page HTML source
string html;
GPAL
// Get all URLs from a sitemap
List<string> urls;
GPAL
Showing off some plain text in these paragraphs eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Here you can find different accents and emphasis sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
This is a link and how it could look like bestlinkinthebeautifulworld. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Here's just some classic bold text adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam notBoldSecondbestlinkinthebeautifulworld illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at adipisicing elit libero vel voluptas? Expedita, adipisicing facere dolores voluptatem ad ab rem assumenda soluta!
Other cuple of colors in case we want to emphasize several ways adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at voluptatem libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repudiandae quas consequuntur illo numquam assumenda autem exercitationem distinctio perspiciatis in natus. Eius dicta similique ipsam ipsa minima, nemo quae enim tempore.
GPAL
.CallIfNotFound(GenericCallIfNotFound)
.WithPublishToConsole();
//System.Drawing.Rectangle windowSize = new System.Drawing.Rectangle(10, 10, 1500, 1024);
// NOTE: we have to set browser = before we execute any steps
// this is due to the 'GenericCallIfNotFound' which might throw an exception, and BankScraper will not have the browser set when it calls scraper.Close()
// until the complete fluent line gets executed (meaning every step, meaning browser is not set until everything else succeeds)
browser = GPAL.Browser
.WithBrowserType(Enums.BrowserType.Chrome)
.WithProfileDataDirectory(ChromeProfileLocation)
.WithUseAutomationEngine(AutomationEngine.Selenium)
.WithWindowSize(new System.Drawing.Rectangle(0,0,1920,1080))
.ToGPALObject();