Selenium-Py.ahk for Ahk v2

Post your working scripts, libraries and tools.
sashaatx
Posts: 333
Joined: 27 May 2021, 08:27
Contact:

Selenium-Py.ahk for Ahk v2

Post by sashaatx » 26 Mar 2023, 08:39

Using the Selenium 4 Python library to pull Chrome WebDriver, pass commands and clicks, and retrieve or supply values.
.exe compiled for python driver here:
https://github.com/samfisherirl/Selenium_Py.ahk (if you enjoy, drop a star for an up and coming dev)


Finding web elements https://www.selenium.dev/documentation/webdriver/elements/finders/
Locating the elements based on the provided locator values. https://www.selenium.dev/documentation/webdriver/elements/interactions/
Interacting with web elementshttps://www.selenium.dev/documentation/webdriver/elements/interactions/
todo: needs to be able to input form data, should be running soon.
A high-level instruction set for manipulating chrome and web controls.

settings := [A_ScriptDir . "\lib\selenium_ahk.exe"]
chrome := Selenium.Commands(["get", "https://slatestarcodex.com/"], settings)

PID := chrome.PID ; manipulate window
chrome.start()
; initiate driver, if first time, downloads appropriate webdriver automatically & restarts
; navigates to initial url request


chrome.newCall(["click", "PARTIAL_LINK_TEXT", "CODEX"])
chrome.newCall(["get_link", "ID", "a"])
chrome.newCall(["click", "CLASS_NAME", "data"])

adding this week:
chrome.newCall(["store_element", "ID", "a"])
chrome.newCall(["send_input", "these keys are being sent to input box"])


MsgBox(chrome.read())
;upon retrieving value like "all_links" or "get_link", log is stored locally and object has retrievable value

chrome.newCall(["all_links"])
MsgBox(chrome.read())
chrome.quit()


Code for AHK:

Code: Select all

settings := [A_ScriptDir . "\lib\selenium_ahk.exe"]
chrome := Selenium.Commands(["get", "https://slatestarcodex.com/"], settings)

chrome.start() ; true if no value but successful
PID := chrome.PID ; manipulate window

chrome.newCall(["click", "PARTIAL_LINK_TEXT", "CODEX"])
chrome.newCall(["get_link", "PARTIAL_LINK_TEXT", "ARCHIVE"])

MsgBox(chrome.read())

chrome.newCall(["all_links"])
chrome.newCall(["get_link", "CLASS_NAME", "data"])
MsgBox(chrome.read())

; returns link by text name
chrome.quit()
/*
chrome.newCall(["click", "TAG_NAME", "A"])
chrome.newCall(["click", "TAG_NAME", "A"])
find_element(By.ID, "id")
find_element(By.NAME, "name")
find_element(By.XPATH, "xpath")
find_element(By.LINK_TEXT, "link text")
find_element(By.PARTIAL_LINK_TEXT, "partial link text")
find_element(By.TAG_NAME, "tag name")
find_element(By.CLASS_NAME, "class name")
find_element(By.CSS_SELECTOR, "css selector")
*/
class Selenium
{
    class Commands
    {
        __New(command, settings) {
            key := command.Length
            this.function := command[1]
            this.key := command.Length
            this.PID := ""
            SplitPath(settings[1], , &folder)
            this.folder := folder
            this.temp := folder . "\temp.txt"
            this.log := folder . "\log.txt"
            this.out := folder . "\log_out.txt"
            this.selpath := Settings[1]
            this.storage := ""
            if (key > 1) {
                this.paramX := command[2]
                if (key > 2) {
                    this.paramY := command[3]
                    if (key > 3) {
                        this.paramZ := command[4]
                    } }
            }
            this.command := command
        }

