Placeholder Image

字幕列表 影片播放

  • Selenium is a portable framework for testing  web applications. Jim from JimShapedCoding  

  • teaches this Selenium course. He has created many  popular courses both on the freeCodeCamp channel  

  • and his own channel. Hi, everyone, and  welcome to the Python Selenium series,  

  • where we will learn how to create user interface  automation for websites. So Selenium is widely  

  • used. And it is very popular for a great reasonBecause it can help you to create tests for your  

  • web applications. And it can also help you  to create online bots, which is very nice.  

  • So in this series, we are going to learn how to  use Selenium from the basics. And later on, we  

  • will learn how to create an online bot that will  report the cheapest deals from a booking website.  

  • And this series is really going to include some  great episodes, so be sure to hit the subscribe  

  • button and as well as click the bell notificationSo you will never miss an episode from this entire  

  • series. So grab a cup of coffee, and let's get  started. Great. So before we really get started,  

  • we need to understand that there are going to  be some prerequisites. So in this series, I'm  

  • going to assume that you have Python installedAnd as well as you got an id like Python that  

  • is configured properly with Python. Now you can  also use a random text editor like Sublime Text,  

  • but just make sure that it is connected  properly to your system interpreter.  

  • And if you don't have any of those, then you can  visit my five our beginners course and catch up  

  • with the installations. Alright, so like a lot  of libraries in the Python programming language,  

  • we need to somehow use the library of scillonianSo that's why we need to somehow install it on our  

  • computer. And we can do that with the PIP  command. Now, if you got Python installed,  

  • then you should also have the PIP package manager  installed together with it. So now I'm going to  

  • open our terminal here. And I'm going to say pip  install Selenium like that. Now I'm doing this in  

  • the system interpreter, because I'm not going to  use virtual environments throughout the series.  

  • So that's why I could allow myself to do that  from the terminal. Okay, so if you see no arrows,  

  • then it means that everything is great. And we can  go back to a pie chart and basically import this  

  • library and see how we can use it. Alrightso here, I'm going to import from Selenium  

  • import WebDriver. So I'm importing something very  specifically inside the Selenium library. And  

  • there's a great reason for doing that. Now, when  it comes to performing user interface automation  

  • in web browsers, then we need to somehow open up  that web browser. So that's why we need to import  

  • a dedicated library that is going to automate  us the action of opening up a web browser. Now  

  • when we talk about performing automations on a web  browser, we need to understand that for each web  

  • browser out there, there is going to be a version  that is going to be dedicated for performing those  

  • automated tasks. So for example, if we want to  perform those automation tasks on Chrome browser,  

  • then we need to instantiate the chrome driver  class to perform those automated actions in it.  

  • So that's why we need to import that libraryAlright, so now that we have imported this,  

  • then we have to instantiate the library that  is going to be responsible to pop up the  

  • Chrome browser to us. So that's why I'm going  to go down. And I'm going to say, for example,  

  • driver is equal to WebDriver dot Chrome, like  that. And as you can see here, there is actually  

  • more options rather than Chrome. So if I delete  that, and I press on control space, then you can  

  • see that we also got Firefox. And I believe that  there is edge as expected. So as you can see, you  

  • can perform those automated actions in different  browsers. But actually, throughout this series,  

  • I'm going to use Chrome browser. So that's whywill say Chrome, and we'll instantiate the class  

  • like that. Now, we could go ahead and try to  execute this. But we will receive some arrows and  

  • the arrows are going to talk about something that  is called chromedriver. That does not exist on  

  • my computer. And as well as it is not in the tatSo let's break down this error into two important  

  • steps that we need to do. So first of them, we  need to download the chrome driver. And actually  

  • chrome driver is a separated executable file that  Selenium WebDriver uses to perform those automated  

  • actions in a Chrome browser. So that means that we  need to download this chrome driver dot e xe file  

  • to our computer. So that's why I'm going to bring  here this website. And I will put the link of that  

  • in the description for sure. And as you can seehere is the page that we can download the chrome  

  • driver, and if we scroll down, then you can see  that there are a lot of versions of Chrome driver  

  • that you can download. Now, you might ask yourself  how I'm going to decide which version should be  

  • installed on my computer? Well, this depends  on what Chrome browser version you have right  

  • now installed on your computer. So if you  use the Chrome browser by default, like me,  

  • then what we can do is basically go to a separate  tab, and we can right here, Chrome, colon,  

  • two forward slashes, and then say, versionAnd right after that, we will see an output  

  • like the following. And as you can see from  here, here is the google chrome version that I  

  • use. And as you can see, we have the major version  as 89. So for yourself, it could be 9091 92,  

  • or even 84. But you need to make sure that  this measure version is going to be matched  

  • to the version that you are going to download from  the chromedriver page. So in my case, I am going  

  • to go back now to that page. And I'm going to  search up for a version that is starting with 89.  

  • And that's why I'm going to click here. And right  after that, I'm going to search for the specific  

  • archive that I need. And for sure it is going to  be windows because that is the operating system  

  • of my machine. And I'm going to click here and  it should start downloading and right after we  

  • have this downloaded, then we need to extract it  because it is actually a zip archive. Alright, so  

  • as you probably know, in Windows, by default, the  download files are going to be downloaded in the  

  • download library. And you want to move this tolocation that you want to have your chromedriver.  

  • So for myself, it is going to be under thedrive. And inside the folder that I named salonu  

  • drivers, as you can see in the left side of my  screen. So I'm going to cut this from here and  

  • move this to the folder that I'd like to have itSo I'm going to do that. And right after that,  

  • let's go ahead and work with this follow nowSo as you can see, the location of that will be  

  • C Selenium drivers like that. And you can againput it in whatever location you would like to  

  • invite up to that I'm going to extract the  files in here. And as soon as I have done this,  

  • then you can see that we got the executable file  located in this directory. So it is a great start  

  • to solving our problems that we have when we try  to execute this fight on file. Perfect. So back to  

  • Python. Now, if we will try to execute this file  again, then we will end up with the same error.  

  • And this is because we did not perform the second  step that I talked about when we saw this error.  

  • And we need to now put the location of our  chromedriver in an environment variable that  

  • is called a path. Now the PATH environment  variable is going to be responsible for the  

  • files that your system should look up for when  you want to execute them immediately from the  

  • terminal. So that's why it should be inside the  PATH environment variable. Now we could have done  

  • this in the system level. But that is actually  a not great idea. If one day you would like to  

  • perform the Selenium project on another server. So  that's why configuring this environment variable  

  • in the code level and not in the system level is  considered as a best practice. So that's why I'm  

  • going to say up top here, something like import  OAS. And I'm going to now upload one more value to  

  • the already existing PATH environment variable. So  I'm going to say Oh, s dot n, v wrong like that.  

  • And then I'm going to look up for the key of  cat and make sure that you should do it all in  

  • capitalized like I did. And now we'd like to use  plus equals because we are adding a pet value to  

  • already existing pad, we could have multiple  values for this environment variable. And  

  • right here, we want to specify the location of  our chrome driver. So I can start typing here  

  • C, colon and Ford slash. So I'm pointing to thedrive of my computer. And I'm going to say here,  

  • selenium drivers like that, because this is  the folder name where I have the chromedriver.  

  • Alright, so right after we have done this, then we  should add here the our letter before the double  

  • quotes. And this is actually a convention forprefix that is going to mark it as a role string  

  • and it is going to be helpful when you specify  paths to different locations. So now if we were  

  • to try to execute this program, then we should see  now the chrome popped up and that is exactly what  

  • is happening. So it means that now we are ready to  work and start trying to perform some automations  

  • on a random website. Alright, so now that we  understood how to set up a Selenium environment,  

  • let's try to perform some automation on a random  website. Now in this episode, specifically, I will  

  • start by a testing Selenium website which will  allow us to to basically take basic actions to  

  • get started with it. So I'm going to use this  Selenium ez.com website. And I'm going to go to  

  • this URL in here, which I will put the link of  that in the description. And I'm going to try to  

  • click on that start download, performing it with  the Selenium only. And right after we click on  

  • that, then we should somehow identify that the  progress has been completed. So actually, those  

  • kinds of actions are good candidates for actions  that you want to perform with Selenium without  

  • really being involved in clicking manually on  those buttons. So let's go ahead and get started.  

  • So first of First, we should understand how we  are going to identify the element that we want  

  • to click on. And in order to do something like  that, we can go to the element that we want. And  

  • we can click on Inspect like the following. Now  actually, when it comes to pages, then each page  

  • is going to have its unique HTML architectureAnd each HTML element is going to be described  

  • by each type. And as well as with some additional  attributes. So let's see what I said in action. So  

  • I'm going to say inspect. And as you can see, we  have this line in blue background. And as I said,  

  • the kind of this element is button because we  see that it is right after the tags. So that's  

  • why it is called a button HTML element. And as you  can see, it has different attributes like class,  

  • Id like that. So what we can do from here is  to try to identify the element that has the ID  

  • of download button, like it says in here. So  let's try to do and see how we can identify  

  • this specific element, and try to click on thatAlright, so let's go back to pi charm. Now before  

  • I do that, let me copy the link of the websitebecause we are going to use it just in a minute.  

  • All right, so now if I open pi charm, and go downand here we are ready to write some additional  

  • code. Now before everything, we need to specify  selenium, what is the website that we are going to  

  • use in our automation, so it should be done with  the get method that the driver is going to have.  

  • So I will say driver dot get. And as you can seeit expects for a URL, so I can straight forward,  

  • put the link of the URL that we actually look to  perform some automatic actions. So as you can see,  

  • this is the way that the driver dot get should be  executed. Alright, so now that we have done this,  

  • then we should somehow tell the Selenium  that we like to click on an element  

  • that eats ID is download button. So I'm going  to say here, driver dot find underscore element,  

  • underscore by ID. And as you can see, we have  multiple methods of find element by so we have  

  • class name, CSS selector, and even more like nameAnd those are methods that we are going to see in  

  • the future throughout the series. But let's  start with the ID. And I'm going to say here,  

  • download button, like the following. Now since  this entire statement is going to return us an  

  • object that we can actually do some additional  actions with it, then I should assign this to a  

  • variable. So I can say my element, something like  that. And then down below, I can say my element  

  • that click like that. And we should see Selenium  driver trying to click on that element. So let's  

  • test this out. So I'm going to execute that right  now. And I'm not going to touch anything. So  

  • let's see that in action. Alright, so it takes us  to here. And as you can see it immediately just  

  • click on that element, because we see  that progress box that says us now that  

  • the download has been completed. But there  is actually a big problem with doing this  

  • approach. Because sometimes, in most of the casesloading an entire website could take some time. So  

  • that's why we can go between those two lines, and  actually try to wait before we try to perform an  

  • action on an element. So that's why going between  those lines and saying something like driver  

  • that implicitly wait. And here, we should specify  the amount of seconds that we'd like to wait to  

  • this website, it's all being loaded successfullySo we can say that we'd like to wait for example,  

  • three seconds, so it's considered as a better  thing to do, because now we are totally safe from  

  • our browser being a little bit slow sometimesOr maybe we can have some cases that our  

  • server could be very slow. So that's why waitinglittle bit here and there could be a better idea.  

  • So now if we run our program one more timethen let's see the results. So at first again,  

  • the page is being loaded. Then as you can see it  immediately try to download this file. Now again,  

  • this is just a simulation, this really doesn't  download anything to your computer. Okay,  

  • so let's identify what happened in here. So first  off first, we can understand that it really did  

  • not wait three seconds. So what that means it  means that the implicitly wait does something  

  • special to our driver object, because we did  not really wait three seconds, right. So let's  

  • try to test this with 30 seconds now. So I'm going  to execute this one more time. And as you can see,  

  • just in a second, it sounds to download it  once again. So this really does not wait in  

  • the amount of time that is specified in this  line. So what is going on here and why we  

  • saw the actions being taken immediately. Wellwe could have use here something like time dot  

  • sleep, and specify the amount of seconds to  wait. But this is something that we don't like  

  • to do here. Because sometimes we don't like  to wait for the complete duration of time,  

  • in case the element is going to be found before  the duration specified, it needs to move on to the  

  • next line of code execution. And so using herethe implicitly wait is actually very, very useful.  

  • Because again, we don't feel the need to wait 30  seconds if the element is already there in that  

  • web page. Now, to be fair, there are more things  that I'd like to talk about this implicitly Wait,  

  • because it has some more functionalities  that I will talk in the future. And if you  

  • have any questions about the behavior of this  tricky method, then let me know this in the  

  • comment section down below. Alright, so before  we go ahead and compare those kinds of weights,  

  • then let's clear some important points about  the implicitly wait. Now till that point,  

  • we know that this is much more efficientrather than typing in the time dot sleep,  

  • because not always we'd like to wait 30 secondsbefore we find an element by ID, for example.  

  • But there is one piece of information that we  should know about implicitly Wait, it is going to  

  • be set up as a timeout across all your scillonian  project. So what that means it means that you  

  • only need to call this implicitly await method  one time, and then it will go ahead and set an  

  • implicit wait amount for all the elements that  you're going to try to find it in the future. So  

  • for example, I might go down here and say  something like my second element, and I can make  

  • that to be equal to driver dot find element by IDAnd I'm going to put here some element that is not  

  • even existing in the page in the left side. Socan say something like, let's just write something  

  • randomly. And then I can basically try to execute  our program. And just for testing reasons, let me  

  • decrease this to eight seconds. So we won't really  wait 30 seconds before we see this program being  

  • crashed. Okay, so if we run this one more timethen let's see the results. So as you can see,  

  • we have no problem with the first element. But in  about a second or two, we should receive an error  

  • that the Selenium was not able to identify  an element with this ID in here. So that's  

  • actually an important point to remember. Because  once we said the implicitly Wait, it is going to  

  • be basically applied across all the elements that  we are going to try to find them somehow in the  

  • future. All right, so now that we completely  understood the behavior of implicitly Wait,  

  • let's go forward with our automation. So now as  a next step, it would have been nice to identify  

  • if the download has been completed successfullyAnd we understood that that to indicate something  

  • like this, we need to understand when this  text in here is going to be exactly equal to  

  • complete with an exclamation mark. So let's try  to write an automation like that. So I'm going to  

  • select everything in here and try to click on  Inspect. And let me do this one more time because  

  • it did not really point to it. Okay, so we can  see that this complete with an exclamation mark  

  • is coming from this HTML element, which says us  that its type is Dave, and it has the class of  

  • progress label. Now till this point, we did  not really identify a specific HTML element  

  • by its class name. And there is actuallygreat way that we can do that by again using  

  • a method with the prefix of find element. So  let's see how we can do that. So let me delete  

  • this line actually, and I will stick with  that one. So I can say something like progress  

  • element is equal to driver dot find elementand then I can select a method that is called  

  • by class name. So we cannot identify an element  by its class name. So it is a great idea to  

  • select that. And I only need to now  pass in the class value. So it will be  

  • progress dash label as we saw earlier. Now, for  those that are confusing What are classes in the  

  • web world, it is basically a reference to a filing  method, unlike with Python, which is referring to  

  • classes where we use it for object oriented  programming. So I just wanted to make sure  

  • that we don't confuse between the meanings of  class in the web. And now that I have this,  

  • then I can go to a new line. And I can say heresomething like print, and I can use the formatted  

  • string. And I can try to basically print the text  of these elements. So it will be by both text,  

  • and it showed that display as the text in hereSo if we do that, and run this one more time,  

  • let's test the result of that. Okay, so we have  a new page being appealed. And as you can see,  

  • it immediately showed the text of this elementwhich was starting down now showing up the text  

  • immediately is actually not a great ideabecause we probably like to wait some time  

  • until we see the complete. But there is actually  not a nice way to do this with the approaches that  

  • we have learned till this point. So we need to use  another utility. And for that there is something  

  • that exists in selenium, which is called explicit  wait. Now with this, we can actually allow our  

  • program to wait for unexpected condition. And then  we can basically wait until this condition returns  

  • through. So what that means it means that we  look to this through expression in here. So I can  

  • say here, completed with the exclamation mark. And  in our case, if we don't wait in a smart way, then  

  • we will always receive false with that. So we need  to figure out how we can wait till this condition  

  • is through. And let's go ahead and see how we can  do that. Alright, so in order to use the explicit  

  • Wait, we need to import some several secondary  libraries that is inside the Selenium. So we  

  • are going to say up top here something like from  Selenium dot web driver, that common.by import by  

  • like that. And we are also going to import two  more things in here. So it will be from Selenium  

  • dot web driver.support.ui. Like that, we need  to import the built in class inside the Selenium  

  • that is called a web driver wait. And we'd also  like to import one final thing, which is going to  

  • be Selenium dot WebDriver dot support. Import  expected conditions as EC, both capitalized  

  • like that. Now I know that it was a lot of info  that we have used as input. But there is actually  

  • a great reason for that. And we will see why  just in a minute. So now I will go down here  

  • and I will start writing the condition that we'd  like to wait for. Now in order to start with  

  • that then First I will instantiate the WebDriver  weight class. So I will say web driver weight and  

  • I will instantiate that without assigning it to  a variable. And at first we need to pass in the  

  • driver object. So we will just say here, driverAnd now we like to specify the amount of seconds  

  • that we should wait until the expected condition  is true or not. So I'm going to say here something  

  • like 30 seconds again, and the actual be enoughAnd then I'm going to automatically launch the  

  • method of until in here. Now by convention, the  info that is written inside this until method  

  • is usually in a separate line. So I'm going to  press enter in here. And then we need to write  

  • the condition that we want it to be through  at some time. And in our case it is after  

  • 30 seconds approximately. So we can say here e c,  which stands for expected condition that we have  

  • used as import in here. And we can now usemethod that says text to be present in element.  

  • So as we can understand this method is designed to  basically to wait until the text has the expected  

  • text. And I'm going to launch up this method in  here and again, press enter. So again, I'm just  

  • writing the arguments just in a separate line. Now  this method expects for two important arguments  

  • as the first one being the element that we'd  like to check the condition on. And the second  

  • one being the text that we expect to have after  30 seconds. So I will just comment here, those  

  • downs So I will say here, element filtration. And  as the second one, we will say the expected text  

  • like that. Alright, so the way that we are going  to identify the element that we'd like to check  

  • is by using the by class. Now, this is just  another approach finding elements in a web page.  

  • So it is not going to look complex. So it is as  easy as saying here something like so I will just  

  • create a tupple in here, and then I will start  my filtration. So we'd like to find this again,  

  • by class name. And as you can see, we have  auto completion for that. So I can just use  

  • class name like that. And the second piece of  information right near of it should be the class  

  • name value, so it will be progress dash label  exactly like before. So this entire expression  

  • is just another way to find an element in a web  page, unlike using the find element by class name,  

  • meddled, and I just realized that I did not delete  those two lines that we don't need. So let's go  

  • ahead and do that. And then in the second lineright after we say, comma, we are going to write  

  • the expected text. So it is going to be complete  with an exclamation mark. So now, if we run our  

  • program, then we should not receive any errorsbecause this condition is probably going to return  

  • us through in less than 30 seconds. So if we run  our automation now, and wait for the results, so  

  • again, we click on this download button. And let's  see, just in a second, what will happen. Alright,  

  • so we got the text of complete. And if we go back  to our program, then you can see that the program  

  • finished successfully. So what that means it means  that we were able to write a nice automation,  

  • until we have waited, that's really verifies  that the download of some file has been completed  

  • successfully. So it is very nice to play around  between those wait methods, we can use implicitly  

  • wait to find elements in our entire page. And  we can also use the explicit Wait, which is the  

  • more custom waiting, so it means that we need to  use it if we want the execution to wait for some  

  • time until some condition is achieved. Now for  everyone that are interested to know what are the  

  • expected conditions that you can use, you can do  that by basically seeing all the options in here.  

  • So if we delete everything from here, and use  that again, and you can see that we have a lot of  

  • options that we can use expected condition for. So  you can see that we have element to be clickable  

  • elements to be selected and basically wait for  new window is opened. So as you can see, there  

  • are tons of options that you can use if you'd like  to wait until some expected condition is achieved.  

  • So it is very nice feature by Selenium that we can  always use to write more dynamic bots and as well  

  • as writing more efficient test cases. Alright, so  the reason you want to learn about ascending keys  

  • or clicking on different buttons right after it is  for performing actions like login and registering  

  • accounts. And this is something very useful  when you want to overcome authorization in some  

  • websites to basically performing some UI testing  or creating a bot for whatever reason. So to  

  • practice this, I am going to pull up this website  again from Selenium easy.com. So that's actually  

  • the page that we are going to try to send the keys  to. So as you can see, in here, we have some HTML  

  • forms. And actually, when we want to send the  keys, usually we'd like to do it in HTML forms  

  • where we actually need to write in some data, and  then we need to click on some button to submit our  

  • data. So as you can see, here, we have some form  in here which asks for two values. And then if we  

  • were to click on Get total, then you can see that  it shows us 30 and if I change this to actually  

  • some other number, there you can see that it is  being updated. So that is what we are going to  

  • try to automate now with Selenium. So first, let  me grab the website of this section in here and go  

  • back to Python and actually say here rival dot get  and the link will be available in the description  

  • for sure. So you can directly copy and paste from  there, alright. So now I will paste this in. And  

  • right after we have done this, then we should  somehow again try to identify the element in  

  • some method by ID class name or whatever method it  will be. So I will go back to our website in here  

  • and as usual I will say inspect, and you can see  that we have here something that is called class  

  • which we can again, find the element by class. And  as well as ID. Now, I always like to filter by ID  

  • because I think that this is the strongest field  that is always unique. So I'm going to use this ID  

  • some one. And I believe that the second box in  here should be some tools. So if I check that  

  • with inspect, then you can see that it is just as  expected. So we need to pull those elements and  

  • basically try to send it some keys to it. So I'm  going to go back to Python. And I will say here,  

  • some one is equal to driver dot find element by  ID. And I can use someone in here. And I can do  

  • the exact same thing by basically copying this  in here, and paste this in and change the values  

  • to number two, like that. And then I can try  to send some keys. So it will be just as easy  

  • as pulling the element. Because now we have full  access to it, and basically launched the method of  

  • send keys like that. Now, you can put herewhatever you would like to, as you can see,  

  • it expects for a value. So this could be any  value that you would like to pass in here,  

  • it could be a string, or it could be directlynumber like that. So let's go with numbers and say  

  • 15, like we have done manually. And I will do the  same thing with some tools. So I will send the 15  

  • as well. And now we can actually test this  out and see if it works. But not before we  

  • go ahead and say here's something like rival  that implicitly wait. And it is enough to wait  

  • five seconds in here for each element. And now  we can test this out. So if I run our program,  

  • then let's see the results. Okay, so the  page has been loaded. And you can see that  

  • we received this message. And we will handle it  in a minute. But you can see in the background,  

  • that we got the values. So I know  that it is a bit transparent in here,  

  • but you can actually see this in the backgroundOkay, so I think we got a new challenge in here  

  • as we see this output. And we need to somehow  perform an operation to click on no things  

  • to continue our automation. So this is actuallygood candidate to handle things along the way. So  

  • now I can go to inspect in here and see what needs  to be done to basically identify this button in  

  • here and click on that before we go ahead with  our automation. Okay, so you can see that we  

  • got this element with multiple classes. And iftake a look in here, then you can see that we have  

  • some classes in here that are separated by spacesNow, if you see classes separated by whitespace,  

  • as I said, then it means that it is referring to  a different class. So I can actually try to filter  

  • this element by class name at dash c m dash, no  dash button, as it will probably the most unique  

  • class name that I can try to filter this  element by. So I can go here and actually  

  • use let me copy this down and manipulate this in  Python. So before we go with everything in here,  

  • let me please go down here and paste this text  in here. And as you can see, this is the class  

  • name that we'd like to filter by. So I will delete  everything. And I will cut this string from here.  

  • And I will say no button is equal to driver dot  find element by class name. And I will paste back  

  • the class name that we'd like to filter by. And  now that I have done this, then I can basically  

  • apply the click method. So it will be no button  dot click like that. Now one final thing that it  

  • is a great idea to do here is to wrap this with  try except block because not always we are going  

  • to find an element by this class name because  maybe in the next time this popup won't appear. So  

  • it is a great idea to use here, try and basically  locate those two lines under a try block. And then  

  • if the Selenium is not going to find any element  with that class name, then it will not crash  

  • our program. Because in the except block, we can  only say something like print no element with this  

  • class name. So I can say just skipping like thatAll right, and down below, I can continue with our  

  • automation exactly like that. Okay, so if we run  our program here, then let's see what will happen  

  • this time. So the page has been opened. And as you  can see, now we don't see the popup, and we don't  

  • see any message from the except walk. So what  that means it means that the Selenium identified  

  • the element with this class name and it emits  Click on that button that we wanted to click on  

  • from the first stage. And then it went ahead and  basically executed the rest of the lines of code.  

  • And you can see that the values are right thereSo we are pretty close to complete the automation  

  • that we wanted to complete. Now there is one  final thing that I like to show you before we go  

  • ahead with the get total element. And I'm talking  about the fact that we can send the keys directly,  

  • not only specifying the text that we'd like to  send. So what that means it means that we can send  

  • the keys like Shift Alt, enter control and stuff  like that. And the way that it is going to work  

  • is by importing the keys class from the SeleniumAnd that is something useful here and there that  

  • you will want to basically automate some actions  that are required to maybe copy some text. So you  

  • need to automate Ctrl C. And again, that's useful  because sometimes you'll want to automate pressing  

  • on some key directly rather than sending some  random text. So I'm going to import here from  

  • Selenium dot WebDriver, dot common dot keysimport keys like that. And inside this class,  

  • you have all the options that are basically the  keyboard keys that are existing in each keyword.  

  • So to show you that, then I'm going to  now change this to something like keys,  

  • dot. And as you can see, in the drop down, we  have all the options. So we even have F 1234.  

  • And we even have the nums that are existing in  the right side of our cable, which is in numpad.  

  • So if we want, we can send the keys from the  numpad, I know that it is not quite useful for  

  • that case. But again, sending keys directly  could be very, very useful in some cases when you  

  • need them. So as you can see, you have  all the options in here. And if one day,  

  • you want to take a look to all the options, then  you can always go here and basically use Ctrl  

  • B in Python or F 12 in visual code, and you can  inspect to that class. And you can see that all  

  • the options are here available. And this is veryvery useful. Now to prove you that it will work,  

  • then let me try this with the numpad. So in the  someone, I will send numpad. One, and I will also  

  • send pay attention that I can separate the values  by comma. And I can say here keys that numpad  

  • five, and then it will basically send the 15.  So it is going to be quite equivalent to what  

  • we have done. And to show you that it works, then  I can basically launch our program again. And you  

  • can see down below that the results are quite the  same. So that is just another approach of sending  

  • keys. And the beauty behind the fate eight is  the fact that you can send all the keyboard keys  

  • that exists out there. Alright, so now we only  have the gate total a button to filter. Now,  

  • not always we'd like to filter the HTML elements  by their ID or class name, we might have in some  

  • more attribute key value pairs that we can try to  filter the elements by. So now I'm going to go to  

  • inspect in here. And we do this again. And as you  can see, we have here an attribute that we did not  

  • see before, which is on click. Now say that I'd  like to filter this element by onClick equals to  

  • that value in here. And this is something that we  did not learn how to do till that point. And this  

  • is possible with something that is called a CSS  selector. And the CSS selector is a pattern to  

  • filter an element by its styling. Now, unlike with  the methods of find element by ID or class name.  

  • with CSS selectors, we don't always need to filter  an element that matches an exact string. So for  

  • example, with CSS selectors, it is an option to  identify an element by only searching a substring  

  • that it contains. Now that is extremely useful  because not always we'd like to filter element  

  • by exact key value match, because we could want  to perform something with elements that are having  

  • the same prefix or suffix. So let's see how CSS  selectors works in action. Alright, so we are back  

  • at pi charm. And let's go down and see how we can  use the CSS selector. So I'm going to say here,  

  • button is equal to driver dot find element by CSS  selector like that. And in here, we need to pass  

  • our CSS selector expression. And the way  that we're going to do that is by using some  

  • special pattern that exists out thereNow to select an element by CSS selector.  

  • You have multiple options but we are going to use  the pattern of a HTML element type, followed by  

  • a matching key value. Now as an example, we can  actually go back to our button and see how we are  

  • going to filter that. And you can see that it's  a type of button. And we can see that from here,  

  • and it has the key value of on click return  total. So that means that we can try to  

  • filter all the buttons out there that are having  the attribute of onClick with the value of return  

  • total like that. So to achieve this, we will go  back here and we will say button, and then we will  

  • need to open a square brackets. And we will need  to say here something like on click equals and  

  • then we will use double quotes in here. Now the  reason I use double quotes is because I use single  

  • quotes here from the beginning. And we do not want  to miss match those. And now in here, I can say  

  • return total like that. And as you can see, this  is a one way that a CSS selector could work,  

  • you can basically specify the HTML element  followed by the key value match, and make sure  

  • that it is inside the square brackets right after  it. Now I'm going to show later on all the CSS  

  • cert or options, because there are tons of options  that you can filter in element by CSS selector.  

  • Okay, so I expect this button to basically have  the HTML element of this button. And then I can  

  • basically use button dot click as expectedSo now we can test if this works. So if we  

  • run our automation, once again, let's see  what will happen. Alright, so down below,  

  • you can see that we received the text of totalplus B is 30. And that is exactly the result that  

  • we expected for. So what that means it means that  we were able to select an element by CSS selectors  

  • successfully. And that is perfect. Alright, sosaid earlier that I will show you the page where I  

  • took this CSS selector expression pattern. And you  can see that now, I mean, a page that has multiple  

  • examples of how you can filter elements by using  CSS selector. So you can see that we have a table  

  • with all the selector options. And as well as some  additional examples for all the patterns. So you  

  • can see that if we scroll down, then I actually  used those patterns. So this is a pattern that is  

  • containing the element type. And in this examplethe type is a and that stands for anchor. And you  

  • can see that within the square brackets in hereit looks for a matching key value. But in that  

  • case, as I said earlier, you can not only search  for an element that includes an exact string, you  

  • can also search for an element that contains some  string. So you can see that in the description it  

  • says selects every eight tag elements whose href  attribute value begins with HTTP s. And same goes  

  • when you want to select some elements that ends  with some text. So you can see that we have a  

  • lot of options in here. And that is very usefulAnd I can totally confirm that the CSS selector  

  • is the method that I use the most, because it  gives you much much control rather than the  

  • other elements. So it helps you really to find the  elements that you will look up for when you want  

  • to perform some automation on them. Alright, so  the first thing that we want to do is to dedicate  

  • a file for the project. Now before I started  recording this, then I already have created this  

  • board folder. So this is going to include the code  for our project. And you can go ahead and create  

  • the folder wherever you want, just make sure  that you connect this to Python. So I will click  

  • OK. And we will start from here. Now you can pay  attention that by default Python created for us  

  • the main.py file, so we can just basically  delete everything from here and continue  

  • working. So if the auto generated file is not  happen to you, then just go ahead and create a new  

  • file by yourself and name it main like that. Okayso now, I am going to basically start designing  

  • the structure of the project. And the way that  I'm going to structure this project is going to  

  • be as I described in my project structural video  on my channel. Now if you don't know what I mean,  

  • then I have a video that describes how a Python  project should be structured, whether it's a  

  • beginner project or an advanced project. So if  you want to take a look then you can click in the  

  • suggested link. So now I'm going to go ahead and  create a sub directory that is named Woking like  

  • that. And I'm doing this because I want to include  all the relevant code to the booking bot inside  

  • this directory. And then this main.py file is  going to go ahead and basically call the necessary  

  • Python files from this directory. And let me go  ahead and change this to run actually is just  

  • more comfortable to win. So I will go ahead and do  that. Alright, so inside this booking directory,  

  • we will start by creating three files. So let's  go ahead and create the first of them. So the  

  • first one is going to be named booking like thatAnd here, I'm going to have a class that is going  

  • to have some instance methods that we're going to  call them in order to make our board to perform  

  • some actions. And the second one is going to be  constants. So we are going to have a lot of values  

  • that we really want to change it through how the  execution of our project. So that's why separating  

  • those variables in another file that we can name  it constant is a great idea. So I'm going to  

  • go ahead and do that. And the other file that I'm  going to create for now is going to be called by  

  • the following way. So it will be underscore twice  in it underscore twice once again. And there is  

  • actually a great reason that I naming this file  the way it is, it is because this is a convention  

  • when you want to create a Python package. Now  the reason you want to create a Python package  

  • it is because we want to call the package from  this run.pi file. Now notice how the run.pi file  

  • is outside of the working directory. And that is  one more reason that I have created this double  

  • underscore, you need double underscore file, it  is just a convention to mark this directory as a  

  • Python package. Now I will use a few more seconds  to explain something very special about the double  

  • underscore init file as well. So say that we go to  run.py file, and we look to import something from  

  • the constants file in order to execute something  successfully. So we could say something like from  

  • booking dot constants, import a for example, now  I know that this variable doesn't really exist,  

  • so we can create it like that. And notice that now  I'm inside the constants. And if I go back to run,  

  • then we are totally fine. Now before I do thislet me go to the double underscore init file and  

  • say, I will print first. Now what I'm meaning  in here, it is the fact that no matter which  

  • sub module you're importing from the booking  directory, at first, the double underscore init  

  • file is always going to be executed. So to prove  that, then I'm going to execute the run.py file,  

  • which basically imports just a variable from  the constants file. So if I go ahead and run it,  

  • then you can see that we received this message. So  that is one more important behavior that we should  

  • be aware of when it comes to Python packagesAnd we are going to use this advantage in our  

  • project as well. But I just want to make sure that  you understand the behavior of Python packages.  

  • And now we are totally ready to go ahead and  start building our bot. Now kind of a disclaimer,  

  • before we start, I will not recommend running  this bot around wild through or running it every  

  • few seconds, because it might lead the server side  to automatically disable your public IP address.  

  • And it is always not nice because then you have  to contact to that website and explain that you  

  • did not mean to do any harm. So that's why Be  sure to give enough room when you test your  

  • bot on those larger websites like the one that  we're going to use which is booking.com. Alright,  

  • so I have deleted everything that I have showed  you previously as example. Now we are ready to  

  • go and write our class. Now I say that we are  going to make this project object oriented. So  

  • this file that I named booking.py file is going to  be responsible to describe the methods that later  

  • on we are going to call them and those methods are  going to take the actions that we want our bots to  

  • take. So let's get started. Now first, we want to  import the WebDriver from the Selenium library. So  

  • we can create a class that will inherit from some  utilities inside this Selenium library. So we will  

  • start by saying from Selenium import web driverand then we will go down to lines. And we will say  

  • class booking so this is actually a good name for  our class. And now this class is going to inherit  

  • from WebDriver dot Chrome. And the reason we want  to do this, it is because I want the sale object  

  • to have the option of both using the WebDriver  dot chrome methods and also the methods that I  

  • will design for the class booking itself. So now  I can go inside our class and excuse me for these  

  • backslash and design the double underscore init  method. Now this is the constructor of our class,  

  • so This method is going to be called immediately  once we instantiate an instance of this class. So  

  • we will say def double underscore in it like thatAnd we will receive one parameter that I can name  

  • that driver, Pat like that. And if Remember, this  is going to store information about the location  

  • of our drivers. Now, just to save us a little bit  of time, then I'm going to receive a default value  

  • for this parameter, which is going to be exactly  the value that I have used throughout the first  

  • three episodes of this synonym series. So it  will be all for rostering in I will open here,  

  • double quotes, and I will say C column backslashsay lynnium. Drivers like that, because this is  

  • the location that I have the chrome driver. Okayso now I can go down and say self dot rival pad is  

  • equal to rival path. And further than that, I will  say, super. And the reason I use a super in here  

  • it is because I want to instantiate the WebDriver  dot chrome class along the way. And the reason I  

  • want to do this, it is because the constructor is  going to complain about how the inherited class is  

  • not instantiated yet. So this is why I can't allow  myself to use the super method to do so. Now,  

  • if you never saw me using the super method, then  I actually have a video from a series that I have  

  • created a year ago that you can watch a video that  is dedicated for class inheritance. So I will put  

  • the link in the suggested video up top. Alrightso this super is going to receive the name of the  

  • current class as an argument, and as well as the  self object. And now I'm going to call the double  

  • underscore init method of the webdriver.com class  as well. So this line is going to be successfully  

  • instantiate an instance of the WebDriver  dot chrome class as well. And to do that,  

  • let's go down and design a random metal. So let's  say here, something like this land first page,  

  • and receive self as a parameter for  sure, because this is an instance method.  

  • And now if I was to say self dot get, then you  can see that I have full access to the methods  

  • of the WebDriver dot chrome class. And you can  see that I can also make use of find element  

  • by something. And as well as using the get methodSo those methods are familiar to us, because we  

  • saw them in the beginning of the series. So this  is perfect. Now I have a class that I can use the  

  • Selenium methods to play around with the action  that we want to take with our online bar. Alright,  

  • so I'm actually going to make use of this  method. And I'm going to point our board to  

  • the first location that we want to do so. And I'm  going to use here the get method. And as the URL,  

  • I'm going to pass in the URL of the website that  we are trying to make an online board on. So it  

  • will be HTTPS, that will double w booking.comNow actually, here is the exact reason that I have  

  • created the constants dot p y, what we can do  in that case, this value in here is probably a  

  • value that is not going to change. So commonlybecause this is the website that we are going  

  • to always use. So that's why moving this value to  a file that I already created, like constants.py  

  • is a perfect idea. So we can make use of this file  by storing some constants. And by convention in  

  • programming, when we have constants, then we  should use all obligates. So I'm going to say  

  • base underscore URL is equal to the exact URL in  here. And now I can go back to our booking.py.  

  • And I can use import booking dot constants like  that as const, just to make it shorter and easier  

  • to read. And I can go now and say const, dot  base URL. And now we are ready to continue from  

  • here. Now actually, before we test this out, if  you remember, before we instantiated the WebDriver  

  • chrome class in the first three episodes, then  we had to do something that is quite mandatory,  

  • because otherwise the Selenium is going to  complain that it does not have the driver Pat  

  • in the system level of PATH variable. So that's  why we used something that was looking like  

  • always thought envy wrong, if you remember. So  let's do this before we use this super metal,  

  • because again, otherwise the cylinder is going to  complain for that error. So I'm going to go up top  

  • here, and I'm going to say import always, and I'm  going to go down and I'm going to use here always  

  • dot envy Ron, and I'm going to access to the cat  variable. And I'm going to use plus equals. And  

  • then I will add the self dot driver path like  that. Now, if you don't remember what this does,  

  • definitely consider watching the first  episode maybe for a minute or two.  

  • But let me bring the code from the first episodeSo I am inside my repository on GitHub that I have  

  • published recently, when I launched the seriesAnd if we take a look, then you'll see that I  

  • used in the very beginning, this line that adds  this path to the PATH variable. So that's why I  

  • have basically use the same expression pretty  much inside our class. All right, so let's go  

  • back to five gentlemen continue. And now what  we can basically do is go to our run.py file,  

  • and import this class and create an instance  of it. So we can really use its methods like  

  • land the first page. So if we go to run.py  file, and zoom in a bit, and say, from booking,  

  • actually, it should be from booking dot bookingbecause we want to refer to the file name as well,  

  • we only want to import the booking class, whichnamed like that. And now I can say inst is equal  

  • to a booking instance. And I'm not going to pass  anything because I want to make use of the default  

  • value of this driver pad. And now I can really  try to use the first metal that we have launched.  

  • So it will be inst dot land first page. So this  line is going to be responsible to take our bot  

  • to the booking.com homepage. So now I can execute  the run.py file and test if everything works.  

  • So if I run that, then let's see what will happenOkay, so you can see that we are inside the page  

  • that we were interested to be at from the very  beginning. And now we can continue on basically  

  • designing our bot from here. Now there are  going to be tons of actions that we want to  

  • take to basically fill in the form of where we are  going, or the date that we'd like to check in Plus  

  • check out and as well as adding more people to  our vacation. So the combination of all of those  

  • are going to be divided into instance, medals. And  the fact that we do that is going to make our code  

  • a lot more readable and maintainable. So that's  why using here, object oriented is a perfect idea,  

  • because it gives you the option to extend your  application, if you want to do that later on.  

  • Alright, so there's going to be one more very  interesting thing that we want to design at this  

  • stage. Now you could pay attention that although  we only wanted to launch the Index page, then  

  • we still got the driver instance being opened. So  what that means it means that on our next testing,  

  • the Chrome browser is still going to be openedAnd this can lead us to have like 10 or even 20  

  • browsers open on our computer. Now sometimes when  we test something, then we immediately want to  

  • force our browser being closed once the  board has finished doing its job. So that's  

  • why sometimes we want to have control whether  if we like to quit the application of Chrome,  

  • or leave it open. So this is a good candidate  for why we like to use context managers,  

  • because using context managers could give uslot more control when we want to start something  

  • and when we want to tear down things up. So that's  why I'm going to make use of context managers  

  • throughout this project. So in order to implement  this, then let me show you what needs to be done  

  • in the class level. And if you did not hear about  context managers yet, then I'm going to give here  

  • a short explanation about it. But basicallyif you want to learn about it more deeply,  

  • then I have a video that is dedicated for context  managers in Python. So if you want, definitely  

  • consider taking a look in the suggested link. But  let's go and cover this up. So I'm going to go to  

  • booking.pi file. And I'm going to use two more  magic methods that will allow us to basically  

  • make use of context managers. Now the way that  context managers are being used on the instance  

  • level is by instantiating, an instance of a class  with the width, keyword and tools similar to that.  

  • Then I'm going to split the panes nowhorizontally, I believe, no, we should do this,  

  • excuse me. I'd like to do that verticallyAlright, so on the left pane, I'm going to have  

  • our booking.py file and on the right pane, I will  have the rumbaut py file. So on the right side,  

  • I'm going to now delete everything and I'm going  to say Wait, booking like that as board and then I  

  • will go here and I will Se bought that land first  page. Now the beauty of using something like that,  

  • it is the fact that once Python reaches the line  outside of the indentation, then it is going to  

  • execute some teardown actions. And the location  that we'd like to define those teardown actions  

  • are by convention used under the magic metal that  is called level underscore exit. So to implement  

  • that, then I'm going to go here, and I'm going to  say there, double underscore exit. And I'm just  

  • going to use this suggestion of Python, because  by default, it receives some more parameters.  

  • And I don't want to screw things up. So I'm going  to just press Enter on the suggestion of Python.  

  • So now I can say self dot quit. So quit is going  to be the method that is going to be responsible  

  • to shut down the Chrome browser, once we have  finished everything. Now to prove that, then  

  • I'm going to run this. And I'm going to say inside  the indentation, exiting, like that. And now let's  

  • try to run the run.py file one more time, and  see what will happen. So I'm going to run that.  

  • And once we received the page, then you can  see that we have exiting, and right after it,  

  • the Chrome browser has shut down. So what that  means it means that once Python gets out of the  

  • indentation of weight expression, then it executes  automatically the double underscore exit middle,  

  • this is how complex managers are working. And  to have something like this, when we want to  

  • test multiple things throughout the development  of this project is perfect. Now to extend the  

  • option of when we want to tear it down. Or when we  don't want to tear it down, then we can basically  

  • receive here one more parameter. So I am now  inside the booking file, then I can say tear down  

  • is equal to false by default. And I can  say under the exit model, something like  

  • if teardown. So this basically refers sorry, this  will be self dot teardown. And we should go here  

  • and say self dot teardown equals to teardown and  then I can basically use here an if statement and  

  • say self dot teardown. So if this is true, then  run the self dot exit. But if this is false, then  

  • just don't do anything and leave the browser  open. And now when I want to basically tear down,  

  • then I can pass in here till now I'm equals to  true, or I cannot pass it. And if I don't pass it,  

  • then the browser will remain open. So againthis is a good way to have a control whether  

  • if you want to close up your browser or you want  to leave it open, go maybe sometimes you want to  

  • test something else, rather than just testing the  Selenium code. Now, let me also remove the exiting  

  • line now that we understood this. And I think that  will be it about the context manager. So I hope  

  • that you understood the concepts of how context  managers are working. Alright, so at that stage,  

  • we have left the previous tutorial, and  we understood in the previous episode,  

  • how we use this booking class in order to have  more control in our WebDriver chrome class. And we  

  • also designed some methods that could be helpfullike the double underscore exit. So we could have  

  • the option of using context managers when we  instantiate an instance of this booking class.  

  • And we also have designed this metal in herewhich is looking like laying the first page. And  

  • it basically lends the board on these cons thought  base URL, which is booking.com, that is located  

  • in the constants.py file. Okay, so now before we  go ahead and understand what are the next steps,  

  • let me do two more actions that are going to be  quite useful for us to have more control. So the  

  • first one is going to be to adding the implicitly  weight method. And just a quick reminder,  

  • implicitly weight is the metal that will allow us  to wait for some amount of time until the element  

  • is ready on the website. So if we were to say  self dot implicitly Wait, and for example, a pass  

  • in here 15 seconds, then no matter which method  is going to be executed with the prefix of find  

  • element, then it is going to wait approximately  15 seconds. But he could also proceed to the next  

  • find element method by less time because not  always, we'd like to wait 15 seconds, and this  

  • method is going to take responsibility for thatAnd the second line of code that I'd like to add  

  • in here is going to be self dot maximize windowAnd the reason I want to do this it is because I  

  • just want to have a cleaner look when I test the  board. So now let me go and run out run.py file.  

  • And now you can see that the web browser has been  opened in maximized window so it is more nicer.  

  • And you can also see that we are inside  booking.com as expected. All right. So  

  • now that we have done this, then the  next step in here is probably going to  

  • automate clicking on Choose your currency. Now  you might have think that we should automatically  

  • automate the Where are you going, and as well as  checking checkout dates, and selecting how many  

  • people we are, but actually, at first, it might  be a great idea to change the currency. So we will  

  • have like a more common look to all the deals that  are going to be resulted after we search for them.  

  • So now, let's basically understand what we need to  automate. So we need to automate clicking on that.  

  • And right after it, we need to automate clicking  on USD or euro or whatever currency we look for.  

  • Now, for the purposes of this tutorial, I am  going to try to automate clicking on USD, because  

  • basically, this is one of the popular currencies  out there. So this is what we should do, we should  

  • basically click on this one. And right after thatwe should somehow try to click on that one. And  

  • then you can see that right after we have clicked  on that, then the currency has been changed. And  

  • then we are ready to try to send some text in  Where are you going textbox. So now let's try to  

  • understand how we are going to automate clicking  on that. So I'm going to click right click and  

  • click on Inspect. So we can understand which  element is responsible to display this button.  

  • Now, I not sure why I have to do it twice, alwaysBut I really did not figure this out yet. But if  

  • this is happening to you, as well, then maybe try  to do it once again. And it will basically color  

  • the element in the blue background as expectedSo I will do that one more time. And you can see  

  • that this button is actually what responsible  to display this currency element. So now,  

  • when we try to identify an element with seleniumthen we should always try to be smart enough  

  • to identify this element in the most unique  expression as we can. Now the attribute of  

  • ID is actually the most unique attribute  that an HTML element that could have,  

  • but not always, we are going to have ID for HTML  elements. So that's why we need to figure out  

  • other approaches to find that element. And  in our case, maybe you would have taught to  

  • find this element with the class is equal to  be UI button. But actually, this might not be  

  • the most smart way to do that. Because there  might be some other buttons with that class,  

  • as classes in HTML are for styling elements. So  multiple buttons could have the same styling. So  

  • that's why maybe feel during this button with the  data tooltip text is equal to choose your currency  

  • is a more unique expression that we can use. So  now, I'm going to copy this expression from here.  

  • And I am going to basically identify it with  CSS selector method that we have learned in the  

  • third episode. So I'm going to go back to pi  charm. And down below, I'm going to say their  

  • change currency. And I will also receive currency  as a parameter in here so that maybe we could have  

  • the option to change to different currency  rather than United States dollars. So I'm  

  • going to say currency. And down below, I'm going  to say self dot find element by CSS selector.  

  • And I will open up and close and I will press  enter, because I don't want to make the lines  

  • of code at all much long, so that it is a great  idea to separate them in the following way. And  

  • I'm going to now open up a single code, and I'm  going to use button and open and close square  

  • brackets. Now if you remember, this is how we use  to work with CSS selectors, we first wrote the  

  • type of HTML element. And then inside we wrote the  expression that we should filter this button by,  

  • and this is going to be data tooltip text is  equal to choose your currency. And then it is  

  • a great idea to assign this whole expression  to a variable. So I'm going to say currency,  

  • underscore element is equal to that one. And  down below, I can say currency underscore element  

  • dot click, so it will automate clicking on thatNow let me change this parameter to have a default  

  • value. So we will not really have to pass the  value all the time. So now I'm going to open up  

  • the run dot p y on the right side. So let me split  that vertically. And let me open this in here  

  • like that, and then let me call these buttons. So  it will be bought that change currency like that.  

  • And then let me run this run.py file and  the test the results. So as expected, we  

  • are on the homepage, and you can see that we have  automated clicking only change currency element.  

  • So that is nice. That is a great first step to  achieve our goal. Now the next thing that we look  

  • to automate is probably to clicking on one of the  currencies. And for the purposes of this tutorial,  

  • let me first show how we could identify the  USD. Now in the future, we have a parameter  

  • that receives the currency type. So maybe we could  choose from the rounded p y, a different currency  

  • that we can automate to click on. So let me use  inspect again. So we could identify this element.  

  • And now if I click on this element  to expand its innerHTML elements,  

  • then you can see that in here, we have this  text and this text as well. But actually,  

  • the whole button is coming from this anchor  tag, because if I take my mouse to here,  

  • and I hover it, then you can see that we have  a green background surrounded our currency.  

  • So that's why maybe we should try to find these  a element. And we should use something that will  

  • help us to identify these a tag. Now I have  expanded the view of the inspect in here so we  

  • can have a cleaner look. And you can see that  we have here some key value attributes again,  

  • now I can try to find the data model header  async URL params, something like that,  

  • that is containing the text that looks like  selected underscore currency is equal to  

  • USD. Now I know that we did not learn how to  find elements that contains some substrings.  

  • But this is actually very easy with the CSS  selector method as well. So let's say in pie  

  • chart on how we can do that, alright, so down  below, I'm going to use a new variable that we  

  • can name it selected the currency element, and  that is going to be self dot find element by  

  • CSS selector. And here we are going to write the  expression again. So it will be single quotes a,  

  • and then we will use square brackets. And  now we will use these long expression that  

  • will help us to identify this element. So  it will be data dash, modal dash, Heather,  

  • dash, async dash again, URL dash Param, like thatSo I know that this is a long expression. But  

  • this is actually a great approach to identify this  element. And that is going to include a substring.  

  • So besides doing equals two, then we should use  asterisk equals. Now I know that again, this is  

  • not something that I have covered in the first  three episodes. But now we know how we can try to  

  • find an expression that contains substring. And  it is as easy as using the asterisk sign near  

  • the equal sign. And then I'm going to use here  double quotes. And I'm going to say selected  

  • currency is equal to USD like that. Now, if you  remember, then this is actually the value that  

  • this long the key had in this element that we  looked to automate. So now we could say selected  

  • currency dot click. And this should be enough to  basically automate clicking on the USD currency.  

  • Now, you may think to yourself, why did you hard  code this USD in here, besides you did not use  

  • the currency parameter. So that is a wonderful  point. And I'm going to just change it now.  

  • So I'm going to add here a formatted string. And  I'm going to refer to the value of the currency  

  • like that. And then what I'm going to do now is  going to run.pi by again, splitting the panes  

  • and working on run.py and I'm going to pass in  currency is equal to USD like the following. And  

  • then this line will be responsible to basically  replace this expression with the USD string,  

  • and this should be enough. So let's test  this out. So I'm going to run our program.  

  • Alright, so you can see that the  currency has been changed to USD.  

  • Now we could also test this one more time by  trying to click on a totally different currency.  

  • So let's go with G BP. Alright, so let's try to  do that. So let's open the PI charm again. And  

  • besides sending USD let's send GB P and run our  program again. And let's see now what will happen.  

  • All right, alright. So again, perfect. So  as you can understand, we have identified  

  • the perfect key value expression to identify the  element for changing the currency and from here,  

  • we We are ready to go forward with the next  step that our board should do in order to  

  • search for the best deals. Alright, so now that  we have completed the changing currency method,  

  • then let's design the next method that we shall  do now. So if you remember, we should now try  

  • to identify an element for sending some text to  the search text form element. So if we run our  

  • automation, again, just to have the browser being  open in booking.com, and wait for this automation  

  • being completed, then now we should automate  sending some text to this area. So I'm going to  

  • inspect again. And I'm going to try to find the  most smart expression as I can to send the text  

  • to this form field. Now, I said earlier in this  tutorial, that if our elements are including the  

  • ID attribute, then this is actually the strongest  attribute that the element could have to basically  

  • identify each uniqueness. So that's why I'm going  to identify an element by the ID with the value of  

  • s twice. So now I can go and say in a new method  in Python, something like this select place  

  • to go something like that. And I can basically  receive a parameter that will say place to  

  • go. And now I can go down and say search field is  equal to let me make the font bigger. So excuse  

  • me if you did not see the text quite well. So  I'm going to say here self dot find element  

  • by IE. And I'm going to find this by s, twice like  that. And at first, I'm going to do something that  

  • we really did not see before, which is  looking like search field dot clear. So  

  • clear stands for cleaning the existing text. So  if one day, we'd like to search something twice,  

  • then maybe we will have some leftovers. So that's  why cleaning the entire text ad first place is a  

  • great idea before we write some new fresh text. So  that's why I launched the clear method. And then  

  • I'm going to use search field dot send underscore  keys. And I'm going to basically saying the place  

  • to go that is arriving from this parameter. And  then I'm going to go to run the spy. And I'm going  

  • to say bot dot select place to go. And let's  use here New York as a first place to go. And  

  • now let's see what will happen. So I'm going to  run our automation again, and test the results.  

  • Now, by the way, we could comment out the  changing currency code, just because we  

  • don't want to probably execute it every time we  test a new area in our board. So I'm going to  

  • do that just in a minute. All right, so you can  see that the minute that I have wrote some text,  

  • then we received a drop down. So what  that means it means that we need to  

  • go now and identify the first result of our of  our drop down. And we need to automate clicking on  

  • that. So this is exactly what I'm going to doNow I'm going to click inspect on the drop down.  

  • And going to do that one more time. And  you can see that this is the value that  

  • we should automate clicking on that. So this  is actually an attribute with the tag of API.  

  • Now Li is actually standing for a list HTML  field that is basically commonly used if we  

  • want to display a list of elements. So that's  why we see this tag with the name of Li.  

  • Now we should again try to find how we  are going to click on these Li. So this is  

  • this element, I believe Yeah. And then if we  expand this a little bit more, then we will  

  • have a cleaner look. Now you can see that inside  each Li we have an attribute that says data dash i  

  • is equal to some string. Now I know that it is  hard to see. But if you will look in the bottom  

  • left, then you can see that once I point on that  element, then the first result of our drop down  

  • is being shown with green background. And ifwas to move my mouse to that element, then you  

  • can see that it now points to the second result  of the drop down. So what that means it means  

  • that the data I value is actually like an index of  all the results because the first result is having  

  • the value of zero and the second one is having  the value of one and the third result is having  

  • the value too, and so on. So that's why we call  the identifying element with the expression of  

  • data dash i is equal to zero. So I'm going to open  our pie chart, and I'm going to say right under  

  • select place to go something like first result is  equal to self dot find element by CSS selector.  

  • And our expression is going to look like l II and  square brackets again, and it will be data dash i  

  • is equal to zero like that. And then after  we have done this, then we are going to say  

  • first resolved dot click like that. And  then I'm going to leave everything as it is,  

  • because those lines are going to be responsible  to basically click on the first result. And this  

  • is exactly what we want to do. So I'm going to  run run up UI, and test the results once again.  

  • And I forgot to uncomment this section, so excuse  me for that. And now you can see that we got a  

  • perfect result, we automated clicking on the first  result of the drop down, and hence we have done  

  • that, then booking.com automatically took us  to the check in check out area. So here's the  

  • exact place that we can already try to automate  selecting check in dates in as well as check out  

  • there. So this is great. And this means that  we have done a wonderful job identifying how  

  • we call the click on that specific element. All  right, so this was the stage that we have left  

  • the previous tutorial. Now as you remember, when  we selected the location that we'd like to go,  

  • then booking.com, by default, popped up the check  in or check out the date that the user can select.  

  • So it means that we can automatically try  to select the check in date, and as well as  

  • the check out date. Now the time that I recordthis video will be may 2021. So if for yourself,  

  • you are seeing a totally different result, then  it probably means that you are watching this in  

  • some another date. So the results are going to be  different for you. But I'm going to write a method  

  • that will be generic enough to support any kind  of date. So let's go ahead and get started. So we  

  • need to somehow click on two dates, as the first  one will represent the check in and the second one  

  • will represent the checkout. So I will just click  on that right click and say inspect in do that  

  • one more time. And then I will go to the inspect  window. And let's see how we are going to automate  

  • clicking on those buttons. So you can see that it  is actually this one. And it is a child element of  

  • this TD element. Now te D stands for table dataAnd what that probably means it means that this  

  • element is part of a bigger HTML tag that is  called table, which is designed for creating HTML  

  • tables. And this is also how this page represents  the calendar. As you can see the table in here and  

  • when I hover my mouse, you can see that it totally  gets colored with blue background. So now this  

  • means that we need to somehow automate clicking on  this one. And we can actually use a circle again,  

  • by selecting an element with data date equals  to some date. As you can see, in this case,  

  • it is 2021 05 16. So I'm going to copy this  statement, and I'm going to go back to pi charm.  

  • And let's try to automate that. So down here in  booking.pi, right where our booking class is,  

  • I'm going to type in one more method that  we can call it something like select, shake.  

  • Or we can just call it select date, something like  that. And then we will receive two parameters like  

  • check in date in the as well as a check out dateAnd we can go down and we can say check in element  

  • is equal to self dot find element by CSS selectorAnd this is going to have as a an argument,  

  • the following statement. So I will open single  quotes, and I will use TD because this is the  

  • element we'll look to automate. And then we will  open up and close the square brackets. And we will  

  • paste in the statement that we'd like to identify  the element by. So now you can see that this date  

  • is hard coded in here. And I'm just going to use  formatted string and replace the value from being  

  • hard coded to have the value of check in datewhich we receive as parameter and then it totally  

  • makes sense to go down and say check in element  dot click and then we are going to do this exactly  

  • same thing for the check out date because this  is how we are going to select a range of dates  

  • for deciding the dates of our vacation. So I'm  going to go Now I'm going to say check elevate,  

  • element, excuse me. And it will be equal to self  dot find element by CSS selector one more time.  

  • And I'm just going to use the same statement like  we did with check in. And I'm going to replace the  

  • check in date with check out date. So down belowwe can again, use the checkout element that click  

  • initially enough to basically decide the check  in and checkout date. And the only thing that  

  • we have to do now is go back to our run.pi and  execute this method by passing in two arguments.  

  • So it will be bought that select date, and we are  going to pass in check in date is equal to let's  

  • say 2021 dash 05. For May, and then they have the  month will be 16. And then we are going to do the  

  • same for checkout date. And we will pass in 2021  05 23, for example. Alright, so let's go ahead and  

  • test our program. Now we understand that we don't  have to really test the change currency anymore,  

  • because this works. So I can allow myself to  comment this just for now. Alright, so let's run  

  • this out and see what happens. Okay, so this works  very well. And you can see that we have the check  

  • in and check out dates. Now, let me tell something  very important in here. Actually, let's say that  

  • you would like to automate for selecting dates  that are two months or three months from today.  

  • So this is something that I'm not going to show  in this tutorial, but you have all the utilities  

  • to basically achieve this by yourselfSo say that you want to select some date,  

  • that is going to be four months from today. So  this means that you have to automate clicking  

  • on this next button three times. And then you will  have the option to select range of dates, that is  

  • four months from today, which in my case will be  September 2021. So I just wanted to mention that  

  • I know that this program is not going to support  selecting vacations in dates, like five months or  

  • six months from today. But again, you have all the  tools that you can try to automate this action by  

  • yourself. Alright, so the next thing that I'm  going to straightforward show in here is how we  

  • can select the adults to decide our vacation. And  to do that, we need to first simulate clicking on  

  • this area. And then we are basically looking  for automating it clicking on this minus sign,  

  • or this plus sign to basically decide the adultsNow again, we will try to write a method that will  

  • basically receive a parameter with adults count  something like that. And then we will try to  

  • simulate deciding the adults for our vacationNow, I'm not going to show for children or rooms,  

  • because this is going to be probably the same  logic that we need to repeat. But again, you have  

  • the option to extend your robot in whatever way  you'd like to. Alright, so now, first of First,  

  • let's start identifying this entire area in  here. So inspect, and loaded again. Alright,  

  • so we look for the element that will basically  color the entire area of this selection button.  

  • So I think this will be this label with the  ID of XP, guests toggle. So I know that the  

  • font is a little bit small in here. But this is  basically what it says here. It says to us HP  

  • guests toggle. So I'm going to just copy  this value. And I'm going to go to our board.  

  • And I'm going to go down and write one more method  that we can again, name it something like this,  

  • select adults like that. And then we will receive  count as a parameter. And we will see how we are  

  • going to control the count of our adults in this  method. So down below, I'm going to say selection  

  • element, something like that. And I'm  going to use self dot find element by ID,  

  • and I will paste the value that I have copied  previously. So you can see that it is XP double  

  • underscore guests double underscore toggle, and  I'm going to just selection element dot click like  

  • the following. And now let me pass in some default  value in here like one. So we will not receive  

  • arrows when we call this from run dot p y and  then I will go here and I will say Bob dot select  

  • adults, something like that. And now let's see  what will happen if we will run our program.  

  • So until now, we should expect to see only  this page being popped up. I mean this area,  

  • not the page and it looks okay. It looks  like It works. So we can continue on to  

  • see how we are going to control the adults for  our vacation. Alright, so now we have to somehow  

  • control how we are going to select the adults  count. Now this might be a tricky action to take,  

  • because let's say that you want to go with  three adults, then it means that in that case,  

  • you only need to automate clicking on the  plus sign once because the default value is  

  • two. But what if one day the default value  is not going to be two adults in booking.com,  

  • say that the default value is going to be changed  to something like four or five, three or any other  

  • count. So we can actually be more smarter. Rather  than trying to automate clicking on the plus sign,  

  • depending on whatever adults count we are going  to pass in, in our method. So we can first try to  

  • decrease the adults count to the minimum valueAnd then we can have more control how many times  

  • we really need to click on the increasing buttonSo at first, we can write some logic to decrease  

  • the count of adults until the value reaches one  adult, because this is the lowest value of adults  

  • that we can have. And then we can try to click on  the plus sign, depending on the adults count that  

  • is passed in. So this is a more generic  way to complete this kind of task. And it  

  • is going to be very interesting to implement  that. So I'm going to go to our PI Chairman,  

  • you know what Before that, we need to understand  how we're going to click on the quiz button.  

  • So let's do inspect and see how we are going  to identify the element of decreasing. So let's  

  • expand this and see what is going on here. So  you can see that when I hover my mouse in here,  

  • then this button of decreasing is being colored  with the green background. So this is the button  

  • that we looked automate clicking on, and  you can see that it has a unique value of  

  • area label with the value of decreased number of  adults. And I believe that we are going to say the  

  • same thing for increased number of adults weight  area label as its key. So I think it should be  

  • this one. So if we open this element, and  see its attributes, so I'm just going to  

  • move here. And Alright, there it is. So you can  see that it has area label increased number of  

  • adults. So this is the approach that we're going  to identify those buttons. And let me basically  

  • copy the statement and try to find  this element by CSS selector. Okay,  

  • so right under the Select adults method, we  are going to use something like decrease.  

  • Adults element is equal to self dot find element  by CSS selector. And we are going to pass in  

  • button and open up and close square brackets and  basically paste the key value statement that we  

  • have copied previously. And you can see that it is  area label is equal to decrease number of adults.  

  • Now we need to somehow simulate clicking on that  button until the adults count reaches one because  

  • as we said, it will allow us to have more control  to basically decide the adults count. So for that  

  • I'm going to use while true, and then inside this  wild rule, we will write an if statement that will  

  • look up for the value of adults count. And once it  reaches the value of one, then we will go outside  

  • of our while loop. So I'm going to say here while  through. And I'm going to go here and basically  

  • fix the indentations. And then I'm going  to go down and I'm going to say decrease  

  • adults element thought click. Now if I leave the  code as it is, then it is always going to try to  

  • click on the crease button, which is obviously not  what we want. But we want some logic to basically  

  • identify if the value reached the number of oneAnd if so we'd like to get out of the wild true.  

  • So it is going to be something like if the value  of adults reaches one, then we should get out of  

  • the while loop. So let's see how we're going  to identify this. So I'm going to go back to  

  • our browser, and I'm going to search for the  element that displays the value. So it should  

  • be this one in here where my mouse is pointing  me zoom in a bit so you can see more clean. And  

  • you can see that I'm pointing exactly to here. So  let's go inspect and you can see that it actually  

  • depends on this span element. And you can see  that it has the text of one. So what that means,  

  • it means that we can try to basically Find this  element and see what its value. Now, if I add here  

  • plus, then you can see that it got updated. So  it means that this is the exact element that we  

  • need. Now I can actually see in this inspect page  that we have more elements that are being updated  

  • with the adults count. And you can see up top in  input in here that we let me zoom in. So you can  

  • see that this element is actually getting updated  continuously as well. And the reason I want to  

  • select this element is actually because it has  ID. And if we remember I said that the ID is the  

  • strongest attribute that the HTML element could  have to identify its uniqueness. So if I click on  

  • plus hearing, and then you can see that it gets  updated. So I'm going to just find this element  

  • by the ID of group adults. And I'm going to go  back to Python and see what we can do with this  

  • element. So I'm going to go here and say, adults  value element is equal to self dot find element by  

  • ID, and it should be the value of group adultsAlright, so now that we have control in the  

  • element that displays the value of adults countthen we should somehow receive the challenge of  

  • the adults. Now this is something that we did not  learn how we can do, because we only learned about  

  • actions like clicking or sending keys. But we did  not learn how we can receive a value of some key  

  • in HTML elements. So it is going to be by using  the adults value element which we have identified  

  • in here. And then it is going to be basically by  launching a method that is called get attribute.  

  • Now get attribute is basically a method that  receives a key name, and then it tries to give  

  • you back the value of whatever attribute it is. So  if we go back to our browser, you can see that we  

  • need to work with this key that is called valuebecause this is what displays the adults count,  

  • right? So we need this key. So I'm going to go  here and go back to a pie chart and basically pass  

  • in here value. And this entire statement should  give back the adults count should give back the  

  • adults count. So I'm just commenting this up  because the code is starting to be more complex,  

  • so we can remember what each line is responsible  for. So down below, I'm going to type in a  

  • conditional and it is going to be much better if  we will assign this entire statement that to a  

  • separated variable. So I can say something like  adults value is equal to that entire statement.  

  • And let me just write here some split lines  because we want to have more organized code. Okay,  

  • so down below, I can say something like if adultsvalue is equal to one like that, now, I already  

  • know that this statement is going to give us back  an error, because by default, the get attribute  

  • returns the value as strings. And we cannot  write if conditionals with comparing strings to  

  • integers. So we are going to convert these adults  value to integer exactly in here. And then we are  

  • going to check if it is equal to one. Now once it  equals to one, then we can break the while true.  

  • So let's see if this logic works. So we should  go ahead and test the run.py file by executing  

  • it. And we should see the adults count being set  up to one adult. So now I'm going to test this up.  

  • And Alright, so we can see that it works perfectthe adults has been changed to one. And the only  

  • thing that we have to do now is identifying this  plus button and basically say that we'd like to  

  • go away to 10 adults, then we'd like to launch  a for loop that is going to click on that button  

  • nine times. So let's go back to pi charm and  implement that now to really test if it works  

  • properly, then really we are going to assume  that we are having 10 adults in our vacation.  

  • So I'm just going to pass in this argument when  we call this method. So now let's go to our  

  • method and minimize this wild rule because we have  finished working with it. And we can say increase  

  • button element is going to be equal to self dot  find element by CSS selector one more time. And  

  • if you remember it was a button with the key  value pair of area labeled is equal to and I'm  

  • going to open those double quotes and say increase  with capital. I Number of adults with capital A,  

  • and then I'm going to go down. And I'm going to  launch a for loop, applying the range building  

  • function, so we can really have control in the  amount of times that this for loop is going to  

  • execute. So I'm going to say for i in range, and  we are going to use range of count, minus one,  

  • because if you remember, I said that we should  decrease this by one to really achieve the exact  

  • count that is passed in in here. So we are going  to say increase button element dot click. And  

  • that's it. Now I'm not sure if you know or notBut basically, if you're not going to refer to the  

  • variable that is used in here, then by conventionyou don't really need to pass in a variable name  

  • like I or a or something like that, you can just  use underscore like that and leave it as it is.  

  • And this is just a convention in Python that says  that we are not going to make use of the variable  

  • value. So now we are ready to test the entire  program. So I'm going to launch our run.pi file,  

  • and we show the C, the adults count being set to  10. So if we run our board, and wait a minute,  

  • all right, so you can see that it really works  perfect At first, we use this safety logic to  

  • basically set the adults to one and then we have  full control of the adults count that we'd like to  

  • set. Now this is really generic and working  perfect, because even if we are going to pass in  

  • count equals to one from our method, then what  is going to end up happening, it is going to go  

  • to here, and it is going to try to  apply a for loop in range of zero,  

  • and it is never going to run because if you are  having a for loop with range over zero, then  

  • you are basically not iterating over anythingSo the adults count is going to remain  

  • one. And that is perfect. So this logic  really works. And we are basically ready to  

  • go ahead and click on the search button to  really test if we are going to receive any  

  • results. So it is going to be very easy, because  by convention, each search button in whatever  

  • site it is, is mostly having a key value pair that  says type equals to submit something in that time.  

  • So we are going to click on Inspect and see  if this is the same for this website. So if we  

  • expand this, and go here and look up for this  button, and you can see that it has type equals  

  • to submit. So again, we can automatically try to  basically identify an element with CSS selector  

  • with HTML type of button that has this key  value pair. So if we go back to Python,  

  • we can easily just create one more method that  we can call it click Search. And we can basically  

  • receive nothing in this method because there is  not going to be any argument we'd like to pass in.  

  • And we are going to say straightforward itself dot  find element by CSS selector. And we should find a  

  • button with this key value pair, and basically  assign these to a variable like search button.  

  • And then later on, we like to click on thatAlright, so here, I'm just going to call this  

  • board dot click search, and see if everything  works properly. Now to really test the entire  

  • logic, let me uncomment the change currency and  change this to USD to really see if everything  

  • works. And let me challenge this program a bit and  pass in another date. So we can maybe say here 19,  

  • and pass in here 25. And really test if everything  just works and functions properly, even though I  

  • have customized the value that a little bit. So  if we run our program, and basically do nothing,  

  • so I'm not going to touch my mouse at all, and  see if everything works. Okay, so currency date,  

  • adults search, perfect, just perfect. Everything  really works well. And we see some results about  

  • different hotels from these booking.com  website. And this is just exciting because  

  • we really have some results going in here. And  in the next episodes, we are going to dive into  

  • more complex stuff because we need to only  identify the best deals and by saying best deals,  

  • we need to somehow automate applying  the filtration to that result. Alright,  

  • so here you can see that we have  some results about what we can really  

  • book. But as we can understand from this pagewe really have multiple options for what we can  

  • filter to basically improve the results that we  received. So for the purposes of applying some  

  • filtrations then we're going to need to write some  methods that will be responsible to apply those  

  • filtrations. But to those in that booking class  might lead us to a situation where we are going to  

  • have too much methods in one class. So that's why  we will basically come up with a new Python file  

  • that will include some methods about filtrationsonce our bot has reached the page of the results,  

  • so if we go back to our Python now, then we can  actually design our project in the following  

  • way. So let me open up booking.pi. And we can go  down here, and we can say something like, apply  

  • filtrations. And this filtration is should be  filtrations. And this one will actually go ahead  

  • and instantiate an instance of another class that  could be named something like booking filtration,  

  • something like that. And then in this class, we  could basically include some methods that will  

  • be responsible to really go ahead and apply those  filtrations with the scillonian. Driver. So let's  

  • go ahead and start working on such a designSo I can go here, and I can say, new file,  

  • and I can name this booking underscore filtrationAnd then inside that, I can write a comment, what  

  • is the purpose of these pages. So it will be this  file will include a class with instance methods  

  • that will be responsible to interact with  our website. After we have some results,  

  • to apply filtrations, so we could really  understand what this page is about. And  

  • right after that, we can basically go down and we  can say class booking filtration. And this class  

  • is not going to inherit from anything. So we can  go directly inside the constructor. And actually,  

  • the constructor needs to receive one parameterAnd this parameter is going to be actually the  

  • driver that we are going to pass in his argument  because we also need this class to work with the  

  • WebDriver. So if I go to booking.pi, then we  can actually see that through how the process  

  • of writing methods, we do it on the self objectSo we need to somehow pass the self object to the  

  • instantiated instance of this booking filtrationSo this is going to look like something like  

  • rival equals to self in the future. So that's  why we should go to our booking dot filtration,  

  • and basically receive here driver as a parameterSo before we continue working on this one, let  

  • me quickly fix the arrows in the other file. Sowill right here and pass to ignore the arrows. And  

  • I will go to booking.pi. And I will basically go  up top of this file. And I will say from booking,  

  • dot booking filtration, import  looking filtration like that.  

  • And I will jump here, one more lineAnd I will go down here. And I will say,  

  • filtration, something like that. And I will  basically instantiate the booking filtration  

  • class. And I see that I missed the tea here as  well. So sorry about that. And then later on,  

  • we can basically decide what kind of filtrations  we want to apply within this method. So that's  

  • actually a good way to design our program. Because  once we have too much methods in our class,  

  • then we might have a tough time to maintain our  code in the long run. So that's why designing  

  • the filtrations in the following way could begreat idea. Alright, so now that we understood how  

  • we could structure the booking filtrations, then  let's see some good candidates for what we can  

  • filter by the results. And you can see that we can  basically apply some star rating for the results.  

  • So that's going to be the first method that we're  going to write within the booking filtration  

  • file. Now we can see that we have this entire box  in here that is responsible to give us back all  

  • those check boxes. So I'd like to first work with  that element, because you can see that we have  

  • some more check boxes in that page. And I don't  want to confuse these five stars, for example with  

  • that one, because this same check box is appearing  also in the popular filters. So that's why it is a  

  • better idea to first take our board and identify  the element that is responsible to display this  

  • whole area. So I'm going to try to click on  Inspect in here. And we're going to do that  

  • twice. And you can see that we have this filter  category title. So the parent element of this  

  • is probably what is responsible to display all  those checkboxes. And if we take our mouse to the  

  • parent element in here, then you can see that this  is exactly the box because it also says filter box  

  • in its class name and it also has an ID that says  filter on this coral class. So that is perfect,  

  • because we can try to use our board to basically  select this element with that ID. And then we are  

  • going to do something very special that will be  responsible to display all the Child Elements  

  • of this div element. So let's go ahead and work on  that. So I'm going to copy this statement in here.  

  • And I'm going to go back to our PI charm. And you  can see that I am inside the booking filteration  

  • file. So at first, we should assign the driver to  the self object of this booking filtration class.  

  • So it is going to be self dot driver is equal to  driver. And if you remember, this is always going  

  • to receive the original driver from the booking  class itself. So that's why I do this little trick  

  • in here. And I can basically launch a method like  a fly star rating in here. And this will receive  

  • self as a parameter. And now I can use something  like self dot driver, dot find element by ID.  

  • And the ID should be filter underscore classbecause this is the ID of the element that  

  • would like to filter by now notice how we did not  have auto completion. And this is very annoying,  

  • because in libraries that are very large, like  selenium, we probably always like to have auto  

  • completion for the different methods to basically  make our lives more easier. And the reason that  

  • the auto completion doesn't work, it is because  this parameter doesn't have a specific type. Now  

  • there is actually something in Python that  is called typing. So we can really decide  

  • the parameters type to work with auto completionsNow to show you an example of how type things are  

  • working, then I will use I mean to explain this  with a more familiar, a type of variable that we  

  • commonly use that is called a list. So say that  this class is actually going to receive one more  

  • list like my list, and we always like to pass  in here a list. But if we go here, and say my  

  • list, then we will not have auto completion  for methods like append, remove, and stuff  

  • that is very related to lists. And again, it is  because this parameter doesn't know it's typing.  

  • So we could actually do something like from typing  import list. And we could go here, and we could  

  • say that this one is going to be list. And now  if I was to say, dot, then you can see that I  

  • have auto completion for the list methods. In  the same approach goes for the driver instances  

  • of selenium, we could basically import a library  to decide this drivers type. And it is going to be  

  • as easy as going here and saying from Selenium  dot WebDriver dot remote dot web driver import  

  • web driver like that. And let me remove the  examples because we really not going to receive  

  • list as a parameter. This was just an exampleAnd then I'm going to say here, call on  

  • web driver. And once we have set the type  of this driver parameter, then we are always  

  • going to have self dot rival dot and you can see  that we have those auto completions as expected,  

  • because now the self dot driver object knows its  type. So that's why this is very useful. And now  

  • we can go ahead and basically assign this tovariable so we can name it something like Star  

  • filtration box. And then we could use that. And  now the next step should be how to identify the  

  • Child Elements of some element that we have  selected. And this is something that we can  

  • achieve by using CSS selectors. So let me show you  how this could be done. So star child elements,  

  • this is our I'm going to name my variable. And I'm  going to now use self dot driver. And I'm going to  

  • use find elements and not elements. So I know that  this is the first time that we use more than one  

  • element as a method. But actually there is also  the option to finding more than one elements in a  

  • website. So I'm going to use find elements by CSS  selector. And actually there is a convention to  

  • find all the Child Elements of a given element and  that will be by basically using the asterisk sign  

  • inside a string. Now I want you to take a look  and think about what is wrong with this statement.  

  • So we go ahead and say self that rival and we  launch. On top of that these find the limits of  

  • ICS cert or method. So this will end up giving us  all the elements of this entire webpage. And this  

  • is wrong because we only want the The elements  of star filtration box element, I mean all the  

  • Child Elements of it. So what we can actually do  is we can remove this self dot driver, and we can  

  • replace this with Star filtration box. And then  this line will be responsible to give us all the  

  • elements that belongs to that star filtration  box element. So now, we could go ahead and  

  • use something like print when star child elements  to really see that we have some elements stored in  

  • this variable. And we can go to booking dot p y.  And we can also use here something like filtration  

  • dot apply star rating. And if you remember, we did  not use this method in our main file, which was  

  • run.pi. So I'm going to go here, and I'm going  to always execute what dot apply filtrations. And  

  • we want to leave here, this method always being  executed. And then we will customize what methods  

  • we want to execute in this apply filtrations  method inside the booking.py file. So now let's  

  • execute our entire project. So I will run run that  pie and see what happens. And in a minute, I will  

  • also show you the results in our console, because  we are printing something. So let's wait for that.  

  • Alright, so if we go back to here, then you can  see that we receive as a result for the end. Now  

  • inside those for the elements, we look for the  five checkboxes that we need. Now if we go back  

  • to the Chrome browser, again, I actually have set  up what are the exact elements that we need. So  

  • we can see that I have expanded here, this filter  class element. And if we take a look a little bit  

  • more down, so if we go to like this area, then in  here, you can see that it is more focused on the  

  • checkboxes. And inside of that, we have all those  eight tags. And those eight tags are what really,  

  • we should click on them. And you can see that each  of those eight tags are having some more child  

  • classes, I know that it has a very long structureAnd you can see that at the end, the innerHTML of  

  • one of the Child Elements is one star. So what we  can actually do, we can try to iterate over those  

  • for the elements in we can look up for those that  are having the substring of one, two, or three,  

  • or four or five. And this is the same structure  for all other stars. So if we go and expand the  

  • two stars element, we can actually see that it has  at the end innerHTML with the value of two stars.  

  • So those are the elements that we look to click  on them. So that's why we can go to here. And we  

  • can basically open up the looking filtrationAnd we can work on those star child elements  

  • by iterating over each one of them. So we can say  four star elements in star child elements. And we  

  • could use something that will look like if star  underscore element dot get underscore attribute.  

  • And we should receive the attribute that is  really responsible to display the inner text.  

  • So it should be inner HTML like that. So this  is a convention to find the values of what is  

  • inside those HTML tags. So for example, if we  were to have a tag that is looking like that,  

  • and we have some value like gym, then if we want  to receive the gym value, only, then you show  

  • the launch obligate attribute medal. And so that's  why we use this expression in here. And we want to  

  • look if this entire expression here is equal to  something like one stars, right, so this is what  

  • we look up for. And of course, I'm not going to  hard code this in. And besides we are going to  

  • receive use something like Star value. And we want  to turn this to a formatted string. And we want  

  • to change this tool store value like that. And if  this entire expression is true, then we want to do  

  • something. Now before I go ahead inside of this  if statement. Let me write in here fast. At first,  

  • we want to convert this entire expression tostring just to be more safe. So I'm going to just  

  • do that and wrap all this expression with the str  method. And I'm also going to launch a method that  

  • you may be seen before that is called strip. Now  this strip is a method that will be responsible  

  • to clean all the white spaces, because we might  have some white spaces when we want to see the  

  • values of the inner HTML files. And this is  basically going to be responsible to clean  

  • all of those. Alright, so now I can go inside of  our if conditional and I can basically say star  

  • element, dot click. And the reason I can do this  right now, it is because we are going to iterate  

  • over for the elements. And if we have one element  that eats innerHTML is actually star value stars,  

  • then we probably want to click on that. So let's  test this in action. So I'm going to basically  

  • now run our run bot p y. But before we do thatwe should go to booking dot p y, because this  

  • is where we use the applying star rating. And we  should pass in some star value. So let's go ahead  

  • and try to click on five stars. So I'm going to  pass in in here, and let's do these keywords so  

  • we can really understand what this is aboutAnd now we can launch our bot again. So let's  

  • go ahead and do that. And let's see what will  happen. So we search for some results as usual.  

  • And then it should go ahead and try to click  on five stars. And if we actually go down,  

  • then you can see that it click on here. So that  is perfect. And you can see that this one got  

  • also activated. And this is great because we were  able to really achieve our goal. Alright, so now  

  • that we understood this, then there is going to  be one more in the future that we'd like to add  

  • to this method. Because this is just realistic  sometimes to search for results that are kind of  

  • like four stars or above, or three stars or above  or something in that kind. So that's why we should  

  • somehow receive as a parameter, not just one  store value, but maybe receive more than one store  

  • value. And the way that we can achieve this is by  turning the store value being arbitrary argument.  

  • And that is a way that we can pass in many  arguments to one argument, basically by doing it  

  • with adding an asterisk sign before the parameter  name. Now if you don't know what this does in here  

  • deeply, then I have a video that explains what  is the difference adding one asterisk sign near  

  • a parameter to adding a double asterisk sign. And  this is commonly seen with orgs. Or maybe you have  

  • seen previously double asterisk key w arms. And if  you don't know what are those, then I have a video  

  • that explains the differences between those in the  suggested video. So definitely consider taking a  

  • look on that one. All right. So now that I have  changed this to being an arbitrary argument,  

  • then let me change this to store values because  it will be just more friendly name for that kind  

  • of variable. And I can go down and basically  use a statement like for store value in store  

  • values. And I can take my entire code in this  area and basically insert this into a new for  

  • loop. So it will be something like that. And  now that we have done this, then let's test  

  • this out with passing in multiple style valuesAnd the way that we're going to do that is as  

  • easy as going to booking.py. And besides passing  in one single value, let's try to pass in three,  

  • four, and also five. So we should see the results  that their star rating is three stars or above.  

  • So if we run our program now and wait a few  seconds, then let's see what will happen.  

  • So as usual, we see the regular  behaviors in the searching results.  

  • Alright, so if we take a look in here, then you  can see that we have activated those star ratings.  

  • And if we actually take a look in your filters  box, there, you can also see that it has been  

  • activated successfully. So we have done a really  great job implementing the star rating to this  

  • booking.com website. And we can continue from  here. Alright, so you can definitely understand  

  • that there is no limit for the filtrations that  you can apply right after you are inside the  

  • results page. So I'm going to add one more  method for the purposes of this tutorial,  

  • that I personally think that is very useful. And  I think if we could have the option to take our  

  • board to click on the sorting option from lowest  price to higher, then it is going to be very,  

  • very useful. So if we take a look in the results  page, then you can actually see that up top we  

  • have some more filtration or basically just some  more utilities that can help us to customize  

  • our results whether you can see that we have the  option of clicking price lowest first. So we would  

  • like to automate clicking on that. And I'm going  to click on Inspect. And you can see that this  

  • is just a button with only this attribute in here  which we have to find to probably click on that.  

  • And that is going to be this data ID is equal to  price. So we can grab this key value attribute and  

  • find This way, the CSS selector and basically  try to click on that one. So we can go back  

  • to our pie charm, and basically to our booking  filtration file. And we can design here one more  

  • method, and we could name this sold price lowers  first. And this is just going to try to find an  

  • element. So we can name it just element. And that  will be equal to self dot driver, dot find element  

  • by CSS selector, and this will be that one. And  this will just receive Li because this was the  

  • element pipe. And we will open up our  square brackets. And we will say data  

  • ID is equal to actually I copied this statementSo excuse me about that. And I can just paste in  

  • data ID is equal to price, make sure to use double  quotes in price. And then down below, I can say  

  • element thought click in that will be it. Now if  you remember, this is only the page that we write  

  • our methods, but we don't actually call them in  the place that we call them is inside the spoking  

  • dot p y file inside the apply filtrations. So  right after we apply some storage thing, we could  

  • also sold our results. So we could say filtrationbut sold price lowest first and this is not going  

  • to receive anything as parameters. So we are  not going to pass any arguments. And now let's  

  • test this entire program now inaction. And let me  actually test our program and wait and only use  

  • four to five stars. All right, so if we run thisthen and wait a bit, let's see what will happen.  

  • Alright, so you can see that we have also  activated the price lowest first button,  

  • as you can really see that we have some more  cheaper results now in the first page. And  

  • if we take a look in our filtration, then you  can see that we got four stars and five stars.  

  • So I think this will be pretty much enough  about the filtrations that you can apply.  

  • If you want to basically see useful results. Of  course, there are more things that you can apply  

  • to receive better results. And as you can see the  technique of that it is pretty much going to be  

  • similar to finding the elements with the  different methods that you can use within  

  • the Selenium library. Alright, so one of the first  things that we're going to do in this episode is  

  • to support running this project, not always  from an ID, because in some cases, you only  

  • want to execute the bot to do its actions and to  receive some results. And for doing such action,  

  • you may not always want to open it in a specific  IV. further than that, sometimes executing  

  • bots from the command line is just more  comfortable rather than going to pi charm and  

  • basically pointing to your project. And then using  the shortcut of shift fn to execute your project,  

  • sometimes the only one to do it from a terminal  executing Python, and then referring to the file,  

  • in our case it is run.pi. Now I'm not gonna  lie to you, but our project currently does  

  • not support such a behavior. Because we have  this tricky line inside the booking.py file,  

  • that is going to add the location of our  driver path to the PATH environment variable.  

  • Now executing such a line from Python will work  because the Python knows how to take this line  

  • and attach it to a Python process and really  add this location to the path system variable.  

  • But when it comes to other processes that  we want to execute the project from them,  

  • for example, a common line interface in Windowsthen we are going to have some troubles because we  

  • need to add the location of our Selenium drivers  before we execute the project. So it is going to  

  • be tricky to handle that from the terminal levelAnd I'm actually going to prove you that our  

  • project won't execute successfully if we will try  to run the run.py file from a terminal. So if you  

  • remember, in this directory, we have this filewhich we look to execute. And if I was to say  

  • Python, run dot p y, then you can see that it  complains about how the chromedriver executable  

  • needs to be in path. So I'm going to fix that. So  you will have the ability to execute this project  

  • from your terminal. It doesn't matter if you  work with a Linux environment or if you work  

  • with a Windows around. Alright, so the way that  we're going to fix that is by using try and accept  

  • blocks. And we can actually try to execute those  lines of code. And if we will have some errors,  

  • then we could always bring to our users that the  user needs to basically execute some line from  

  • a command line that will add this C Selenium  rivals folder to the path system variable. So  

  • let's go ahead and try to design that. So I'm  going to Grab all those lines of code in here,  

  • and I'm going to indent all of those ones. And  I'm going to wrap it up with the try block. Now,  

  • right after this, we can go down hereand we could say, accept any exception.  

  • And we could grab the exception inside a variablesomething like E, maybe. And as a starter,  

  • we could say something like print, there isproblem or running this program from command line  

  • interface. So that is just a great start handling  things inside the except block, of course,  

  • there is going to be some more additions in the  future. So I'm going to go back to our terminal,  

  • and I'm just going to run the same command. So it  is going to be python run.pi. And you can see that  

  • now we automatically see this, there is a problem  running this program from command line interface,  

  • because we were able to hit the except blockbecause we really have some exceptions.  

  • But in some other cases, we might also  have some problems directly in our bot.  

  • Now, not all the exceptions in the world are about  how the chrome driver is not inside the path.  

  • So that's why it is quite dangerous to  output something like that for all the  

  • exceptions that are going to occur in  our program. So that is why we need to  

  • verify that we really hit this message that says  chromedriver executable needs to be in path. So  

  • that's why we could go back to Python. And  we could verify that the message is really  

  • about how there is not a folder inside the system  path. So we could say if str E. So that is a way  

  • to cache the exception message. And we could go  back to terminal and check the substrings of this  

  • specific exception. So if we were to check if this  in path is a substring in the exception message,  

  • then it really means that the problem is about how  the chrome driver executable needs to be impaired.  

  • So I'm going to go back to pi charm. And I'm going  to say if impaired inside a string like that,  

  • in the exception message, then we could go down  and we could print this message. But if we have  

  • some other problems say that we have a problem  with selenium, say that we have a syntax problem,  

  • then we would like to raise the  original problem. And that is achievable  

  • by easily saying else. And basically using the  keyword or phrase like that. Now the minute that  

  • you are doing something like this, then once we  don't hit that specific problem, then we really  

  • raise the original exceptions. So that is a great  way to handle the exceptions. So now we could  

  • verify that this works. So we could go back to  our terminal. And we could clean the screen for a  

  • minute. And we could say again, python run.pi. And  you can see that we again received this message.  

  • So that means that our if conditional works  great. Now To be honest, I am going to change this  

  • message to be more friendly. So the user could  understand what command needs to be executed to  

  • add the location of our chrome drivers to the  path system environment variable. So I'm going to  

  • grab a code snippet that I prepared as a print  message. And I'm just going to paste the same  

  • in here. And obviously, you can grab this from  my website in the eighth episode of our entire  

  • series. So you can see that here I have a nice  error message that says you are trying to run  

  • the bot from command line. And the backslash n is  just an escaping characters to just jump to the  

  • next line. So in the next line, we say please add  the towpath your Selenium drivers and for Windows  

  • use this command. Now I know that this looks  a little bit complex. But that is actually a  

  • built in Windows command that we can execute to  add some more locations to our already existing  

  • pad system around variable. Now as I said in the  first episode, our path environment variable has  

  • already around 50 or even 100 locations that we  have as a value. So that's why we use set path  

  • and we add the original paths. And in  addition, that's why we have semicolon,  

  • we add our path, which is going to be C Selenium  drivers or whatever folder you have set up the  

  • chromedriver window and if you use Linux, then you  should execute this line which is looking pretty  

  • much similar. Only the windows includes the set  command. Alright, so now we could basically go to  

  • our terminal. And again, run this command and now  you can see that we have a very friendly message.  

  • So the only thing that I have to do now is grab  this and paste this in and now really customize  

  • the path that we should have. Add here. So it  will be Selenium rivals, once again. And I'm  

  • not always sure if it needs to be added withbackslash at the end or not. So I'm going to try  

  • both. And I recommend for you to try both  as well. So let's try that and then try to  

  • run Python run.pi. And you can see that I againreceived this message. So let me clean the screen  

  • and run the same command with a backslash at the  end. And again, executing python run.pi. And now  

  • you can see that our bot works. And you can see  that everything is pretty much functioning as  

  • expected, you'll see that we were able to search  the results, you can see that we were able to  

  • filter out and also applying the sorting. So this  means that it works perfect. And if we take a look  

  • to our terminal, then you can see that we actually  received some warnings that we are really going  

  • to fix soon. But at all in the big picture, our  board functions perfect if we even execute it from  

  • a terminal. Alright, so now you might notice some  weird warnings about their withdrawals, listening  

  • to our localhost with some port, etc. So that is  actually something that we did not see before.  

  • But actually, when we work with browsers like  Chrome, which is in our case, Chrome driver,  

  • then chrome now comes built in with some  dev tools. That is a set of web developer  

  • tools built in directly into Google Chrome  browser. And I think when it recognizes that we  

  • execute some automated Chrome browser to run some  test cases, or basically automating websites, then  

  • it already executes that utility. Now, in this  tutorial, I'm not going to cover too much about  

  • their tools. So that is why we can allow ourselves  to ignore those kinds of errors. And to ignore  

  • those kinds of errors in that stage, we  need to go back to our booking.py file,  

  • and basically add some additional configurations  to our WebDriver instance. So it is going to be  

  • as easy as going back to pi charm and add some  lines in this file in here. And if you remember,  

  • we have some line that says super, that is  really responsible to instantiate an instance  

  • of the inherited class and as well as the class  that we are writing just in that moment. So that  

  • is why we need to customize this line, because  we need to pass in some additional options to  

  • this class that we inherit. So it is going to  be something like that. So we are going to say  

  • options is equal to web driver dot chrome  options. And you can see that it is just a  

  • built in class that I instantiate. And then  I can say something like options, dot add  

  • experimental option. And I'm just going to write  in here some strings that will be responsible to  

  • ignore those kinds of errors. And I'm not gonna  lie, I searched a lot about this dev tools. And  

  • why do we see those warnings. So I ended up by  grabbing this code from a Stack Overflow poll,  

  • which I will add in the description. So you can  take a deeper look in the discussion that is going  

  • on there. Alright, so it will be exclude switches  like that, pay attention that the switches is with  

  • a capital S. And then there is going to be one  additional value that I will pass in here, which  

  • is going to be accepted inside the list with one  element and it is going to be enable dash logging,  

  • like that. And once I have done this, then we need  to grab the options, which is equal to an instance  

  • of Chrome options. And we need to pass this in  in the initialization line. So it is going to be  

  • options is equal to options like that. And then  once I have done this, then let's test out if we  

  • still see those logs. So I'm going to open back  our terminal. And I'm going to say again, python  

  • run.pi. And I'm just going to let our bot running  in the background. Let's see what is going on with  

  • the terminal. And you can see that now since  we ignore those kinds of logs, then we really  

  • don't see anything. And you can see that our bot  finished its job. And that is perfect, as we can  

  • see the results in this Chrome browser page. So  that is one way that we can ignore those kinds of  

  • errors. And we can continue from here. All rightso now that we were able to figure this out,  

  • then there is going to be one more thing that we'd  like to test in this stage. Now we like to test if  

  • we will really see the original exception, even  if we are having an exception that is not about  

  • the chrome driver being in the path. So that's  why let's do something wrong in our project by  

  • purpose. So I'm going to go to our PI charm and  I'm going to say hear a is equal to two divided  

  • by zero like that. And we should receive basically  zero division error before even our board  

  • starts. So that is why we should now go back to  our table again. And let's clean the screen and  

  • try to execute our bot one more time and see the  results. All right, so we can see that we see now  

  • the original exception, which says to us zero  division error, because we really try to divide  

  • a number by zero. So we will have some arrows  and you can see how our award did not even start  

  • executing the lines of code, because we had some  early exception. So that is perfect. And this is  

  • a great preparation for the future of this board  project. And we can now continue to extend our  

  • application as much as we want, and even test this  with a terminal. Now the main reason that I really  

  • wanted the ability to execute this project fromterminal, that is because if you remember from the  

  • preview of the board, then we like to visualize  the results of the deals in a nice way. So the  

  • user can really have engravable look on what are  the results for the best deals. And we probably  

  • want to avoid doing that with the PI charm. And we  probably want to visualize it nice with terminal.  

  • So that is why I want the ability to execute this  board from any terminal, even if it's a Windows  

  • environment, or even if it is a Linux environmentAlright, so let's look into the next element that  

  • we should identify in order to start reporting  the results to the user that uses this bot.  

  • So as expected, I'm going to run this project  now from the terminal. And I'm going to make use  

  • of this recommendation that we have generated  in the print line. So we can really have the  

  • drivers folder under the patch system environment  variable. So I'm going to execute those lines. And  

  • as you can notice, I replaced this path with my  original path. So I'm going to run that. And right  

  • after that, I'm going to say Python run.pi exactly  like the previous episode. Now, just a reminder,  

  • if you remember, I showed some exception  by purpose in the end of the last tutorial,  

  • and I deleted that, and it was a zero division  error. So make sure that you have deleted this.  

  • And then I can go back to our browser. And you  can see that we have the results as expected and  

  • the filtrations and the sorting are pretty much  fine. Alright, so now we should look into that  

  • large element that is responsible to display all  the results. Now I'm going to be honest with you,  

  • I'm not going to show how you can see all the  170 results. But we are going to just seek for  

  • the first page for the purposes of this tutorialSo that means that we should identify an element  

  • that has 25 child elements that are responsible  to display all those boxes that are displaying the  

  • deals price, hotels name and star rating and  stuff like that. Alright, so let's get started.  

  • Now again, our goal is going to be first to find  that parent element that is responsible to display  

  • all those 25 boxes, that each one of the boxes are  responsible, as you'll see to display the hotel's  

  • name and star rating and stuff like that. So at  first, let me try to find the element that is  

  • pretty much covering the entire box. So I scrolled  up to the first result. And let's click here on  

  • Inspect. And I will do that twice. Excuse me about  that. And as you can see, this is for example,  

  • the title of the first box. And ifscroll up a bit, and here, for example,  

  • we only see the part that is without the  image, which is again, not what we want,  

  • we want the entire box. And as you can see, this  one is responsible to display the entire box.  

  • And if I minimize this element, and you can see  that now if I take my mouse to the second one,  

  • then you can see that the second box has been  covered with blue background. So this means that  

  • those div tags that are having maybe the data  score, or data or tail ID are what responsible  

  • to display each one of the boxes. And you can  also see that the parent element of all of those  

  • is a div tag with the ID of alternate list EnterNow before we go ahead and start writing this in  

  • the Python side, let me show you some tricks that  you could have done in the JavaScript side of the  

  • web pages. Now don't be afraid of JavaScript,  I know that this is a programming language,  

  • that there is a great chance that you did not  practice it. But I'm just going to make a short  

  • walk through how I identified when I developed  this project, all of those 25 boxes. So let's  

  • go to console Alright, so the council is actually  an area that you can execute pure JavaScript code  

  • that is going to be responsible to identify  each one of the elements that we look for.  

  • So if we go to console and Way, ignore what  is going on here. And let me zoom in a bit.  

  • So you can see everything. And I am first going  to clean everything in here. So it is going to be  

  • clear. And then this is a metal that we should  execute. So, like that, and then you have a clean  

  • console. Now to grab all the elements that are  responsible to display this page, then we can use  

  • the built in document statement. Now this is again  from JavaScript. So don't confuse it with Python.  

  • But I just want to make a short walk through how  I identified the elements that I need. So you can  

  • execute a method that is pretty much similar like  in selenium, and now it is called get element by  

  • ID. Now pay attention that E, B and the AI are  capitalized. And then I'm going to say here, or  

  • fail list underscore inner. And if you rememberthis was the ID that is responsible to display all  

  • the results. And I'm going to assign this whole  expression to a variable, we can name it like  

  • hotel list, and this is going to be equal to that  expression. And you can see that once I click on  

  • Enter, then you can see that it returns me the  element that is responsible to display all those  

  • boxes. Now I'm again going to clear the screen  and work with the hotel list library. And I mean  

  • the variable. And then on top of that I'm going to  execute get elements by class name. So II, B, C,  

  • and n are capitalized. And I'm going to find here  the class name that is pretty much unique for each  

  • box. So I actually found this ASR underscore  property, walk string as unique per each box. Now,  

  • you can see that even before I executed thisit is going to be responsible to give me back 25  

  • elements. So what that means it means that now  we have the control of all those 25 elements  

  • that we can work with. So if I press on Enterthen you can see that we have a result with  

  • 25 elements. So again, this is what  we need to do on the Selenium side,  

  • we should first find that parent element with the  alternate list, inner ID. And then on top of that,  

  • we should go ahead and execute, find elements  by class name, and we should pass in this value.  

  • So let's translate what we have done here to  Python. And I think that the fact that I showed  

  • you this on the JavaScript side is just one more  utility that could be useful for you. Alright,  

  • so we are inside pi charm inside the booking.pi  file. And I'm just going to go down here and  

  • type in one more method that we can call it report  results. And then inside this method, we are going  

  • to say self dot find element by ID, and it  is going to receive hotel list on the score,  

  • Enter. And then on top of that, we are going to  execute dot find elements by class name. And let's  

  • actually split the line so we can have a cleaner  look. So I'm going to do that in here. And this  

  • by class name is going to receive as argument  ASR underscore property underscore block. And  

  • I need to assign this entire expression to some  variables. So we can name it artell underscore  

  • boxes, this is going to be equal to that oneAnd actually, let's test first that this works.  

  • So I'm just going to say return ortel boxes, and  I'm going to go to our run.pi. And I'm going to  

  • say print the length of bot dot report resultsBecause if you remember, this is returning a list.  

  • And we only like to see the length of this listAnd of course, this should return us 25. And if it  

  • is then it means that we are ready to continue  on the further actions. So I'm just going to  

  • go to our terminal. And I'm again going to execute  Python run dot p y. And let's wait for the results  

  • of that. Alright, so the filtrations have been  applied. Now if I go to terminal, there, you can  

  • see that we receive 25 back. So this means that  we have done a great job receiving all those boxes  

  • that are responsible to display data about the  deals. Alright, so now that we have the control on  

  • each of the boxes, then we should somehow try to  pull the specific data that we need. And obviously  

  • we'd like to start with the details name. So we  should find a pattern to get the title of all  

  • the 25 deals. So let's try to find these up. So  I'm going to open our latest browser and see what  

  • is going on here. So let's go to our first box  and try to click on Inspect And if you remember,  

  • this is under the elements that we have foundSo this means that we can iterate over each one  

  • of the 25 boxes. And we can try to find an element  by a class name that is equal to ASR dash hotel,  

  • underscore underscore name. So I'm going to  remember that and start working on that one. Now,  

  • I want to start working on a new file now, because  we want to have the reporting in a separator, the  

  • Python file to basically have more organized  project. So I'm going to go to the booking  

  • directory. And I'm going to create a file that we  can name this booking underscore report. And let's  

  • document first what this file is about. So this  file is going to in clewd, methods that will parse  

  • the specific data that we need from each one of  the veal boxes. So this explanation is pretty  

  • much making sense. All right. So down here, we  are going to have a class that is going to be  

  • responsible to have some methods that will start  to display the data that we need in a nice table.  

  • So I'm going to start by saying class booking  report. And then this will not inherit from  

  • anything. So we are ready to straightforward  to receive something inside our constructor.  

  • Now what is making sense the most is probably  to receive as a parameter, the parent element  

  • that is responsible to display all those deal  boxes. So I'm going to receive as a parameter  

  • something like boxes section element. And then  I'm going to say here, self dot voxels section  

  • element is equal to that one. And then we are  going to instantiate an instance of this one,  

  • and we're going to pass in the element with  the ID of a pill list, enter. So let's work  

  • on that. So I'm going to go here, and I am going  to basically go up top, and I'm going to use from  

  • booking dot booking report, import booking  report. And I'm going to go down, and I'm going to  

  • say report is equal to booking report and I'm  going to pass in the hotel boxes. So excuse me  

  • for instantiating this before that one it  is wrong. So I'm going to just fix that.  

  • And I'm going to just move it and actually replace  this with the return because we really don't want  

  • this to return anything for now. So I'm just going  to instantiate it in here. And then I'm going to  

  • pass in hotel boxes like that. Now I'd actually  like to execute these find elements by class name  

  • on the booking report side. So I'm just going to  cut this from here, and only pass in the element  

  • with that specific ID. And now let me basically  remove this empty line from here. And I think that  

  • will be it. So later on, we can basically execute  some methods from the booking report class,  

  • like I don't know, display table, stuff like  that, I know that this metal doesn't exist,  

  • but I'm just assuming the future of our projectSo let me delete that. And actually, there should  

  • be one more area that we should remove everythingAnd this should be this one. So let's also remove  

  • the execution from here and continue designing  this. Alright, so now I'm inside the booking.py  

  • file. And I'm going to just leave everything here  as it is. And I'm just going to continue working  

  • on working report. Okay, so now that we have an  HTML elements inside this box section element,  

  • then we should go and execute find elements  by class name on top of it. So we will have  

  • all those 25 elements back. So I'm going to just  type in a metal that will say pull deal boxes, and  

  • this will receive self as a parameter. And I'm  going to say self, excuse me, this should be  

  • self dot boxes, section element, dot find element  by class name, and this will have the argument as  

  • ASR property block. And again, this is just going  to pull all those 25 elements. And what I want to  

  • do now is actually instantiate one more list  inside our init method. And I'm just going to  

  • say here, return to all the expression in  here, and I'm going to say in the init method,  

  • self dot deal boxes is equal to pull self dot pull  the boxes and then we will have control for all  

  • the elements under this name, which is in Making  much more sense. And I'm sorry that I missed the  

  • s right after find the elements. So, obviouslythey should be find elements by class name,  

  • because we would like to pull all the elements  that are matching this class name. Alright. So now  

  • that we have done this, then again, you might have  noticed that we did not have any auto completion  

  • about the find elements by class name. And  that is because we did not have typing for the  

  • box section element. And we are already familiar  with these from the seventh episode. So now that  

  • is going to be a very similar action to what  we have seen previously through how the series  

  • and it is just going to be importing  the typing for the web element class,  

  • and then basically use this specific  typing. So I'm going to say here,  

  • from Selenium dot WebDriver dot remote, that  web element, import web element like that.  

  • And then I'm going to say that these boxes  section element is going to be in kind of web  

  • element like that. And then we will start having  auto completions as expected. Alright, so now that  

  • we have done this, then let's start pulling in  the data that we really need from each one of the  

  • boxes. And as a great starter, we'd like to first  poll the hotel name for each one of the boxes. And  

  • if you remember, I said that we have a specific  spend tag with this unique class name that says  

  • s our hotel underscore underscore name. So I'm  going to find all the elements with that class  

  • name. So it will be going back to pi charm and  say something like their full titles. So again,  

  • receive self here as a parameter. And then I'm  going to basically iterate over each one of  

  • the boxes. So it is going to be for deal box in  self dot deal boxes. And I will say deal box dot  

  • find element by class name, and we will paste in  the value or the class that we are looking for,  

  • which is ASR dash ortel, underscore underscore  name. And then what we'd like to do with this  

  • element is basically pulling its inner  HTML, because if you have noticed in here,  

  • this element has the alltel name inside its inner  HTML, which you can really see from here. So it  

  • is just going to be as easy as saying to that  expression, something like that. So let me first  

  • split this to multiple lines. And then I can say  on top of that dot get attribute. And we would  

  • like to receive the inner HTML of that elementAnd if you remember, when I use the get attribute  

  • innerHTML, I also executed some metal that will  be responsible to delete all the white spaces.  

  • And if you remember, it was dot strip, like thatAnd that will be it, this entire expression should  

  • really display the hotel's names. So let me first  assign this entire expression to a variable like  

  • hotel name, or deal name doesn't really matterAnd then we can basically try to use here, print  

  • hotel name and see if that works. Now, if you  remember, pull titles is not called anywhere.  

  • So I'm just going to go to working dot p y. And  I'm going to launch the methods that we need  

  • to report the results in this report results  method. So it will be report dot full  

  • titles. And now let's see what is going on  in the run.py file. And if you remember,  

  • we removed the report results section. So in  that case, it will be what that report results.  

  • And I'm just going to leave everything here as  it is. And now our Ward should be responsible  

  • to display all the hotel's names. So if  we go to our terminal, and say, again,  

  • Python run.pi, then let's see the results. And  as expected, we change the currency, we add new  

  • york and one adult, and we apply the filtrationsAnd we saw the prices. And now let's see what we  

  • have in the terminal. And you can see  that we really have all the hotels  

  • names, we really have everything that we need. So  we really have here 25 hotels, and let's actually  

  • see if the sorting matches our convention. So  the first hotel should be here to place New York  

  • City Times Square. And if we go here, you can see  that it doesn't quite match. And I know the reason  

  • for that because sometimes you probably need to  refresh after receiving those kinds of results.  

  • And we probably don't want to execute polling each  one of the titles too much fast. And they remember  

  • that this was a workaround. That I applied whendeveloped this project before I present it to you  

  • now. So what I'm going to do before pulling the  results, I'm just going to trigger a refresh to  

  • that page. So the board will have a second or two  to basically grab the data properly. So I'm just  

  • going to go to Python. And I'm going to say herebot dot refresh. And this workaround should do  

  • the trick. So I'm just going to say here, a work  around to plate our bot to grab the data properly.  

  • Now, let me try to execute our board one more time  and see now if it is going to work as expected.  

  • Now, I think that the reason it happens, it is  because we try to pull the data. And this holding  

  • didn't finish each job. So it makes sense to  refresh everything before we really tried to pull  

  • all the titles. So let's just clean the screen and  say python run.pi. And wait again a few seconds.  

  • Okay, so now the first hotel is appeal Edison  Times Square. And if we go to our terminal,  

  • and then you can see that the first hotel is  hotel Edison Times Square. And the second one  

  • should be Holiday Inn. And the third one should  be Paul Times Square. So let's verify that. Okay,  

  • so I think that now the results are displayed  as expected, this refresh really gives our board  

  • a second to breed, and to grab the data in the  ordering that we want. Now I want to do something  

  • important before I go ahead with this tutorialAnd that will be changing the amount of the check  

  • in date and the checkout date. And this is because  it has been a week or two weeks since I recorded  

  • the last episode in this series. So I just want  to make sure that the dates are going to match the  

  • today's date. So that's why I'm just going to  jump the date by one month only by changing those  

  • 206 besides 05. All right, so now we are ready  to continue to customize the data that we need.  

  • Now we said that we'd like to customize the data  in a nice table, where we will display the hotel's  

  • name and price and also storing the hotel's scoremeaning the rating could be a great idea. So if  

  • you remember, we ended up by customizing inside  the booking report file, this poll titles middle  

  • and this one is actually going over each of the  deal boxes and tries to pull some attributes  

  • that is going to be useful for us. So we could  take advantage that we iterate over each one of  

  • the deal boxes. And we could actually try to pull  the price and the score along the way. So that's  

  • why I'm going to remove the printing line from  here. And I'm just going to comment out what I'm  

  • doing in each iteration step. So I will comment  here, following the ortel name. And then later on,  

  • I'm also going to change the pole titles to more  generic function like pull deal box attributes  

  • something in that kind. And now I'm going to  go over and start basically pulling the prices.  

  • Now to save some time, then I'm just going to show  here, what was the approach of finding the price  

  • on each deal box. And as well as how I found the  score of each hotel. So that's why I'm going to  

  • straightforward, say here hotel underscore priceAnd that will be equal to do underscore box dot  

  • find element by class name. And then the value  here is going to be so let's open up strings.  

  • And then I'm going to say V UI dash price dash  display, underscore underscore value. And so if  

  • I have a class with this value, then it means that  this is going to give us the price. So that's why  

  • I can allow myself to do that. Now I'm going to  use the same approach of getting the innerHTML  

  • attribute. And the only thing the whitespace is  and if we remember we have done this in the hotel  

  • name as well. So I'm going to copy that and paste  this in and that line, I mean those three lines  

  • will be responsible to pull the hotel price. And  then if we go down below, and we also say here  

  • hotel score, and that will be by deal box, dot  get attribute and there is a great reason why I  

  • use here get attribute and not me dot wait find  element by something. And that is because this  

  • element has already an HTML attribute that looks  like data dash score. And so what that means it  

  • means that if we were to pull only the value of  this attribute in here, then it means that we are  

  • going to receive back the author score in scale  of 10. And so no tail score could be 8.5 9.1 9.5,  

  • and so on. And so now that I have done this, then  again, I'd like to clean the whitespaces. So just  

  • for safety, I'm only going to launch your dot  strip, like we did previously with price and  

  • hotels name. Alright, so now that we have a nameprice and score for each one of the deal boxes,  

  • then let's test first our problem. Now, I am  going to do something that might look weird to  

  • you. But I will explain just in a minute why I'm  doing this. So in order to test this out, then I'm  

  • going to go up here, and I'm going to use a list  that I want to name it something like collection.  

  • And that is going to be equal to an empty listAnd then as we keep iterating over name, price and  

  • score, I am basically going to need to add those  attributes to this collection. So we could have  

  • an organized and structured data. And I'm going  to use nested lists here where the collection  

  • will be the list. And that will include multiple  lists. And each list that it is going to have  

  • is going to include the three elements and one of  them is going to be name, the other one the price.  

  • And the third one is going to be score. So I'm  going to use here something like collection,  

  • dot append, and then I'm going to add to that one  more list. And then I will say hotel name, alltel  

  • price, and the other one should be hotel  score. And now that I have done this,  

  • then I really want to test out if I have all the  data that I wanted to pull from the beginning. So  

  • I'm going to use here return collection. And  I'm going to go back to our booking.py file.  

  • And I'm going to search for the method where we  report the results. And I think this should be  

  • here. And then I'm going to launch here  the method that that we have just finished  

  • to design. And that is going to be pull  deal box attitudes. Now, if you remember,  

  • we use return statements. So when we execute  this line, straightforward, then we are not  

  • going to see anything. So we're going to need to  wrap this up with the print built in statement  

  • so that we will be able to see the dataAlright, so now that we have done this,  

  • then let's open our terminal and test the resultsSo I'm going to bring our terminal to here.  

  • And I'm going to say Python run dot p y, and  let's see if everything is going to function  

  • correctly. Now let me move the web browser to  the screen. And let's see what will happen.  

  • Alright, so the bot is running. And now we should  see in the console of our terminal the result. So  

  • I'm going to open that up. And let's zoom outbit so we can understand what is going on here.  

  • Alright, so you can see that we are sort of having  a weird output. But let me break down what is  

  • going on here. So you can see that the first and  the last characters are actually square brackets,  

  • because this is the collection of data that we  have now. And you can see that this includes the  

  • list of First of all, and then the second  hotel and then the third hotel and so on. Now,  

  • I'm not sure why we see here an empty string. And  that is probably because this Webster square Tony,  

  • they rentals does not have a score. So that's  why it is an empty list. And you can see that  

  • as I keep going, we are having a lists inside  one bigger list that each list represents a  

  • collection of data about hotel name, hotel priceand also hotels call. And that is a great start to  

  • visualize our data with the pretty table library  that I talked about in the very beginning of the  

  • series. And again, this library is going to help  us to visualize the data in a nice table divided  

  • into columns and rows. And now let's see what we  need to do in order to be able to visualize our  

  • data nicer. Alright, so let's go ahead and see  how to visualize our data now. So I'm going to  

  • clean the screen, and I'm going to installlibrary that is called a pretty table. So it is  

  • going to be pip install pretty table like thatNow for those that are asking why I'm not using  

  • visual environment here, right now, because I'm  installing a library on the system interpreter,  

  • you can go ahead and do that. Because it will  help you to basically have organized environment  

  • for this specific project. I just don't feel  like going through something in few seconds,  

  • at least if I don't have a full tutorial on  that on my channel. So that's why I'm going  

  • to straightforward install those packages in  the system interpreter and I'm going to rely on  

  • that on this project. But if you feel comfortable  using virtual environments, then I really welcome,  

  • you will to do so because this is a nicer way  to organize your projects in your machine.  

  • Alright, so now I will use this line. And  you can see that I have this installed,  

  • so it means that we can work with this librarySo now I'm going to go back to our PI charm and  

  • see how we call work with that. Alright, so now  that we have the collection of data in here,  

  • then we are going to write here a few more lines  to basically display this in a table. So I'm  

  • going to scroll up. And I'm going to import the  pretty table library. But I'm going to import only  

  • one class from this library. So it is going  to be from 3d with the table, import free t  

  • table like the following. And then we need to  instantiate this class. And so it is going to be  

  • here. So I'm going to say right heresomething like that. So I will use here table  

  • is equal to 50 table, and this one is going to  receive a few arguments. And the first one is very  

  • important, which is the field names. Now I said  that in the table, we are going to have columns.  

  • So in our case, we probably want to declare herethree columns. And the first one will be the  

  • name and then the price and then the score. So I'm  going to pass here straight forward a list that is  

  • going to look like the following. So I'm going to  use here, alltel name, and then I'm going to use  

  • hotel price. And then I'm going to use hotel score  like that. And again, those are going to be used  

  • as the columns. So let me use actually a keyword  parameter in here so we can understand. Alright,  

  • so now that I pass this argument, then we need  to go ahead and create some rows in our table.  

  • So it is going to be as easy as using the Add  rows method. And then we are allowed to pass a  

  • collection of data. And guess what we are going  to pass in the nested list that I created a few  

  • minutes ago. So that's why in here, I use a nested  list. So it will be easier for us to basically  

  • pass in directly this collection list objectSo it is going to make our lives very easy.  

  • So now the only thing that we need to do in this  booking.py file is going to be table dot add  

  • underscore rows. And then we can basically  pass in whatever this returns in here. So it  

  • is going to be just copying and pasting this  right there. And then I'm going to basically  

  • leave it as it is. And actually we need to print  the table itself. So excuse me for deleting the  

  • print line before. Now we need to print the table  itself to really see the real table with columns  

  • and rows. Alright, so now that we are readynow let's go back to our terminal and actually  

  • clean the screen and run our word. So it  is going to be Python run.pi again, and  

  • let's see what will be the results. So I'm going  to display the results in here. And we will see  

  • in the background just in a few secondsthe table that we expect to see. So now,  

  • you can see that we have this nice organized  the table that is really responsible to display  

  • everything that you need about the deals that  you read from the booking website. And you can  

  • see that it is very, very organized. And you can  see that it is with the sorting of lower to higher  

  • because we have applied this filtration throughout  the series. And you can see that it is just more  

  • easier to read the data in that way. And you can  also use this pretty table library for different  

  • projects as well. I really like this library  to visualize data when I need to do some tasks.  

  • And I want to display the data that I received  back. Usually I work with this library because  

  • it really displays the data nicer, and it's just  more comfortable to look at. Alright, so now that  

  • we have completed this, then it could have been  nicer to control each time how we want to execute  

  • our bot. So we might want to see results for  different locations in the future. And for sure,  

  • we also like to change our check in date and  check our date, depending on what is the exact  

  • time that we want to prepare for a location. So  that's why maintaining those kinds of information  

  • in the code itself might not be a great idea. So  that's why what we can do exactly like I showed  

  • in the very beginning of the series, how the  project is developed from the beginning is to  

  • turn those hard coded strings into obeying inputsAnd then we will have the ability to ask the user  

  • about those kinds of pieces of information. So  we could have here something like input and then  

  • We could ask here where you want to go. And  then what will end up happening is that the  

  • string that I'm going to pass in here as the  input is automatically going to be passed in  

  • inside this board dot select place to go methodAnd so it will be useful because now we will not  

  • have to change the code, every time that we want  to look up for a different location to prepare our  

  • vacation. So that's why I am going to do this  approach for check in date and checkout date and  

  • as well as adults count. And I'm going to leave  the currency as it is because it will probably be  

  • nicer to see the prices in United States dollarsyou can change it to whatever currency you want.  

  • But let's leave it just hard coded in here and  only change those. Alright, so I'm going to  

  • ask you besides hard coding in the check in  date, something like this. So I'm going to ask,  

  • what is the check in date? And now I will also  copy this statement and paste this in here. And  

  • then I will ask, what is the check out date? Andwill also ask you something like, how many people  

  • something like that, and then we are ready to goOkay, so now I will allow myself to run again,  

  • Python, run dot p y. And let's see what  will happen. So at first we see our program,  

  • and you can see that nothing will happen only  after we select our currency, because our program  

  • expects for an input from us. So it is going to be  now answering each step at a time. So it is going  

  • to be something like so let's say that we want to  go to Los Angeles now. So I'm going to put that  

  • and you can see that now it asks for me, what is  the check in there. So now if I open the browser,  

  • then we can see that it has already selected  the Los Angeles. So that is perfect,  

  • it means that it really works. And we should see  the exact same result, when we provide in check  

  • in and checkout date. Now we should be careful in  here because we want to give the correct format.  

  • So it will be year dash Mont and only the day  of demand right after that. So I'm going to  

  • use that. And then I will say check out date  something like the following. So let's say  

  • we want to go for five days. And then you'll see  that it asked for how many people. And meanwhile,  

  • I can check if this works. And you can see that  it fills in the correct information. And now I  

  • will provide in four people, for example. And you  can see that we receive an error. And I believe  

  • that is because we did not convert the count  of adults to an interview. Now by default. So  

  • this is a great mistake that we had in here. And  let's fix this quickly. So let's bring our program  

  • and explain what is going on here. So you can see  that in select adults, if we use here Ctrl V, you  

  • can see that here we are operating some actions  that are requiring from this count to being an  

  • interview. And you can see that exactly from this  line where we use minus one. So it complains about  

  • how a string could not use subtracting in hereSo that's why we should go ahead and automatically  

  • convert this to an integer. So I'm going to use  an int built in function here and allow myself to  

  • execute this program one more time. So let's clean  the screen. And again, use Python run dot p y.  

  • And let's minimize this up and then providing  the information. So it's going to be Los Angeles.  

  • And then it is going to be again this date and  then that checkout date and then we can say  

  • for and you can see that now we don't receive any  arrows. So I believe in few seconds, we should see  

  • the results in a nice table. And you can see  that exactly, this is what is going on here.  

  • And we actually have an appeal with 10 scoresSo that is great news about this Grand Park la  

  • told the the states and Okay, so you can  really see that the results are perfect  

  • and everything functions well. And the fact  that we use input code really helps us to  

  • execute this program. Every time that we want  to test out the results for different locations  

  • and as well as for different dates. So we could  select the next month or the next three weeks or  

  • even tomorrow. So we will have this dynamic  option to just providing the information  

  • once we run the program and we don't really  need to change the code every time. Alright,  

  • so I think this will be it about designing this  Selenium project. Now of course, there is always  

  • room for improvement and also adding some features  here and there. But I think that I have covered  

  • everything that I wanted to accomplish from  the very beginning. And that is the fact that  

  • we display the results in a console in a nice  way depending on the information that has been  

  • provided in where we want to go and the check  in checkout date and how many people so if you  

  • enjoyed this series, then be sure to hit the like  button and drop a comment. So we can really spread  

  • this video to more people on YouTube. Andwill see you in my future videos. So again,  

  • hit the thumbs up button and as well as  subscribe to my channel and see you around.

Selenium is a portable framework for testing  web applications. Jim from JimShapedCoding  

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

A2 初級 美國腔

Selenium 初學(Selenium Course for Beginners - Web Scraping Bots, Browser Automation, Testing (Tutorial))

  • 8 1
    Learning language 發佈於 2022 年 10 月 23 日
影片單字