Selenium Sucks, make way for PlayWright
About a year and a half ago I made a discord bot called RuneMaster its a simple League of Legends Discord bot now running in 60+ different discord servers and is featured in
top.gg. In the making of the bot I needed to do some web scrapping, let me tell you of why I'll never touch Selenium again.Like many other software stories, the desire to make this came from the desire to automate away a mild inconvenience. After one of my friends who I played League of Legends with at the time told me that he hated the fact that since he just came back he didn't know the runes or build for any of the new champions without having to open a web browser and look it up. This set off an idea in my head to develop this bot that would run on discord, an application that almost all gamers run on a daily basis. With the sole purpose of making information like the one you'd find on op.gg easily available through the Discord interface. I thought this wouldn't be too hard since it was going to mostly just be scaffolding work of connecting different and forwarding information, but boy was I wrong...
When I began I used Selenium, thinking it was the most industry standard thing at the time, It'd probably be well documented and I would encounter few bugs. Welp, here was my first mistake. Selenium is horrible, instead of providing an intuitive API that would interact with the browser in a systematic way, it seemed like the way they built selenium was from the top down. They looked at a browser and said how do humans interact with it? Ok that's how we'll create this API. It doesn't take a genious to realize that humans are inefficient and this was a horrible idea that would give me many headaches in the future. Exhibit A:
To take a simple Screenshot with Selenium, I had to:
- Install some headless driver called
geckodriver
, while making sure it was supported by that version of Selenium - I had to initialize this driver which basically was just running the headless browser from the command-line, and I had to tell Selenium which browser it was interacting with, and pass it a path to an executable:
if os.name == "nt":options = webdriver.FirefoxOptions()options.add_argument('--headless')options.add_argument('--no-sandbox')options.add_argument('--disable-dev-shm-usage')DRIVER = webdriver.Firefox(executable_path="./geckodriver.exe",options=options)elif os.name == "posix":options = webdriver.ChromeOptions()options.binary_location = os.getenv("GOOGLE_CHROME_BIN")options.add_argument('--headless')options.add_argument('--disable-dev-shm-usage')options.add_argument('--no-sandbox')DRIVER = webdriver.Chrome(executable_path=os.getenv("CHROMEDRIVER_PATH"), options=options)
Oh did I mention that because of the way that Selenium was doing this, other operating systems weren't supported depending on the browser you used! And yes, each browser had different methods that would not work for them and so I had to separate the way I did things on my local vs on my production environment substantially.
Ok... So finally I have my headless browser, now lets take a screenshot of a part of this web page:
self.driver.find_element_by_tag_name('body').send_keys(Keys.COMMAND + 't')self.driver.get(self.url)self.driver.set_window_size(733,481)self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div/div[2]/div[5]/div[1]/div/div[1]/div/table/tbody[2]').screenshot(f'./temp/{seed}.png')self.driver.find_element_by_tag_name('body').send_keys(Keys.COMMAND + 'w')
That's right! I had to in code send_keys
to the browser so I can open a new tab. Then finally navigate to the url, change the entire window viewport size because it wouldn't let me just screenshot by element and only grab the size of that element, then finally find the element throught the XPath and take a screenshot.
Ok, now what does that look like in PlayWright? Here it is:
- Launch the browser:
browserType.launch()
- Generate a new Context:
browser.newContext()
, isn't that nice, having a context manager :). - Create a new Page based on Context:
context.newPage()
, ohh that's wonderful, no having to deal with sending key presses. Just a simple function call. - Take Screenshot:
page.screeenshot()
WoW, that's all we needed!
PlayWright is so well built, it's intuitive and I think it'll be the future of web scrapping and user-simulated web testers. No more are the days of having to use Selenium to build your sites test suite, all you need now is PlayWright and Cypress.
Source Code for the bot.