        start() {
            if !FileExist(this.log) and !FileExist(this.temp) {
                msg := "This may be your first time using, please restart after selenium driver download."
                this.stringify()
                Run(this.selpath, this.folder, , &PID)
                exitapp

            }
            this.clean(this.log)
            this.clean(this.temp)
            this.stringify()
            Run(this.selpath, this.folder, , &PID)
            this.pid := PID
            this.call() 
            sleep(1000)

        }
        call() {
            
            this.clean(this.log)
            this.clean(this.out)
            this.stringify()
            this.loopEngage()
            Selenium.Commands.delLog(this.out)
            
        }
        loopEngage(){
            f := this.function
            if (instr(f,"get_link")) or (instr(f,"get_links")) or (instr(f,"all_links")) {
                this.readLog(this.out, 2)
            }
            else if (instr(f,"quit")) {
                exitapp
            }
            else {
                this.readLog(this.log, 1)
            }
        }
        stringify(){
            stringify := ""
            loop this.command.Length {
                stringify .= "--" . this.command[A_Index]
            }
            Selenium.Commands.delLog(this.temp)
            sleep(5)
            FileAppend(stringify, this.temp)
        }
        quit(){
            this.newCall(["quit"])
            exitapp
        }
        clean(file){
            if FileExist(file) {
                Selenium.Commands.delLog(file)
            }
        }
        newCall(params) {
            Sleep(500)
            this.command := params
            this.key := params.Length
            this.function := params[1]
            if (this.key > 1) {
                this.paramX := params[2]
                if (params.Length > 2) {
                    this.paramY := params[3]
                    if (params.Length > 3) {
                        this.paramZ := params[4]
                    }
                } 
            }
            this.call()
        }
        readLog(file, mode) {
            x := "`n"
            loop 50
            {
                if (A_Index == 50) {
                    msg := this.function . x .
                        file . x .
                        this.PID . x .
                        x . this.storage . x . mode
                    FileAppend(msg, A_ScriptDir "\er.txt")
                    Msgbox("Looping, not receiving updates from selenium`nLogdump:`n" . msg)
                    processclose(this.PID)
                    exitapp
                }
                else if FileExist(file) {
                    try {
                        output := FileRead(file)
                    }
                    catch {
                        Sleep(200)
                        continue
                    }
                    if (mode == 1) {
                        if (instr(output, "False")) {
                            break
                        }
                        else {
                            Sleep(200)
                            continue
                        }
                    }
                    else if (mode == 2) {
                        this.storage := output
                        Selenium.Commands.delLog(file)
                        break
                    }
                    else {
                        if (output == this.paramX) {
                        sleep(200)
                        break
                        }
                        else {
                            sleep(200)
                            continue
                        }
                    }
                }
                else {
                    Sleep(200)
                    continue
                }
            }

        }
        read()
        {
            return this.storage
        }

        static delLog(log) {
            loop 10 {
                try {
                    FileDelete(log)
                }
                catch {
                    sleep(50)
                } 
            }
        }
    }
}

Python script that handles webdriver download and interaction. Go to github to get compiled version:

Code: Select all

from selenium.webdriver import Chrome #, ChromeOptions
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
from pathlib import Path

log = Path.cwd()  / "log.txt"
temp = Path.cwd()  / "temp.txt"
out = Path.cwd() / "log_out.txt"
    #options = ChromeOptions()
    #options.add_experimental_option('excludeSwitches', ['enable-logging'])

