Selenium-based engines need a matching driver executable (chromedriver, msedgedriver, geckodriver) on disk. This tutorial uses GPAL to detect the installed browser version, find the matching driver download on the vendor's release page, download it, and unzip the driver into your drivers folder.
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.WithPublishToDebug().WithPublishToConsole();
Selector driverLinkSelector = (Selector)GPAL.Selector
.WithXPath("//a[contains(@href, 'geckodriver') and contains(@href, 'geckodriver-v0.36.0-win32.zip')]")
.CallIfNotFound(CallIfNotFound)
.CallIfFound(CallIfFound)
.WithStopOnNotFound(true);
IBrowser browser = GPAL.Browser
.WithBrowserType(BrowserType.FireFox)
.WithUseAutomationEngine(AutomationEngine.Selenium)
.WithOpenPDFExternally(false)
.WithDriverLocation(@"c:drivers")
.ToGPALObject();
browser
.GoTo("https://example.com/mozilla/geckodriver/releases")
.WithSelector(driverLinkSelector)
.WithAllThatMatch();
browser.StartWorkflow();
The selector looks for an anchor whose href contains both 'geckodriver' and the specific release filename you want. CallIfFound and CallIfNotFound register the callbacks GPAL will invoke once it knows whether that link exists, and WithStopOnNotFound(true) halts the workflow if it doesn't.
Selector driverLinkSelector = (Selector)GPAL.Selector
.WithXPath("//a[contains(@href, 'geckodriver') and contains(@href, 'geckodriver-v0.36.0-win32.zip')]")
.CallIfNotFound(CallIfNotFound)
.CallIfFound(CallIfFound)
.WithStopOnNotFound(true);
Hard-coding the release filename (here geckodriver-v0.36.0-win32.zip) keeps the download reproducible. Swap in chromedriver-win64.zip or msedgedriver.zip patterns for Chrome or Edge, matched against the locally installed browser version.
GoTo loads the releases page, WithSelector queues the driver-link selector, and WithAllThatMatch tells GPAL to evaluate every match. StartWorkflow runs the queued steps and fires CallIfFound or CallIfNotFound based on what was found.
browser
.GoTo("https://example.com/mozilla/geckodriver/releases")
.WithSelector(driverLinkSelector)
.WithAllThatMatch();
browser.StartWorkflow();
Once the link is found, CallIfFound reads the href off the matched element, closes the current browser (since the driver file it's about to replace may be in use), downloads the zip, and extracts the driver executable into the drivers folder.
public static CallIfStatus CallIfFound(IBrowser browser, List<GPALElement> foundElements,
List<GPALElement> matchedElements, Selector selector, bool matchedAll)
{
string path = @"c:drivers";
string filename = "geckodriver.zip";
string driver = "geckodriver.exe";
GPALElement webElement = matchedElements[0];
string href = webElement.GetAttribute("href");
browser.Close(true); // release the driver file we are about to replace
System.Threading.Thread.Sleep(2_000);
using (var webClient = new System.Net.WebClient())
webClient.DownloadFile(href, path + filename);
using (var archive = System.IO.Compression.ZipFile.OpenRead(path + filename))
{
var entry = archive.Entries.FirstOrDefault(e =>
e.Name.Equals(driver, StringComparison.OrdinalIgnoreCase));
if (entry != null)
{
string destination = System.IO.Path.Combine(path, entry.Name);
if (System.IO.File.Exists(destination))
System.IO.File.Delete(destination);
entry.ExtractToFile(destination);
}
}
System.IO.File.Delete(path + filename);
return CallIfStatus.Handled;
}
On Windows, the driver executable is locked while its browser session is running. Call Close(true) and give the process a moment to exit before downloading a new copy over it.
If the release link can't be found - the page layout changed, or the version string no longer matches - CallIfNotFound shows a message and returns CallIfStatus.Terminate, stopping the workflow rather than continuing with a missing driver.
public static CallIfStatus CallIfNotFound(IBrowser browser, List<GPALElement> foundElements,
List<GPALElement> matchedElements, Selector selector, bool matchedAll)
{
MessageBox.Show($"Unable to find {selector.Name}");
return CallIfStatus.Terminate;
}
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();