<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: BK ☕</title>
    <description>The latest articles on DEV Community by BK ☕ (@bk).</description>
    <link>https://dev.to/bk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F97901%2F20ffaa48-3d96-4d6b-816c-5cfd1643bc36.jpg</url>
      <title>DEV Community: BK ☕</title>
      <link>https://dev.to/bk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bk"/>
    <language>en</language>
    <item>
      <title>Single App, Multi Login - Proof Of Concept</title>
      <dc:creator>BK ☕</dc:creator>
      <pubDate>Tue, 19 Jan 2021 15:37:30 +0000</pubDate>
      <link>https://dev.to/bk/single-app-multi-login-proof-of-concept-2k9m</link>
      <guid>https://dev.to/bk/single-app-multi-login-proof-of-concept-2k9m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post was originally posted on &lt;a href="https://bilalkhoukhi.com/blog/single-app-multi-login"&gt;https://bilalkhoukhi.com/blog/single-app-multi-login&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Original Post:
&lt;/h2&gt;

&lt;p&gt;Google, Instagram, Twitter, and many other services allow users to login to multiple accounts and to switch between them without having to logout from one and login to another. &lt;/p&gt;

&lt;p&gt;At BuildOn Technologies, I saw the need for a similar solution, so I have decided to create a Proof of Concept to assess how easy it can be done with Angular, and whether this is the right solution for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  How is it done?
&lt;/h2&gt;

&lt;p&gt;Google's approach is the oldest I have seen as I used it for many years. When you go to Gmail, you will notice the URL looks like this &lt;code&gt;https://mail.google.com/mail/u/N/...&lt;/code&gt; where N is the index of the account starting from 0. This allows the Frontend app to identify which account it should get data for from the server or &lt;code&gt;localStorage&lt;/code&gt; and gives you the ability view different accounts in different tabs, where refreshing does not affect which account/page you are viewing.&lt;/p&gt;

&lt;p&gt;Looking at Gmail's cookies, you will notice that the keys &lt;code&gt;GMAIL_AT&lt;/code&gt; and &lt;code&gt;COMPASS&lt;/code&gt; have their &lt;code&gt;Path&lt;/code&gt; values correspond to &lt;code&gt;/mail/u/N&lt;/code&gt; and I believe this is where each account's token is stored.&lt;/p&gt;

&lt;p&gt;Twitter's approach is a bit different, you cannot use different accounts in different tabs, once you switch to a different account in one tab, the other tab switches the account right away, I believe they use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel"&gt;BroadcastChannel Web API&lt;/a&gt; to detect the switch, and then refresh the whole page. As for Instagram, I could not test their multi-login mechanism, but I would think it behaves in a similar way to Twitter. &lt;/p&gt;

&lt;h2&gt;
  
  
  Objective
&lt;/h2&gt;

&lt;p&gt;My goal is to imitate Google's approach with some twists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login to multiple accounts&lt;/li&gt;
&lt;li&gt;The ability to use them independently in different tabs.&lt;/li&gt;
&lt;li&gt;Refreshing a tab should not log you out or switch you to another account.&lt;/li&gt;
&lt;li&gt;Logout from each account should not force you to log out from all, which is the case for Google's implementation.&lt;/li&gt;
&lt;li&gt;Avoid Google's routing style. So, no &lt;code&gt;/mail/u/N&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tracking logins and workarounds:
&lt;/h3&gt;

&lt;p&gt;As mentioned, Google forces you to logout of all accounts and that is due to the use of indexes they assign for each account, you cannot logout of account 3 (index 2) and still use account 4 (index 3). If we follow Google's approach, we would be giving up on the ability to logout from accounts independently. Unless we use random digits instead of in-order indexes.&lt;/p&gt;

&lt;p&gt;We could also use query parameters instead to detect which account we are currently using, but we would still face the same logout issue, unless we use random digits instead of indexes. &lt;code&gt;?account=536&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;My preferred solution is to use a mix of &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt; to keep track of the user in each tab. &lt;code&gt;localStorage&lt;/code&gt; keeps track of all the accounts that are logged in, while &lt;code&gt;sessionStorage&lt;/code&gt; can store which of these accounts we are using in the current session. This should allow using different account in different tabs, refreshing does not remove your session, but closing the tab does.&lt;/p&gt;

