Selenium is a powerful web automation tool that can be used for browser automation, to test front-end code, and create web scraping bots.

We just released a full course on the freeCodeCamp.org website that will teach you Selenium while you build a web scraping bot.

Jim from JimShapedCoding developed this course. Jim He has created many popular courses both on our channel and his own channel.

Here are the sections in this course:

  • Getting Started with the basics
  • Explicit vs Implicit
  • Sending Keys & CSS Selector
  • Structure a Bot Project
  • Deal Searching
  • Booking Filtrations
  • Execution from a CLI
  • Deal Reporting

Watch the full course below or on the freeCodeCamp.org YouTube channel (3-hour watch).

Transcript

(autogenerated)

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 reason.

Because 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 notification.

So 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 installed.

And 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 scillonian.

So 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.

Alright, so 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 library.

Alright, 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 why I will 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 tat.

So 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 see, here 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, version.

And 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 to a location that you want to have your chromedriver.

So for myself, it is going to be under the C drive.

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 it.

So I'm going to do that.

And right after that, let's go ahead and work with this follow now.

So as you can see, the location of that will be C Selenium drivers like that.

And you can again, put 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 the C drive 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 for a prefix 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 architecture.

And 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 that.

Alright, so let's go back to pi charm.

Now before I do that, let me copy the link of the website, because we are going to use it just in a minute.

All right, so now if I open pi charm, and go down, and 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 see, it 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 name.

And 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 cases, loading 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 successfully.

So 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 sometimes.

Or maybe we can have some cases that our server could be very slow.

So that's why waiting a little bit here and there could be a better idea.

So now if we run our program one more time, then 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.

Well, we 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 here, the 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 efficient, rather than typing in the time dot sleep, because not always we'd like to wait 30 seconds, before 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 ID.

And I'm going to put here some element that is not even existing in the page in the left side.

So I can 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 time, then 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 successfully.

And 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 actually a great 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 element, and 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 here, something 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 here.

So 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 element, which was starting down now showing up the text immediately is actually not a great idea, because 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, driver.

And 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 enough.

And 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 use a method 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 line, right 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 errors, because 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 here, whatever 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 directly a number 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 background.

Okay, 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 actually a good 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 if I take a look in here, then you can see that we have some classes in here that are separated by spaces.

Now, 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 that.

All 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 there.

So 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 Selenium.

And 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 keys, import 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 very, very 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 there.

Now 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 expected.

So 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 total A plus 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, so I said 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 example, the type is a and that stands for anchor.

And you can see that within the square brackets in here, it 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 useful.

And 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.

Okay, so 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 that.

And 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 this, let 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 packages.

And 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 driver, and 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 that.

And 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 backslash, say lynnium.

Drivers like that, because this is the location that I have the chrome driver.

Okay, so 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.

Alright, so 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 method.

So 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.com.

Now 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 commonly, because 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 episode.

So I am inside my repository on GitHub that I have published recently, when I launched the series.

And 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 booking, because we want to refer to the file name as well, we only want to import the booking class, which I named 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 happen.

Okay, 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 opened.

And 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 us a lot 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 basically, if 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 now, horizontally, I believe, no, we should do this, excuse me.

I'd like to do that vertically.

Alright, 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 again, this 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 helpful, like 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 here, which 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 that.

And the second line of code that I'd like to add in here is going to be self dot maximize window.

And 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 that, we 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, always.

But 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 expected.

So 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 selenium, then 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 that.

Now 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 that.

So 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 do.

Now 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 if I was 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 record, this 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 data.

And 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 date.

And we can go down and we can say check in element is equal to self dot find element by CSS selector.

And 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 date, which 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 below, we 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 yourself.

So 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 adults.

Now 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 vacation.

Now, 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 value.

And then we can have more control how many times we really need to click on the increasing button.

So 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 one.

And 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 adults.

Alright, so now that we have control in the element that displays the value of adults count, then 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 value, because 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 adults' value 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 perfect, the 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 not.

But basically, if you're not going to refer to the variable that is used in here, then by convention, you 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 anything.

So 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 that.

Alright, 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 page, we 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 filtrations, once 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 design.

So I can go here, and I can say, new file, and I can name this booking underscore filtration.

And 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 parameter.

And 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 object.

So we need to somehow pass the self object to the instantiated instance of this booking filtration.

So 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 parameter.

So before we continue working on this one, let me quickly fix the arrows in the other file.

So I will 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 line.

And 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 be a great 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 class, because 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 completions.

Now 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 example.

And 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 to a variable 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 structure.

And 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 filtration.

And 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 to a string 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 that, we 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 about.

And 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 values.

And 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 statement.

So 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 filtration, but 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 this, then 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 Windows, then 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 level.

And 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 file, which 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 here, and we could say, accept any exception.

And we could grab the exception inside a variable, something like E, maybe.

And as a starter, we could say something like print, there is a problem 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 block, because 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 with a backslash 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 again, received 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 right, so 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 from a terminal, 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 environment.

Alright, 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 tutorial.

So 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 if I scroll 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 Enter.

Now 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 remember, this 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 this, it 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 Enter, then 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 one.

And 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 results.

Because if you remember, this is returning a list.

And we only like to see the length of this list.

And 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 found.

So 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 project.

So let me delete that.

And actually, there should be one more area that we should remove everything.

And 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, obviously, they 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 element.

And 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 that.

And 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 matter.

And 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 filtrations.

And 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 when I developed 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 here, bot 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 tutorial.

And 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 score, meaning 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 price.

And 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 name, price 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 list.

And 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 data.

Alright, so now that we have done this, then let's open our terminal and test the results.

So 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 out a bit 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 price, and 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 install a library that is called a pretty table.

So it is going to be pip install pretty table like that.

Now 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 library.

So 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 here, something 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 here, three 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 object.

So 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 ready, now 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 seconds, the 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 inputs.

And 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 method.

And 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 dollars, you 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? And I will also ask you something like, how many people something like that, and then we are ready to go.

Okay, 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 here.

So 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 scores.

So 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.

And I will 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.