A workflow that closes an ad nested inside an iframe using a persistent selector, then left-clicks a download link and saves the resulting PDF to disk. Covers GPALFile as a download target, LeftClickAndDownload, and persistent selectors for nags that can reappear at any time.
Here's the whole workflow, start to finish. Each piece is broken down and explained below.
using GenerallyPositive;
using GenerallyPositive.Browser;
using static GenerallyPositive.Enums;
GPAL
.WithExceptionHandler(ExceptionHandler)
.WithInformationHandler(InformationHandler)
.WithImageMatchingPercentage(80)
.WithSimulateMouseMovement(false)
.WithTypingDelay(100)
.WithPublishToConsole();
string url = "https://example.com/sample-downloads/";
Selector downloadLink = (Selector)GPAL.Selector
.WithXPath("//a[normalize-space()='Download sample pdf file']")
.WithCSS("#table-files > tbody > tr:nth-of-type(1) > td.file-link > a")
.WithOffsetX(115)
.WithOffsetY(18);
GPALFile downloadTarget = @"c:sdisample.pdf";
Selector adIframe = (Selector)GPAL.CssSelector("iframe").MatchText("Advertisement").WithSelectorName("Ad Iframe");
Selector closeByText = (Selector)GPAL.Selector
.WithText("Close").CallIfFound(CloseCallIfFound).WithSelectorName("Close by Close");
Selector closeByButton = (Selector)GPAL.Selector
.WithCSS("#dismiss-button").CallIfFound(CloseCallIfFound).WithSelectorName("Close by X");
IBrowser browser = GPAL.Browser
.WithBrowserType(BrowserType.Chrome)
.WithUseAutomationEngine(AutomationEngine.Selenium)
.WithLoadImages(true)
.WithBlockPopUps(true)
.WithOverwriteExistingFile(true)
.WithPromptForDownload(true)
.WithDriverLocation(@"C:drivers")
.WithOpenPDFExternally(true)
.ToGPALObject();
browser.GoTo(url);
browser.WithPersistentSelector(closeByText).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);
browser.WithPersistentSelector(closeByButton).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);
browser
.WithSelector(downloadLink)
.LeftClickAndDownload(downloadTarget);
browser.Close(true);
static CallIfStatus CloseCallIfFound(IBrowser browser, List<GPALElement> foundElements, List<GPALElement> matchedElements, Selector selector, bool matchedAll)
{
if (1 == matchedElements.Count && false == matchedElements[0].TagName.ToLower().Equals("script"))
matchedElements[0].Click();
return CallIfStatus.Handled;
}
downloadTarget is a GPALFile, not a plain string. Assign it from a full path the same way you'd assign a string, but it carries file metadata GPAL can use - here it's the destination LeftClickAndDownload writes the PDF to once the click triggers the browser's download.
GPALFile downloadTarget = @"c:sdisample.pdf";
browser
.WithSelector(downloadLink)
.LeftClickAndDownload(downloadTarget);
WithOverwriteExistingFile(true) lets a repeat run replace the file at the same path instead of appending (1), (2), and so on. WithPromptForDownload(true) and WithOpenPDFExternally(true) tell the browser settings to behave like a normal download dialog rather than rendering the PDF inline.
Some ads load inside an iframe and can reappear after the page settles, so a one-time LeftClick isn't enough. WithPersistentSelector registers a selector GPAL keeps watching for; InPersistentIframe scopes that watch to elements inside a matching iframe (here, any iframe whose text contains "Advertisement"); PersistentCallIfFound wires up the callback that closes it whenever it shows up.
Selector adIframe = (Selector)GPAL.CssSelector("iframe").MatchText("Advertisement").WithSelectorName("Ad Iframe");
Selector closeByText = (Selector)GPAL.Selector
.WithText("Close").CallIfFound(CloseCallIfFound).WithSelectorName("Close by Close");
Selector closeByButton = (Selector)GPAL.Selector
.WithCSS("#dismiss-button").CallIfFound(CloseCallIfFound).WithSelectorName("Close by X");
browser.WithPersistentSelector(closeByText).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);
browser.WithPersistentSelector(closeByButton).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);
It's common to register more than one persistent selector for the same nag - one that matches by visible text ("Close") and one that matches by a stable CSS id (#dismiss-button). Whichever one actually appears on a given page load triggers the callback.
CloseCallIfFound is a CallIfDelegate - the same callback shape used by CallIfFound and CallIfNotFound elsewhere in GPAL. It receives the matched elements and clicks the one that matched, as long as it isn't a stray <script> tag (which can sometimes match broad selectors like WithText).
static CallIfStatus CloseCallIfFound(IBrowser browser, List<GPALElement> foundElements, List<GPALElement> matchedElements, Selector selector, bool matchedAll)
{
if (1 == matchedElements.Count && false == matchedElements[0].TagName.ToLower().Equals("script"))
matchedElements[0].Click();
return CallIfStatus.Handled;
}
With the ad-closer registered, the rest of the workflow is a single chain: select the download link, and call LeftClickAndDownload with the GPALFile target. GPAL clicks the element and waits for the resulting file to land at the path you specified, then Close(true) shuts down the driver.
browser
.WithSelector(downloadLink)
.LeftClickAndDownload(downloadTarget);
browser.Close(true);
Persistent selectors keep watching for the rest of the browser's life, not just for the next action. Register them once after navigation and they'll keep firing in the background - including while LeftClickAndDownload is waiting for the file to finish writing.
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();