&lt;p&gt;So, what happens if we visit a link in a fresh tab IF we are already logged into multiple accounts? Well, we can show the user a list of accounts, they can choose which to use. And this is a pro and a con:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The con is that the user's navigation must interrupt, but luckily it only happens if the user is logged into multiple accounts.&lt;/li&gt;
&lt;li&gt;The pro is fixing the issue in Google's implementation where sharing links with others is wacky... let me explain: Jane and I work at a company that uses Google Suite, she uses the work account as her default (index 0), and I use my personal Gmail account as my default, and the work account as index 1. Jane shares a link with index 0, Google will not let me see the content, and I must switch manually.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;This POC is done using Angular. To view the completed solution head over to &lt;a href="https://github.com/Bilal-io/Multi-Login-POC"&gt;https://github.com/Bilal-io/Multi-Login-POC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's walk through some files to explain what is going on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anything under &lt;code&gt;interceptors&lt;/code&gt; is borrowed from &lt;a href="https://github.com/cornflourblue"&gt;https://github.com/cornflourblue&lt;/a&gt; and it is acting as a fake backend. We call &lt;code&gt;./users/authenticate&lt;/code&gt; route to login and we get a response.&lt;/li&gt;
&lt;li&gt;The auth service &lt;code&gt;services/auth.service.ts&lt;/code&gt; has helper functions to login, logout, switch accounts, and deal with &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt;, as well as store our login state. Too much in one file, it could be refactored.&lt;/li&gt;
&lt;li&gt;There are two lazy loaded modules, dashboard and admin.&lt;/li&gt;
&lt;li&gt;If we look at &lt;code&gt;guards/authentication.guard.ts&lt;/code&gt; and &lt;code&gt;guards/admin.guard.ts&lt;/code&gt; we find two auth guards for the dashboard and admin modules. Each has a &lt;code&gt;canLoad&lt;/code&gt; interface that prevent downloading its respective module if we are not authenticated, or authenticated but not an admin. There is also a  &lt;code&gt;canActivate&lt;/code&gt; interface that prevents the route from being accessed if not authenticated. (in can the module was loaded, then the user logs out)&lt;/li&gt;
&lt;li&gt;The Header component lists the users in a dropdown menu, allows us to logout of the active user, or switch to another.&lt;/li&gt;
&lt;li&gt;The Login page checks the app state to see if we have any accounts, and displays them, giving us the option to choose which to use. Or we can switch to the form and enter the credentials of a different account. In either case, we get redirected to the &lt;code&gt;/dashboard&lt;/code&gt; unless the tab was initially to a specific URL i.e. &lt;code&gt;./dashboard/test&lt;/code&gt; then we store that in a query param &lt;code&gt;?redirect=&lt;/code&gt; and redirect the user to it after a successful login or account switch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Remark
&lt;/h2&gt;

&lt;p&gt;If you've worked on a similar implementation, I'd like to hear your feedback.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>poc</category>
    </item>
    <item>
      <title>Automate Taking Website Screenshots With Selenium in Python</title>
      <dc:creator>BK ☕</dc:creator>
      <pubDate>Tue, 25 Dec 2018 04:44:01 +0000</pubDate>
      <link>https://dev.to/bk/website-screenshots-with-selenium-in-python-kcn</link>
      <guid>https://dev.to/bk/website-screenshots-with-selenium-in-python-kcn</guid>
      <description>&lt;h2&gt;
  
  
  Update 09/11/2020
&lt;/h2&gt;

&lt;p&gt;Updated Title for more clarity&lt;br&gt;
Published on &lt;a href="https://bilalkhoukhi.com/blog/automate-taking-website-screenshots-with-selenium-in-python"&gt;https://bilalkhoukhi.com/blog/automate-taking-website-screenshots-with-selenium-in-python&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Update 01/01/2019
&lt;/h2&gt;

&lt;p&gt;Here is a Node.js app doing the same work, it is using a React front-end. &lt;a href="https://github.com/Bilal-io/Web_Screen_Shooter-React-Selenium"&gt;Node.js App with Selenium and React&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Update 12/26/2018
&lt;/h2&gt;

&lt;p&gt;If you wish to run the job using Headless Chrome, either on Windows, Mac, Linux (Desktop or through SSH), I have added a new section at the end of the post.&lt;/p&gt;
&lt;h2&gt;
  
  
  Original Post
&lt;/h2&gt;

