[Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

Post your working scripts, libraries and tools
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

05 Jan 2020, 11:49

After fruther, in depth review, my previous post was wrong. I discovered that I was creating several instances of the pageInst, each with their own keep alive timers. After creating a couple of them, chromeInst would die.
Within EACH function that you want to Call/Evaluate against the page, you MUST reconnect to the page (NO GLOBALS!!!!), then, you must DISCONNECT from the page to prevent death.

Also, in chrome.ahk I found it helps sometimes to change "awaitPromise":Chrome.Jxon_false() to "awaitPromise":Chrome.Jxon_True()

Code: Select all

Evaluate(JS)
		{
			response := this.Call("Runtime.evaluate",
			( LTrim Join
			{
				"expression": JS,
				"objectGroup": "console",
				"includeCommandLineAPI": Chrome.Jxon_True(),
				"silent": Chrome.Jxon_False(),
				"returnByValue": Chrome.Jxon_False(),
				"userGesture": Chrome.Jxon_True(),
				"awaitPromise": Chrome.Jxon_True() ;Change this to Chrome.Jxon_True() instead of Chrome.Jxon_False()
			}
			))
			
			if (response.exceptionDetails)
				throw Exception(response.result.description,, Chrome.Jxon_Dump(response.exceptionDetails))
			
			
			return response.result
		}
AHK SCRIPT

Code: Select all

#Persistent
#include %A_ScriptDir%\Lib\Chrome.ahk

FileCreateDir, ChromeProfile 
ChromeInst := new Chrome("ChromeProfile", "http://localhost/test.html") ;create the class
sleep, 3000 ;Since we're not running headless, give the UI a chance to draw

pageInst:= ChromeInst.GetPageByURL("http://localhost/test.html",,,"event_callBack") ;Don't Disconnect this one!
pageInst.WaitForLoad()
pageInst.Call("Console.enable") 

ESC::
ExitApp
return ;END AUTOEXECUTE

event_callBack(Event)
{	
	if (Event.Method == "Console.messageAdded"  && InStr(Event.params.message.text,"AHK:") >=1) 
	{			
		Text := Event.params.message.text 		
		Split := StrSplit(Text, ",","AHK:") 			
		fnName := Split[1]  
		%fnName%() ;Allows you to call a func dynamically from JS like this console.log("AHK:myFirstFunction").
 	} else if(Event.Method == "Inspector.detached") { 
		;You can do stuff here when the user closes the browser.
	}
}

getState() {
	pageInst:=reconnectToPage()
	readyState:= pageInst.Evaluate("document.readyState").value
	pageInst.Disconnect() ;You MUST disconnect after each reconnectToPage(). Otherwise you will end up creating several class instances. Too many instances will cause death :https://www.autohotkey.com/boards/viewtopic.php?f=6&t=42890&hilit=chrome.ahk&start=100 (look at bmilcs post)
	trayTip,document.readyState, % "is " . readyState
	;reloadPage()
}
reloadPage() {
	;an example of how to handle a call as opposed to an evaluate
	pageInst:=reconnectToPage
	pageInst.Call("Page.reload")
	pageInst.WaitForLoad()
	pageInst.Disconnect()
}
reconnectToPage() {	
	pageInst:=Chrome.getPage(,,,,"event_callBack") ;call our Chrome class and get a reference back from our first page. You could also use getPageByURL. Make sure to pass the callback func again!	
	return pageInst
}

HTML

Code: Select all

	<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button onclick="console.log('AHK:getState')" id="TEST">Get document.readyState</button>
<button onclick="console.log('AHK:reloadPage')" id="TEST">Reload the Page</button>
</body>
</html>
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

06 Jan 2020, 11:11

I created a mixed JS/AHK script that you can use to confirm from the browser that you're still connected to autohotkey, and inform your users if not (so they don't get confused when things stop working.
Let me know if ya'll have suggestions on how to make this better!

JAVASCRIPT/JQuery

Code: Select all

var interval = 15000; //how often in MS to run this.
$(document).ready(function() //as soon as the page is loaded run this.
{    
    var now = Date.now(); //get current miliseconds since jan 1970
    window.localStorage.setItem('LastKeepAlive',now); //store it in localstorage so it persists across reloads
    setInterval(logToSystem, interval);
});
 
function browserCheckForConnectionToAHK() {
    var lastCheck = window.localStorage.getItem('LastKeepAlive'); //bring in the var from localStorage
    var now = Date.now(); //get time in MS
if(now - lastCheck > (interval*2)) { //if the last time we updated was more than 2x the interval, we assume we're disconnected
        alert('Disconnected ' + ((now-lastCheck)/1000) + ' seconds ago from page. Please reload the app');
    } else {
//might want to log this to the system some how so we can identify what they were doing to get disconnected.
    }
    AHK('confirmBrowserConnection'); //Now we'll reach out to AHK and try to confirm we're still connected
}
 
function ahkStillConnected() { //called by AHK
    var now = Date.now(); //get current time
    window.localStorage.setItem('LastKeepAlive',now); //update our localStorage with this new time, so browserCheckForConnectionToAHK has updated time to compare
    var response = "success"; //we need to return a value to AHK to prevent a sleep loop.
    return response
}
AUTOHOTKEY

Code: Select all

/*********************** AHK *********************************
*/
confirmBrowserConnection() {   
    pageInst:=reconnectToPage() ;connect to the existing instance of chrome page
    response:=pageInst.Evaluate("ahkStillConnected()").value ;eval the func, grab the return value.
    wb.Disconnect() ;disconnect from page - if you don't do this, the script will die.
}
 
reconnectToPage() {
    pageInst:=Chrome.GetPage(,,,,"event_callBack") ;connect to the first page in Chrome index. As long as yuou have a single page app this should work.
    return pageInst
}
bmilcs
Posts: 11
Joined: 27 Feb 2017, 13:54

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

06 Jan 2020, 19:26

For whatever reason, I'm receiving "Access Restricted" errors from several web pages. It's definitively tied to Google Chrome.

How can I convert this .ahk to work with another Chrome-based browser such as Brave? Any help would be immensely appreciated!!!

I did it once before with Chromium I believe, but can't figure out how to do it with Brave.

Thank you so much.
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

06 Jan 2020, 19:41

@bmilcs: Well, some webpages don't like to be constantly scraped or automated - for example, Cloudflare protections with waiting times sometimes kick in, if you do automation that is deemed excessive (the individual limits can vary wildly from website to website). If that's the problem, I doubt that Brave can really solve that problem.

But what are these "Access Restricted" errors? Are these from Cloudflare? Can you post an example picture ? I could imagine that there could be a whole lot of reasons - for example: proxies, VPNs, malware, just a "bad" or shared IP or third-party browser extensions (just recently, someone here on the forum identified a browser plugin as the reason for the constant captchas on Youtube he had to solve)

I am not even sure, if Brave supports the chrome debugging protocol - not all chromium-based browsers implement it. The new Edge and Opera do it - but Vivaldi does not, and has no current plans to do so, at least the last time I looked.
So, you should first find that out.

Edit: So far, I can't find any mention of the protocol on the Brave forums. So, I would strongly doubt that it's currently possible with Brave or that it is even in the works.
Natitoun
Posts: 4
Joined: 09 May 2019, 06:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Jan 2020, 04:30

Hi All !

I am trying to use chrome in headless mode but it seems that the examples given with the library are not working on my computer:

Code: Select all

ChromeInst := new Chrome("ChromeProfile", "https example.com ",  Broken Link for safety "--headless")
If I run the line above (in ExportPDF.ahk), it says "Could not retrieve page!"

Code: Select all

ChromeInst.AddArgument("headless")
If I run this second line, nothing happens.

Does anyone knows how to make it work ? The Chrome.ahk V1.0 says it was not compatible but I understand that then it became compatible. I am wrong ? Is the library still incompatible ?

This library brings very nice opportunities ! Thanks a lot !!
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Jan 2020, 11:30

Hey @Natitoun

Looks to me like your URL is invalid. Also - why arent you using Chrome v1.2? I know it works in that version.

Try:

Code: Select all

ChromeInst := new Chrome("ChromeProfile", "https://example.com", "--headless")
bmilcs
Posts: 11
Joined: 27 Feb 2017, 13:54

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Jan 2020, 12:30

gregster wrote:
06 Jan 2020, 19:41
@bmilcs: Well, some webpages don't like to be constantly scraped or automated - for example, Cloudflare protections with waiting times sometimes kick in, if you do automation that is deemed excessive (the individual limits can vary wildly from website to website). If that's the problem, I doubt that Brave can really solve that problem.

But what are these "Access Restricted" errors? Are these from Cloudflare? Can you post an example picture ? I could imagine that there could be a whole lot of reasons - for example: proxies, VPNs, malware, just a "bad" or shared IP or third-party browser extensions (just recently, someone here on the forum identified a browser plugin as the reason for the constant captchas on Youtube he had to solve)

I am not even sure, if Brave supports the chrome debugging protocol - not all chromium-based browsers implement it. The new Edge and Opera do it - but Vivaldi does not, and has no current plans to do so, at least the last time I looked.
So, you should first find that out.

Edit: So far, I can't find any mention of the protocol on the Brave forums. So, I would strongly doubt that it's currently possible with Brave or that it is even in the works.
Interesting.

Well, the Access Restricted (always containing a Reference Code") appears like a standard blank/plain text 404 page. The leading site that gives me issues is Macy's, but it spans across many others. All my searches start w/ Google, but I also go to web sites manually and still receive the error.

If I take that same access restricted URL and paste it into Brave, the site loads without a problem. I am using Cloudflare, via DNS Forwarding (EdgeRouter X), but wouldn't the issue persist across browsers if that is the case? Unless Brave is acting as a proxy of some kind, I'm not sure that's the issue.

I've attempted using VPN's to bypass the issue and that completely blocks most shopping sites.

I've tried disabling all of my addons and still struggle.

Malware is highly doubtful, but I'll check just in case.

I've tried having my ISP reset my IP, but Xfinity was no help. I tried unplugging my modem for 10 or so minutes as instructed, which did nothing.

Either way, what steps are necessary to adapt this to another supported browser? Any will do.
Natitoun
Posts: 4
Joined: 09 May 2019, 06:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

10 Jan 2020, 03:57

Tre4shunter wrote:
09 Jan 2020, 11:30
Hey @Natitoun

Looks to me like your URL is invalid. Also - why arent you using Chrome v1.2? I know it works in that version.

Try:

Code: Select all

ChromeInst := new Chrome("ChromeProfile", "https example.com ",  Broken Link for safety "--headless")
Hi @Tre4shunter !

Thanks for answering me. Actually, I am using Chrome.ahk V1.2 and the URL is good in the code (I don't know why it was broken when pasting it here).
I don't understand, even the example files such as "ExportPDF.ahk" given with Chrome.ahk are not working.

Here is a reminder of the code from ExportPDF.ahk :

Code: Select all

#NoEnv
SetBatchLines, -1

#Include ../Chrome.ahk


; --- Create a new headless Chrome instance ---

FileCreateDir, ChromeProfile
ChromeInst := new Chrome("ChromeProfile", "https example.com ",  Broken Link for safety "--headless")

; --- Connect to the page ---

if !(PageInst := ChromeInst.GetPage())
{
	MsgBox, Could not retrieve page!
	ChromeInst.Kill()
}
else
{
	PageInst.WaitForLoad()
	
	
	; --- Export a PDF of the page ---
	
	; Get the PDF in Base64
	Base64PDF := PageInst.Call("Page.printToPDF").data
	
	; Convert to a normal binary PDF
	Size := Base64_Decode(BinaryPDF, Base64PDF)
	
	; Write the binary PDF to a file
	FileName := "Exported_" A_TickCount ".pdf"
	FileOpen(FileName, "w").RawWrite(BinaryPDF, Size)
	
	; Open the file
	Run, %FileName%
	
	
	; --- Close the Chrome instance ---
	
	try
		PageInst.Call("Browser.close") ; Fails when running headless
	catch
		ChromeInst.Kill()
	PageInst.Disconnect()
}

ExitApp
return


Base64_Encode(ByRef Out, ByRef In, InLen)
{
	DllCall("Crypt32.dll\CryptBinaryToString", "Ptr", &In
	, "UInt", InLen, "UInt", 0x40000001, "Ptr", 0, "UInt*", OutLen)
	VarSetCapacity(Out, OutLen * (1+A_IsUnicode))
	DllCall("Crypt32.dll\CryptBinaryToString", "Ptr", &In
	, "UInt", InLen, "UInt", 0x40000001, "Str", Out, "UInt*", OutLen)
	return OutLen
}

Base64_Decode(ByRef Out, ByRef In)
{
	DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &In, "UInt", StrLen(In)
	, "UInt", 0x1, "Ptr", 0, "UInt*", OutLen, "Ptr", 0, "Ptr", 0)
	VarSetCapacity(Out, OutLen)
	DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &In, "UInt", StrLen(In)
	, "UInt", 0x1, "Str", Out, "UInt*", OutLen, "Ptr", 0, "Ptr", 0)
	return OutLen
}
As it is and with Chrome.ahk V1.2 and Chrome "Version 79.0.3945.117 (Official Build)", the code stops at "Could not retrieve page!". Moreover, if I delete the "--headless" and run the script, it will say:
Error in #include Chrome.ahk: Chrome indicated error in response.
Specifically:{"code":-32000,"message":"PrintToPDF is not implemented"
It may help to diagnose the issue.

Thanks again !
Natitoun
Posts: 4
Joined: 09 May 2019, 06:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

10 Jan 2020, 07:03

Natitoun wrote:
10 Jan 2020, 03:57
Tre4shunter wrote:
09 Jan 2020, 11:30
Hey @Natitoun
...

Hi @Tre4shunter !
...
When I start the script, there are Chrome's background processes that starts. So I guess that it's the

Code: Select all

ChromeInst.GetPage()
that doesn't work.
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

10 Jan 2020, 08:50

Hey @Natitoun

I think its an issue with the Chrome profile path. Try Replacing the Code for creating a new chrome instance with this:

Code: Select all

profile := A_Temp "\ChromeProfile"
if !FileExist(profile)
	FileCreateDir, % profile
	
ChromeInst := new Chrome(profile, "https://www.google.com", "--headless")
Also note that if you remove '--headless' then Page.printToPdf is invalid, because pdf printing is not available while not in headless mode per the dev protocol spec.

Let me know if it works for you now!
Natitoun
Posts: 4
Joined: 09 May 2019, 06:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

13 Jan 2020, 11:18

Hi @Tre4shunter !

It works, thanks a lot for that !

I have another issue now that headless mode is working, I cannot figure a way out to do my stuff... Would you know how I could manage the login in headless mode ?

Here is what I want to do: open a new Chrome on which the user logs in. Then open a new headless chrome with the same profile (i.e. on which the account is still logged). Something like this:

Code: Select all

#Include Chrome.ahk ; Chrome.ahk v1.2

profile := A_Temp "\ChromeProfile"
if !FileExist(profile)
	FileCreateDir, % profile

ChromeInst := new Chrome(profile, "https://www.autohotkey.com/boards/ucp.php?mode=login")
sleep 5000 ; let's say that at the end on these 5 seconds, the used is logged in
ChromeInstHeadless := new Chrome(profile, "https://www.autohotkey.com/boards/", "--headless") ; I wish this session to be also logged in
So far I haven't found a way to do so. I tried to extract the cookies with "Newtork.GetAllCookies" from DevTools protocol but didn't reached it out. I've searched on many forums but still cannot find a way to deal with it...

Your help would be really welcome !

Thanks !
Nathan
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

14 Jan 2020, 11:15

Why dont you just build a little AHK gui window for them to put their credentials in, and then pass that to the headless chrome to login? Though, might be an issue if you don't want that credential being passed in plaintext.

@Natitoun
[EDIT]

Here would be another way...
Spoiler
Log in, grab the URL which includes SID, then close that instance, and open another with the logged in users SID as part of the URL. You would want to add some error checking to make sure pages are loaded etc, or if things fails...but this should work and give you a general idea.
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

17 Jan 2020, 15:17

I made a slight change to the Chrome.FindInstances() Function:

Code: Select all

	FindInstances(PortsToClose:="")
	{
		static Needle := "--remote-debugging-port=(\d+)"
		Out := {}, i = 0
		for Item in ComObjGet("winmgmts:").ExecQuery("SELECT CommandLine, ProcessId FROM Win32_Process WHERE Name = 'chrome.exe'")
			if RegExMatch(Item.CommandLine, Needle, Match){
				if Instr(PortsToClose,Match1){
					Process, close, % Item.ProcessId
				}else{
					Out[Match1,i++] := {"cmd":Item.CommandLine,"pid":Item.ProcessId,"port":Match1}
				}
			}
		return Out.MaxIndex() ? Out : False
	}
I added a parameter so that you can first check for, and close Chrome instances on certain ports. I found i had a need for this because in some cases if a script fails and the connection drops, if you dont first close those processes then your new connection on the same port will cause errors. Of course, if your programs never fail or bug out...i guess its not an issue, but im not that good - lol

Here would be an example use:

Code: Select all

path := A_Scriptdir "\form2.html"
Port := 9222

ProcessesToClose := "9500,9501"
if (Chromes := Chrome.FindInstances(ProcessesToClose)).HasKey(Port){
	port := chromes.maxIndex() + 1
	profile := A_Temp "\ChromeProfile" Port
}else{
	profile := A_Temp "\ChromeProfile"
}
I use Ports 9500/9501 for headless chrome printing of the 'Form2.html' document. In my case, sometimes my print routine will fail, and then subsequent print function calls will keep erroring - because there are still processes attached on port 9500. Now, i can give it a CSV list of ports to kill before i try creating the new headless instances.

Works great for my needs - might not for you, just thought id share!

Thanks!
Meridin
Posts: 2
Joined: 20 Jan 2020, 21:25

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

20 Jan 2020, 21:56

I can't figure out how to use dom to set the text in a textarea element. I can do it for input elements, but not textarea. When I run this code nothing happens to the textarea.

Code: Select all

SubjectNode := PageInst.Call("DOM.querySelector", {"nodeId": RootNode.nodeId, "selector": "input[name=subject]"})
PageInst.Call("DOM.setAttributeValue", {"nodeId": SubjectNode.nodeId, "name": "value", "value": "Chrome subject"})

MessageNode := PageInst.Call("DOM.querySelector", {"nodeId": RootNode.nodeId, "selector": "textarea[name=message]"})
PageInst.Call("DOM.setAttributeValue", {"nodeId": MessageNode.nodeId, "name": "value", "value": "Chrome message"})
The element itself looks like this.

Code: Select all

<textarea id="textarea_id" name="message" style="width:100%" rows="20" class="textarea"></textarea>
My current workaround is using Evaluate, but I want to know how to do it with Call so I don't have to escape the whole message, and that I'm just curious why it doesn't work. I suspect it may be that textarea's don't have a value attribute, but in that case I don't know what else to use then (other than Evaluate).

Code: Select all

PageInst.Evaluate("document.getElementById('textarea_id').value='Chrome message';")
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

21 Jan 2020, 14:02

Try using textContent property rather than value. @Meridin
Meridin
Posts: 2
Joined: 20 Jan 2020, 21:25

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

21 Jan 2020, 15:28

Thanks, but I tried 'textContent' along with 'innerHTML' and neither would work!
bourdin07
Posts: 33
Joined: 23 Jun 2019, 12:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

05 Feb 2020, 16:27

I found error :
RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Pahs\chrome.exe

should be instead ?
RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

05 Feb 2020, 17:40

bourdin07 wrote:
05 Feb 2020, 16:27
should be instead ?
RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
Yes. That's a known issue in the 1.2 release version.

It's fixed in this version: https://github.com/G33kDude/Chrome.ahk/blob/master/Chrome.ahk, but unfortunately there hasn't been a new release version since.
bourdin07
Posts: 33
Joined: 23 Jun 2019, 12:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

06 Feb 2020, 17:21

Code: Select all

	Jxon_True()
	{
		static obj := {}
		return obj
	}
	
	Jxon_False()
	{
		static obj := {}
		return obj
	}
Gnneeeee?????

I need explanation lol.
bourdin07
Posts: 33
Joined: 23 Jun 2019, 12:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

07 Feb 2020, 11:10

Code: Select all

    __New(WS_URL)
    {
        static WB 

        ; Create an IE instance
        Gui, +hWndhOld
        Gui, New, +hWndhWnd
        this.hWnd := hWnd
        Gui, Add, ActiveX, vWB, Shell.Explorer
        Gui, %hOld%: Default

        ; Write an appropriate document
        WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible' content='IE=edge'><body></body></html>")

        while (WB.ReadyState < 4) ; <------ Stay 1 all time
            Sleep 50
            
        ...
WB.ReadyState = 1 and seem to still loading page
Any ideas why the value is always 1 ?

Return to “Scripts and Functions”

Who is online

Users browsing this forum: robodesign and 69 guests