I increased that Timeout to 1000 and decreased the Sleep time back to 1000...tried it with Eval3...does not work...13 of the 19 images are links, not images. But thanks for the idea...worth a try. Regards, Joeteadrinker wrote:You can try to increase also this value: const timeout = setTimeout('scrollPage()', 100);.
[Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No IE!
- JoeWinograd
- Posts: 2213
- Joined: 10 Feb 2014, 20:00
- Location: U.S. Central Time Zone
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
-
- Posts: 139
- Joined: 26 Jan 2016, 16:05
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
Slightly improved code to a previous post i made about finding multiple instance of Chrome Debug sessions in a multi user environment like a Terminal Server:
UPDATED - at G33kDudes suggestion - hardcoding in domain admin credentials is obviously unwise, so updated the PS to use a secure key. There are simple instructions online how to do this. Thanks G33k
Main difference is that the PS is now just a string, rather than an external file being loaded. I had a need for this due to a terminal server application where the regular "FindInstances()" does not return all open chrome processes for all users due to privileges. So, this is just a workaround.
The username/password must be someone with proper privileges so that the PS can execute and read all the chrome processes correctly.
If anyone has a better way to handle the temporary .txt file creation - id love to hear it!
Thanks,
-Tre4
UPDATED - at G33kDudes suggestion - hardcoding in domain admin credentials is obviously unwise, so updated the PS to use a secure key. There are simple instructions online how to do this. Thanks G33k
Code: Select all
FindInstances()
{
PS =
(
$password = Get-Content F:\UserFolders\md\MCSTool\Chrome\password.txt | ConvertTo-SecureString -Key (Get-Content F:\UserFolders\md\MCSTool\Chrome\aes.key)
$Cred = New-Object System.Management.Automation.PsCredential(\"vgesinc\md\", $password)
start-process -WindowStyle hidden -FilePath "$env:comspec" -ArgumentList \"/c wmic Process where ``\"name = 'chrome.exe'``\" get commandline, processid > $env:TEMP\test.txt\" -Credential $Cred
)
runwait % "PowerShell.exe -Command & {" PS "}", % A_Temp,"Hide"
FileGetSize, oSize, %A_Temp%\test.txt
oSize = -1
While (fSize <> oSize) {
if(fSize > 0)
oSize := fSize
sleep, 100
FileGetSize, fSize, %A_Temp%\test.txt
}
FileRead, rfile, %A_Temp%\test.txt
FileDelete, %A_Temp%\test.txt
Needle := "--remote-debugging-port=(\d+)"
Out := {}, i = 0
Loop, parse, rFile, `n,`r
{
if RegExMatch(A_Loopfield, Needle, Match){
Out[Match1,i++] := {"cmd":Item.CommandLine,"pid":Item.ProcessId,"port":Match1}
}
}
return, Out
}
The username/password must be someone with proper privileges so that the PS can execute and read all the chrome processes correctly.
If anyone has a better way to handle the temporary .txt file creation - id love to hear it!
Thanks,
-Tre4
-
- Posts: 139
- Joined: 26 Jan 2016, 16:05
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
Yet another Iteration...
Realized i can just let PS handle the file creation...so no need to loop over the file checking for filesize. Also - like in my edited post above, no longer have the password credential hardcoded in plain text.
Thanks,
Tre4
Realized i can just let PS handle the file creation...so no need to loop over the file checking for filesize. Also - like in my edited post above, no longer have the password credential hardcoded in plain text.
Code: Select all
FindInstances()
{
PS =
(
$password = Get-Content F:\UserFolders\md\MCSTool\Chrome\password.txt | ConvertTo-SecureString -Key (Get-Content F:\UserFolders\md\MCSTool\Chrome\aes.key)
$Cred = New-Object System.Management.Automation.PsCredential(\"vgesinc\md\", $password)
start-process -WindowStyle hidden -Wait -FilePath "$env:comspec" -ArgumentList \"/c wmic Process where ``\"name = 'chrome.exe'``\" get commandline, processid\" -RedirectStandardOutput $env:TEMP\test.txt -Credential $Cred
)
runwait, % "PowerShell.exe -Command & {" PS "}", % A_Temp , "Hide"
FileRead, rfile, %A_Temp%\test.txt
FileDelete, %A_Temp%\test.txt
Needle := "--remote-debugging-port=(\d+)"
Out := {}, i = 0
Loop, parse, rFile, `n,`r
{
if RegExMatch(A_Loopfield, Needle, Match){
Out[Match1,i++] := {"cmd":Item.CommandLine,"pid":Item.ProcessId,"port":Match1}
}
}
return, Out
}
Tre4
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
Can I do this in Firefox?
I posted a question in the help forum asking about how to enter text in a text box in a browser running in the background. A response from @gregster cited Chrome.ahk as a possible solution. I'm working in Firefox, and I'm not excited about installing Chrome, but I can do that if necessary.
Gregster posted sample code that used Chrome.ahk to execute a javascript command that sets the value of a document element in a webpage. Can I use AHK to do that in Firefox?
There's some detail about what I've been trying to figure this out in my reply to gregster in the other post.
I posted a question in the help forum asking about how to enter text in a text box in a browser running in the background. A response from @gregster cited Chrome.ahk as a possible solution. I'm working in Firefox, and I'm not excited about installing Chrome, but I can do that if necessary.
Gregster posted sample code that used Chrome.ahk to execute a javascript command that sets the value of a document element in a webpage. Can I use AHK to do that in Firefox?
There's some detail about what I've been trying to figure this out in my reply to gregster in the other post.
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
As Geekdude explained in this post, Chrome.ahk can be generalized to automate Firefox (but currently only the "nightly builds", which is not the main release channel).
Btw I don't think that Firefox is Chromium-based (in contrast to the new Edge browser or Opera), but it seems that its developers started to support a subset of the Chrome debug protocol, probably to help standardize browser debugging across different platforms. That said, not even all Chromium-based browsers support that protocol (Brave's and Vivaldi's developers don't seem to care for it - at least, the last time I looked).
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
Thanks for that link, gregster. The problem there is that instead of installing Chrome, I have to install a different implementation of Firefox, and possibly one that is not fully tested, so maybe not a great idea.
Something I would like to know is, if all I want to do is to change the value of a document element on the webpage, do I need all the capability of Chrome.ahk in order to do that, or is there a way to do it directly in AHK?
Something I would like to know is, if all I want to do is to change the value of a document element on the webpage, do I need all the capability of Chrome.ahk in order to do that, or is there a way to do it directly in AHK?
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
Use WebDriver api:
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=69390
Or You can run JS from address bar.
Or You can set value with IAccessible or IUIAutomation help.
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=69390
Or You can run JS from address bar.
Or You can set value with IAccessible or IUIAutomation help.
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
I don't want to hijack this topic away from its intended focus on Chrome.ahk, so I've responded further to @gregster and @malcev back in the other topic on how to send text to an inactive window. I'd really appreciate further input there if people know how to do that. In the end, that post concludes that I'm going to have to start using Chrome instead of Firefox and learn Chrome.ahk in order to do it, but I'm really surprised that there doesn't seem to be a simpler way, but I'd love to find out that I'm wrong and that there is a simpler way.
Documentation of Chrome.ahk
To help myself understand what I'm working with, I compiled the following notes, adapted from the documentation of the code in the Chrome.ahk script. Perhaps this may be helpful to other newbies. If @GeekDude or other experts here let me know of any errors or suggested improvements, I'll edit accordingly.
If this information exists somewhere else besides in the code, let me know where, and I'll delete this post.
-------------------------------------------------------------------------------------
Documentation of Chrome.ahk, version 1.2
Compiled from documentation in the script file, "Chrome.ahk".
The script consists of one definition:
If this information exists somewhere else besides in the code, let me know where, and I'll delete this post.
-------------------------------------------------------------------------------------
Documentation of Chrome.ahk, version 1.2
Compiled from documentation in the script file, "Chrome.ahk".
The script consists of one definition:
- Class: Chrome
The class consists of one variable assignment (DebugPort) and definitions of 13 methods and one class.- Methods:
- __New() -- Called by constructor. Parameters:
- ProfilePath: Path to the user profile directory to use. Will use the standard if left blank.
- URLs: The page or array of pages for Chrome to load when it opens
- Flags: Additional flags for chrome when launching
- ChromePath: Path to chrome.exe, will detect from start menu when left blank
- DebugPort: What port should Chrome's remote debugging server run on
- CliEscape() -- Escape a string in a manner suitable for command line parameters.
- FindInstances() -- Find instances of Chrome in debug mode and the ports they're running on. If no instances are found, return a false value. If one or more instances are found, return an associative array where the keys are the ports, and the values are the full command line texts used to start the processes.
Example usage:- Open Chrome on a different port if an instance of Chrome is already open on the port you wanted to use.
Code: Select all
; If the wanted port is taken, use the largest taken port plus one DebugPort := 9222 if (Chromes := Chrome.FindInstances()).HasKey(DebugPort) DebugPort := Chromes.MaxIndex() + 1 ChromeInst := new Chrome(ProfilePath,,,, DebugPort)
- Scan for running instances and attach to one instead of starting a new instance.
Code: Select all
if (Chromes := Chrome.FindInstances()) ChromeInst := {"base": Chrome, "DebugPort": Chromes.MinIndex()} else ChromeInst := new Chrome(ProfilePath)
- Open Chrome on a different port if an instance of Chrome is already open on the port you wanted to use.
- GetPageList() -- Query Chrome for a list of pages that expose a debug interface. In addition to standard tabs, these include pages such as extension configuration pages.
- GetPageBy() -- Return a connection to the debug interface of a page that matches the provided criteria. When multiple pages match the criteria, they appear ordered by how recently the pages were opened.
- GetPageByURL() (Shorthand for GetPageBy("url", Value, "startswith") )
- GetPageByTitle() (Shorthand for GetPageBy("title", Value, "startswith") )
- GetPage() (Shorthand for GetPageBy("type", Type, "exact") )
The default type to search for is "page", which is the visible area of a normal Chrome tab. - Kill() -- End Chrome by terminating the process.
- Jxon_Load() (No documentation. Called by .GetPageList() and .Event() .)
- Jxon_Dump() (No documentation. Called by .Call() and .Evaluate() .)
- Jxon_True() (No documentation. Called by .Evaluate() .)
- Jxon_False() (No documentation. Called by .Evaluate() .)
- __New() -- Called by constructor. Parameters:
- Class: Page -- Connects to the debug interface of a page given its WebSocket URL.
The class consists of definitions of six methods and one class.- Methods:
- __New() -- Called by constructor. Parameters:
- wsurl: The desired page's WebSocket URL
- fnCallback: A function to be called whenever message is received
- Call() -- Call the specified endpoint and provide it with the given parameters.
Endpoint: Wikipedia, Stack Overflow, Chrome DevTools
Example domain.methods: Page.navigate, Page.printToPDF, Browser.close, Schema.getDomains - Evaluate() -- Run some JavaScript on the page. For example:
- PageInst.Evaluate("alert(""I can't believe it's not IE!"");")
- PageInst.Evaluate("document.getElementsByTagName('button')[0].click();")
- WaitForLoad() -- Wait for the page's readyState to match the DesiredState.
- Disconnect() -- Disconnect from the page's debug interface, allowing the instance to be garbage collected. This method should always be called when you are finished with a page or else your script will leak memory.
- Event() (Internal function triggered when the script receives a message on the WebSocket connected to the page.)
- __New() -- Called by constructor. Parameters:
- Class: WebSocket (No documentation.)
The class consists of definitions of five methods.- Methods:
- __New() -- Called by constructor. Parameter: WS_URL
- _Event() -- Called by the JS in response to WS events.
- Send() -- Send data through the WebSocket.
- Close() -- Close the WebSocket connection.
- Disconnect() -- Close and delete the WebSocket, removing references so the class can be garbage collected.
- Methods:
- Methods:
- Methods:
How to run an onClick function?
Okay, I've got Chrome and Chrome.ahk up and working. Looking good.
So now, when I need to put text in a text box on a webpage, instead of simulating a click in the box and keystrokes, I just set the value of the text box with JavaScript. Very cool.
But now, what about this: A page I'm working with has two buttons, one to search by city and one by zip code. I need to set the value of either the city or zip code to search for, and then do the equivalent of clicking the corresponding button. Problem is, the buttons don't have IDs, so I can't use "getElementById" to determine which one to execute .click() on. Is there a way to call the appropriate onclick function directly without simulating a button click? Or another way to do this? I suppose this may be more a question on Javascript than Chrome.ahk, but does someone here know how to do this?
Here is the code from the webpage:
So now, when I need to put text in a text box on a webpage, instead of simulating a click in the box and keystrokes, I just set the value of the text box with JavaScript. Very cool.
But now, what about this: A page I'm working with has two buttons, one to search by city and one by zip code. I need to set the value of either the city or zip code to search for, and then do the equivalent of clicking the corresponding button. Problem is, the buttons don't have IDs, so I can't use "getElementById" to determine which one to execute .click() on. Is there a way to call the appropriate onclick function directly without simulating a button click? Or another way to do this? I suppose this may be more a question on Javascript than Chrome.ahk, but does someone here know how to do this?
Here is the code from the webpage:
Code: Select all
<h3>City Search</h3>
<div style="padding-left: 10px;">
City:<br />
<input type="text" class="notranslate" id="OCity" />
<input type="button" value="Search" onclick="doCSearch()" />
</div>
<h3>ZIP Code Search</h3>
<div style="padding-left: 10px;">
ZIP Code*:<br />
<input type="text" id="OZipCode" maxlength="5" style="width: 65px" />
<input type="button" value="Search" onclick="doZSearch()" />
</div>
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
This may be a good opportunity to use the relatively uncommon CSS selector + which is used for selecting adjacent elements. document.querySelector("#OCity+input[type=button]").click() may work for you here.
Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!
That worked perfectly. Thanks @GeekDude.
Warning on URLString
When I run a script that #includes Chrome.ahk with the #warn directive, I get the following warning:
I have addressed this issue by inserting, two lines above that, the line:
Right?
Code: Select all
Warning in #include file "...\Chrome.ahk":
This variable has not been assigned a value.
Specifically: URLString (a local variable)
Line#
...
---> 093: URLString .= " " this.CliEscape(URL)
...
Code: Select all
URLString := ""
Use page.evaluate() to get element value?
I learned above that I can use Javascript in page.evaluate() to set the value of a webpage element. Now I'm trying to do the reverse of that. In Chrome.ahk, the code for page.evaluate() says that it returns "response.result", which it appears is supposed to be whatever the Javascript command returns. So with the following code:
I expect the message box to say:
but it says:
However, if I open the DevTools console and enter:
it replies with:
So the Javascript command is doing what I expect, but page.evaluate() is not returning its result. What am I doing wrong?
Code: Select all
#NoEnv
#include Chrome.ahk
oChrome := new Chrome()
SetTitleMatchMode 2
WinWait, - Google Chrome
if not(oWebpage := oChrome.GetPage())
{MsgBox, Could not retrieve page!
oChrome.kill()
}
else
oWebpage.WaitForLoad()
oWebpage.call("Page.navigate", {"url": "https://82187.csb.app/"})
oWebpage.WaitForLoad()
oWebpage.evaluate("document.getElementById('OZipCode').value = 11111")
sZipSet := oWebpage.evaluate("document.getElementById('OZipCode').value")
MsgBox % "Zip set to: """ . sZipSet . """"
return
Code: Select all
Zip set to: "11111"
Code: Select all
Zip set to: ""
Code: Select all
document.getElementById('OZipCode').value
Code: Select all
"11111"
Re: Use page.evaluate() to get element value?
Oops. In the debugger, I looked at sZipSet, and it told me it was an object. So I suffixed ".value" on page.evaluate(), so that line 15 of the above code became:
Code: Select all
sZipSet := oWebpage.evaluate("document.getElementById('OZipCode').value").value
How to do a find text in a Chrome window in the background?
How do I open the find box in a Chrome window in the background and do a search for text?
Manually, I can do this with Ctrl-F. If the window is active, I can send that with the "send" command. But that doesn't work, and neither does ControlSend, when the window is in the background.
The find box is not a document element, so I can't access it that way. Is it a window element?
I figure there's a way to get Chrome.ahk to do this, but I haven't been able to find it.
Manually, I can do this with Ctrl-F. If the window is active, I can send that with the "send" command. But that doesn't work, and neither does ControlSend, when the window is in the background.
The find box is not a document element, so I can't access it that way. Is it a window element?
I figure there's a way to get Chrome.ahk to do this, but I haven't been able to find it.
Re: How to do a find text in a Chrome window in the background?
From experimenting, I have a partial answer to my previous question. I have not found out how to bring up the "Find" box, but I have succeeded in (a) simulating the behavior of the "Find" box, and (b) bringing up a dialog box that is in the browser window and is not part of the webpage (i.e., not part of "window.document"). These two things are demonstrated in the following script:
I'd still be interested in knowing how to bring up the "Find" box if someone knows.
In DevTools, the tab "Elements" maps out the entire structure of the webpage and all its elements in the form of the HTML code in a hierarchical structure. This is the "document", which is an element of "window". It would be interesting to know if there's a way to get an analogous map of "window", with all of its elements, of which "document" would be one element among many others. I've been searching and haven't found anything like that.
Code: Select all
#NoEnv
#include Chrome.ahk
oChrome := new Chrome()
SetTitleMatchMode, 2
WinWait, - Google Chrome
if not(oWebpage := oChrome.GetPage())
{MsgBox, Could not retrieve page!
oChrome.kill()
}
else
oWebpage.WaitForLoad()
oWebpage.call("Page.navigate", {"url": "https://www.google.com/search?q=hello"})
oWebpage.WaitForLoad()
oWebpage.evaluate("window.window.find('hello')")
MsgBox First execution of "window.window.find('hello')" has no observed effect.
oWebpage.evaluate("window.window.find('hello')")
MsgBox Executing it again has highlighted the first occurrence of "hello" on the webpage.
oWebpage.evaluate("window.window.find('hello')")
MsgBox Executing it again has highlighted the next occurrence. So this is simulating the behavior of the browser's "Find" box.
MsgBox Next, I'll demonstrate popping up a dialog box in the browser window, not in the webpage.
sResponse := oWebpage.evaluate("window.window.prompt('Type something and click Ok.')").value
MsgBox % "You typed: """ . sResponse . """."
return
In DevTools, the tab "Elements" maps out the entire structure of the webpage and all its elements in the form of the HTML code in a hierarchical structure. This is the "document", which is an element of "window". It would be interesting to know if there's a way to get an analogous map of "window", with all of its elements, of which "document" would be one element among many others. I've been searching and haven't found anything like that.
Warning on %value%
I'm getting another warning on Chrome.ahk. The previous one was happening all the time, but this one just started, so I don't know if there's something in my code that has caused it to occur, but the code in Chrome.ahk does seem to be problematic. The warning is:
Those lines and their adjacent comments are:
This code is in the method Jxon_Load(), and the variable "value" does not occur anywhere else in that method. There is a parameter "Value" in the methods GetPageBy*(), but those parameters don't seem to be accessible in this method. So this does seem to be an error. I tried changing "%value%" to "null" and to "%null%", but those still gave the same warning. I then changed "%value%" to " "" " (i.e., the empty string). After that, the warning ceased, but I worry about whether this is the right correction or might cause some other problem later. Could someone please say if what I've done is right?
Code: Select all
<stderr>:
Warning in #include file "Chrome.ahk":
This variable has not been assigned a value.
Specifically: value (a local variable)
Line#
...
538: if (val == "true" || val == "false")
---> 539: val := %value% + 0
...
Code: Select all
; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo,
; SOMETIMES return strings due to certain optimizations. Since it
; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a
else if (val == "true" || val == "false")
val := %value% + 0
; AHK_H has built-in null, can't do 'val := %value%' where value == "null"
; as it would raise an exception in AHK_H(overriding built-in var)
Re: Warning on %value%
Afaik, that's a known bug/typo in Coco's Jxon_Load() function, which is used here - and should read val := %val% + 0 (or an equivalent).Newer2AHK wrote: ↑01 Aug 2020, 23:12This code is in the method Jxon_Load(), and the variable "value" does not occur anywhere else in that method. There is a parameter "Value" in the methods GetPageBy*(), but those parameters don't seem to be accessible in this method. So this does seem to be an error. I tried changing "%value%" to "null" and to "%null%", but those still gave the same warning. I then changed "%value%" to " "" " (i.e., the empty string). After that, the warning ceased, but I worry about whether this is the right correction or might cause some other problem later. Could someone please say if what I've done is right?
Adding 0 here just makes sure that no string, but a number, is used for the boolean variable.
Return to “Scripts and Functions (v1)”
Who is online
Users browsing this forum: gwarble and 88 guests