&lt;p&gt;A not too long story short I dread doing repeated tasks manually and that's why I'd rather spend minutes (hours when doing it for the first time) to write a script that automates the process for me. Otherwise I cannot focus, and if you're also horrible at focusing just like I am, I recommend reading &lt;a href="http://calnewport.com/books/deep-work/"&gt;Deep Work by Cal Newport&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Problem:
&lt;/h3&gt;

&lt;p&gt;I have a Photoshop mock-up of a laptop, stablet and a phone that I need to use to showcase a website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6kOIlAg---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/arxj243bn90hu4z27zbs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6kOIlAg---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/arxj243bn90hu4z27zbs.jpg" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's an easy and quick job, 3 screenshots and 2 minutes later, you're done. But what if you have 10, 20, or 100 websites, and you have to supply screenshots of different screen sizes (a desktop, a mobile and a tablet)?&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution:
&lt;/h3&gt;

&lt;p&gt;Selenium is the solution. And since I've only used it with Python, I choose to do this tutorial with Python, it is also faster for me to setup compared to a Node.js project, but the functionality should be similar.&lt;/p&gt;

&lt;p&gt;Let's start:&lt;/p&gt;

&lt;p&gt;If you don't have Python installed in your system already, get it from here: &lt;a href="https://www.python.org/downloads/"&gt;Python 3.6+&lt;/a&gt;&lt;br&gt;
You will also need &lt;a href="http://chromedriver.chromium.org/downloads"&gt;Chrome Webdriver&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have Python setup, install Selenium by running the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;selenium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's easy to get started with python, all you need is a single file that imports the modules you will be using, &lt;code&gt;selenium&lt;/code&gt; and perhaps &lt;code&gt;time&lt;/code&gt; which will allow us to delay/wait a few seconds after the page is loaded before we take the screenshot.&lt;/p&gt;

&lt;p&gt;We will also make use of the &lt;code&gt;time&lt;/code&gt; module, which is part of the &lt;a href="https://docs.python.org/3/library/time.html"&gt;Python standard library.&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Next, Create a file with the extension &lt;code&gt;.py&lt;/code&gt; and open it with your favorite editor.&lt;/p&gt;

&lt;p&gt;First we'll start by importing webdriver from selenium and time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's define a list of links we want to take screenshots of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'dev.to'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'twitter.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'github.com'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's import to note that the webdriver does not consider the links above as valid since they do not have the &lt;code&gt;https://&lt;/code&gt; protocol. We will be adding it later, and the reason I added them without the protocol is so that I can use the above as file names.&lt;/p&gt;

&lt;p&gt;We can start using the webdriver by calling &lt;code&gt;webdrive.chrome(/path/to/chromedriver)&lt;/code&gt; as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:/chromedriver_win32/chromedriver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# code goes here using driver
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above reads as &lt;code&gt;with this function defined as driver.. do things with driver&lt;/code&gt;&lt;br&gt;
Which is somewhat equivalent to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:/chromedriver_win32/chromedriver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# code goes here using driver
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As far as I can tell, the difference is that the first method will automatically close the browser once it is done doing its job, unlike the second one, which you would have to explicitly call &lt;code&gt;driver.close()&lt;/code&gt; to close the browser. If anyone knows other reasons, please let me know in the comments.&lt;/p&gt;

&lt;p&gt;For this tutorial I will be using the first method.&lt;br&gt;
For those new to Python, it is important to note that the following lines will be indented, as they're contained in the statement above, similar to how you would use curly braces to contain a function/class body in C++, JavaScript and other languages.&lt;/p&gt;

&lt;p&gt;For the purpose of taking a screenshot, we will be using 3 commands: &lt;code&gt;set_window_size()&lt;/code&gt;, &lt;code&gt;get()&lt;/code&gt; and &lt;code&gt;save_screenshot()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_window_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# takes two arguments, width and height of the browser and it has to be called before using get()
&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# takes one argument, which is the url of the website you want to open
&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save_screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# this one takes one argument which is the path and filename all concatenated. Important: the filename should end with .png 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To learn more about these commands head to the &lt;a href="https://selenium-python.readthedocs.io/api.html"&gt;Webdriver API documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's put it all together, we will need to loop through the list of links we created above, and define some variables to use for filename, width and height, and the link with the &lt;code&gt;https://&lt;/code&gt; protocol&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:/chromedriver_win32/chromedriver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;desktop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'-desktop.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="s"&gt;'width'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="s"&gt;'height'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;tablet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'-tablet.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s"&gt;'width'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s"&gt;'height'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;mobile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'-mobile.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s"&gt;'width'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s"&gt;'height'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;linkWithProtocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'https://'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have defined 3 dictionaries that contain the output filename, width and height for every screen size we need, you can change the dimensions based on your needs.&lt;/p&gt;