class Sel:
    def __init__(self):
        self.links = []
        self.running = True
        self.element = None
        self.lastcommand = ""
        self.counter = 0

    def listener(self):
        while True:
            sleep(1)
            if self.running == False:
                break
            else:
                self.reader()

            
    def reader(self):
        if Path.is_file(temp):
            with open(temp, "r") as f:
                command = f.read() 
            with open(log, "w") as f:
                f.write("True")
            if command == self.lastcommand or command == "":
                return True
            else:
                print(f"\n\nSuccess!\n{command}\n")
                self.lastcommand = command
                self.commander(command)
        else:
            Sel.write_ahk_log("False", log)
        

    @staticmethod
    def write_ahk_log(notes, file):
        with open(file, "w") as f:
            print(notes)
            f.write(notes)
            
    def commander(self, command):
        try: 
            counter = command.count("--") 
            if counter == 2:
                command = command.split("--")
                if command[1] == "quit":
                    self.close_driver()
                else:
                    self.call_function_by_name(command[1], command[2])
            elif counter == 3:
                command = command.split("--")
                self.call_function_by_name(command[1], command[2], command[3])
            elif counter == 1:
                command = command.split("--")
                if command[1] == "quit":
                    self.close_driver()
                else:
                    self.call_function_by_name(command[1])
        except Exception as e:
            print(str(e))
            Sel.write_ahk_log(str(e), out)
            self.close_driver()
            
            
    def call_function_by_name(self, function_name, *arg):
        print(f"\n\ncall\n{function_name}\n{arg}\n")
        func = getattr(self, function_name)
        if not arg:
            func()
        elif len(arg) == 1:
            print(str(arg))
            func(arg[0])
        elif len(arg) == 2:
            print(str(arg))
            func(arg[0], arg[1])
        Sel.write_ahk_log("False", log) 

    def get(self, url):
        """Navigates to the specified URL"""
        driver.get(url)
        return True

    
    def find_element(self, locator, value):
        return driver.find_element(locator, value)

    def find_elements(self, locator, value):
        return driver.find_elements(locator, value)

    def by(self, locator, value):
        com = getattr(By, locator)
        self.element = self.find_element(com, value)
    
    def click(self, locator, value):
        com = getattr(By, locator)
        self.find_element(com, value).click()
        #print("\n\n\nclicking")
        #com = getattr(By, locator)
        #link = self.find_element(com, value).get_attribute("href")
        #link.click()
    
    def get_link(self, locator, value):
        com = getattr(By, locator)
        link = self.find_element(com, value)
        out_data = link.get_attribute("href")
        if "Message: no such element" in out_data:
            self.err(self, out_data)
        else:
            self.write_listor_string(out_data)
        return out_data
        
    def all_links(self):
        links = self.find_elements(By.TAG_NAME, "a")
        urls = [link.get_attribute("href") for link in links]
        text = [link.text for link in links]
        list = {f"{k}: {v}" for k, v in zip(urls, text)}
        self.write_listor_string(list)
    # def all_links(self):
    #     links = driver.find_elements(By.TAG_NAME, "a")
    #     urls = [link.get_attribute("href") for link in links]
    #     text = [link.text for link in links]
    #     dic = {k: v for k, v in zip(urls, text)}
    #     self.write_listor_string(str(dic))
        
    def wait_until_element_visible(self, locator, value, timeout=1):
        WebDriverWait(driver, timeout).until(EC.visibility_of_element_located((locator, value)))


    def pass_return_to_ahk(val):
        pass

    @staticmethod
    def delete_er():
        with open("er.txt", "w") as f:
            f.write("")
    
    def err(self, e):
        print(str(e))
        with open("er.txt", "a", errors="ignore") as f:
            f.write(str(e))
        with open("er.txt", "a", errors="ignore") as f:
            f.write(str(e))
        self.close_driver()
        

    def write_listor_string(self, *args):
        with open(out, "w", errors="replace") as f:
            if args:
                [f.write(f"{i}\n") for i in args]
                    
        Sel.write_ahk_log("False", log)
    #################################################
        
    def writer(self, dic, filename):
        file = Path(__file__).parent / "log_out.txt"
        with open(f"{file}", "w", errors="ignore") as f:
            for k, v in dic.items():
                f.write(f",{k},{v},\n")
        Sel.write_ahk_log("False", log)


    def close_driver(self):
        """Closes the webdriver instance"""
        driver.quit()
        self.running = False
        return

if __name__ == "__main__":
    log = Path.cwd()  / "log.txt"
    temp = Path.cwd()  / "temp.txt"
    out = Path.cwd() / "log_out.txt"
    print(log)
    print(out)
    driver = Chrome(service=ChromeService(ChromeDriverManager().install()))
    Sel.delete_er()
    S = Sel()
    S.get("https://www.slatestarcodex.com/")
    S.all_links()
    S.listener()
    #drivers = Sel() 
    #drivers.get("https://www.github.com/")
    #drivers.all_links()
Last edited by sashaatx on 29 Mar 2023, 14:45, edited 15 times in total.
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :

sashaatx
Posts: 333
Joined: 27 May 2021, 08:27
Contact:

Re: Selenium-Py.ahk for Ahk v2

Post by sashaatx » 29 Mar 2023, 10:56

completely re-wrote ahk script, python code, I'll be working to speed it up between the log and the python script.
From functional rough draft to tighter execution of base features, with added todos for future.
Handles exceptions, handles all links and dumping logs/errors in fixes.
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :

Post Reply

Return to “Scripts and Functions (v2)”