feat(webscrape-bank): added code to webscrape my bank, api is next

This commit is contained in:
devaine 2026-01-26 19:20:48 -06:00
commit 711816726a
Signed by: devaine
GPG key ID: 954B1DCAC6FF84EE
6 changed files with 3973 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# PERSONAL INFO
.env
**/My-Finances*
notes
**/*qfx
# Others
node_modules/

31
actual-api/src/index.js Normal file
View file

@ -0,0 +1,31 @@
require("dotenv").config();
let api = require("@actual-app/api");
// Constants
const ACTUAL_URL = process.env.ACTUAL_URL
const ACTUAL_PASSWORD = process.env.ACTUAL_PASSWORD
const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID
(async () => {
await api.init({
dataDir: "./",
serverURL: ACTUAL_URL,
password: ACTUAL_PASSWORD,
});
console.log("Downloading the Budget Data")
await api.downloadBudget("1d6b0769-5ed4-4601-b6f2-b63df21f1f46");
console.log("Getting Account Info")
let accountInfo = await api.getAccounts()
console.log(accountInfo)
function inspectBankFile() {
}
//await api.shutdown();
})();

3817
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "transaction-sync",
"license": "ISC",
"main": "index.js",
"scripts": {
"actual": "node actual-api/src/index.js"
},
"dependencies": {
"@actual-app/api": "^26.1.0",
"concurrently": "^9.2.1",
"dotenv": "^17.2.3"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"eslint": "^9.39.2",
"eslint-plugin-import": "^2.32.0"
}
}

View file

@ -0,0 +1,99 @@
from playwright.sync_api import Playwright, sync_playwright, Page
from dotenv import load_dotenv
import os
from time import sleep
load_dotenv()
BANK_USER = os.getenv("BANK_USER")
BANK_PASS = os.getenv("BANK_PASS")
LOGIN_LINK = os.getenv("LOGIN_LINK")
ACCOUNT_LINK = os.getenv("ACCOUNT_LINK")
def main(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
page.goto(LOGIN_LINK)
page.locator("#username").click()
page.locator("#username").fill(BANK_USER)
page.locator("#password").click()
page.locator("#password").fill(BANK_PASS)
page.get_by_role("button", name="Login", exact=True).click()
# Wait a little bit
page.wait_for_load_state()
page.get_by_role("button", name="Text").click()
page.get_by_test_id("text-field").click()
# Bank prompts 2FA (hopefully only once)
two_fac = input("2FA Code: ")
page.get_by_test_id("text-field").fill(two_fac)
page.get_by_test_id("private-button").click()
# Wait for everything to load...
page.wait_for_timeout(5000)
# We should be at the home page here
download_file(page)
def switchingForever(page: Page):
print("Switching to not get timed out!")
page.goto(ACCOUNT_LINK)
count = 0
while (count < 12):
page.wait_for_timeout(5000)
page.get_by_role("link", name="Cards").hover()
page.get_by_role("link", name="Activate Card").click(timeout=0)
sleep(300)
page.get_by_role("link", name="My Accounts").click(timeout=0)
count = count + 1
print("Count updated: " + str(count))
download_file(page)
def download_file(page: Page):
print("Going to download a new QFX File")
page.goto(ACCOUNT_LINK)
page.locator('iframe[title="Next Gen Home Page"]').content_frame.get_by_test_id(
"com.ncr.dbk.olb.widgets.my-accounts"
).get_by_test_id("feature-link").click()
page.wait_for_timeout(5000)
page.locator("#ncr-la-chat-win-notif-close path").click()
page.locator(
'iframe[title="NextGen account history page"]'
).content_frame.get_by_role("button", name="Download Transactions Button").click()
with page.expect_download(timeout=0) as download_info:
page.mouse.wheel(0, 70)
page.locator(
'iframe[title="NextGen account history page"]'
).content_frame.get_by_role("menuitem", name="Export QFX").dblclick()
download = download_info.value
download.save_as("./test.qfx")
print("Downloaded!")
switchingForever(page)
# context.close()
# browser.close()
if __name__ == "__main__":
with sync_playwright() as playwright:
main(playwright)

View file