&lt;p&gt;Now we can start using the commands explained above&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="c1"&gt;# set the window size for desktop
&lt;/span&gt;        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_window_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desktop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'width'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;desktop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'height'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linkWithProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save_screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desktop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Noticed that we have used &lt;code&gt;time.sleep(1)&lt;/code&gt;, what that means is that we asked python to delay the execution of the next line until 2 seconds pass. Even without the &lt;code&gt;sleep&lt;/code&gt; command, the driver will wait for the page to fully load before it takes the screenshot, however, if you have any animation, you may want to wait for things to settle down, if that's not needed, then you can get rid of the line. &lt;/p&gt;

&lt;p&gt;and this is how the tablet and mobile will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="c1"&gt;# set the window size for tablet
&lt;/span&gt;        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_window_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tablet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'width'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tablet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'height'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linkWithProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save_screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tablet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="c1"&gt;# set the window size for mobile
&lt;/span&gt;        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_window_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mobile&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'width'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;mobile&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'height'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linkWithProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save_screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mobile&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, all you need to do is run the script from the command line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python script.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open Chrome browser, resize it, take the screenshot each time and output the file at the same directory you have script.py until it is done.&lt;/p&gt;

&lt;p&gt;The whole code put together can be found &lt;a href="https://github.com/Bilal-io/Selenium-Screenshot-Script"&gt;here on Github&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final note
&lt;/h3&gt;

&lt;p&gt;Selenium is capable of great things, it can manipulate pages with CSS and JavaScript, fill out forms and submit them, etc... Learn it and put it to good use. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Selenium with Headless Chrome
&lt;/h2&gt;

&lt;p&gt;It can be annoying to have the browser open and close so many times while you're trying to finish other work. The solution is to use Headless Chrome.&lt;/p&gt;

&lt;p&gt;Not much will change from the code above. &lt;br&gt;
We need to define our &lt;code&gt;ChromeOptions()&lt;/code&gt; and add an argument to it using &lt;code&gt;add_argument()&lt;/code&gt; command as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromeOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# define options
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"headless"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# pass headless argument to the options
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will pass a new argument to the command we defined before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:/chromedriver_win32/chromedriver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# code here
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:/chromedriver_win32/chromedriver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# code here
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is all you will need for Mac and Windows which I have tested. Linux desktop have not been tested, but I assume it will work as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux Server through SSH
&lt;/h3&gt;

&lt;p&gt;Things work differently between Linux Desktop and Server, the latter has no screen, and I am sure it is stripped from so many drivers it does not need, and for that reason, we will have to pass a few more arguments to  make it work.&lt;br&gt;
As I have not tested this on Linux Desktop, if the solution above does not work, then treat it like we will treat Linux Server&lt;/p&gt;

&lt;p&gt;First install Chrome Browser (I never thought I'd need it here)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; chromium-browser &lt;span class="c"&gt;# BTW I use Ubuntu (says a non-Arch user)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we add these arguments&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromeOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# define options
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"headless"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# pass headless argument to the options
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binary_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'/usr/bin/chromium-browser'&lt;/span&gt; &lt;span class="c1"&gt;# location of the Chrome Browser binary
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"disable-infobars"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# disabling infobars
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--disable-extensions"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# disabling extensions
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--disable-gpu"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# applicable to windows os only
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--disable-dev-shm-usage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# overcome limited resource problems
&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--no-sandbox"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Bypass OS security model
# thanks to https://stackoverflow.com/a/50642913/2291648, this answer helped debug the issue
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like we did above, pass the &lt;code&gt;chrome_options=options&lt;/code&gt; to the &lt;code&gt;webdriver.Chrome(...,webdriver.Chrome)&lt;/code&gt; as a second argument.&lt;/p&gt;

&lt;p&gt;This worked for me, and I hope it does for you as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/Bilal-io/Selenium-Screenshot-Script"&gt;Github link (updated repo)&lt;/a&gt;
&lt;/h3&gt;

</description>
      <category>selenium</category>
      <category>python</category>
      <category>automate</category>
      <category>screenshots</category>
    </item>
  </channel>
</rss>
