The reason I was doing this is because I don't want to pay the expensive fee to see the certification dumps lol.
Upload object to Storage Bucket
Docker containers in workflow created & removed
Node & NPM in workflow created & removed
Scape raw document from website using inspect network and code
- What happens if scraping fail, how to recover?
- What happens if transformation fail, how to recover?
- What happens if presentation fail, how to recover?
- npm run unitTest -- build/test/extractAllRawDocument.test.js
- java -jar /Users/jonathankee/examTopicScraper/static_page/build/libs/static_page-1.0-SNAPSHOT-all.jar
- Run markdown code
- tsc --build --clean
- tsc --build
- tsc && node ./build/commands/scrapeWebsiteLinksIntoPostgres.js
- tsc && node ./build/commands/scrapeWebsiteLinksIntoPostgresHardcode.js
- tsc && node ./build/commands/scrapeDataIntoPostgres.js
- tsc && node ./build/commands/scrapeImages.js
- tsc && node ./build/commands/markdown.js
Youtube link:
Start a postgres container:
- docker run --name postgres-container
-e POSTGRES_PASSWORD=abc123
-v "$(pwd)/docker_volume:/var/lib/postgresql"
-p 5432:5432
-d postgres:latest
Check container processes:
- docker ps -a
Run psql inside postgres container:
- docker exec -it postgres-container psql -U postgres
Feed sql file to postgres container:
- docker exec -i postgres-container psql -U postgres -d postgres < ./sql/docker_pg_scraper.sql
- docker exec -i postgres-container psql -U postgres -d postgres < ./sql/docker_pg_seq_scraper.sql
- docker exec -i postgres-container psql -U postgres -d postgres < ./sql/docker_pg_schema.sql
- docker exec -i postgres-container psql -U postgres -d postgres < ./sql/docker_pg_seq_schema.sql
Use datagrip and connect and see if the SQL files are ran
Data still persist after start & stop:
- docker stop postgres-container
- docker start postgres-container
docker exec -t postgres-container pg_dump -U postgres -d postgres > ./backup_sql/backup.sql
(Skip, Managed my Docker-managed named volumes) Create data and logs directories:
- mkdir -p data logs
(Skip, Managed my Docker-managed named volumes) Change the owner of these directories:
- sudo chown -R 10001:10001 data logs
Using latest version (Using Docker-managed named volumes) (Gemini fix):
- docker run --detach
--name=rustfs
--publish 9000:9000/udp
--publish 9000:9000/tcp
--publish 9001:9001/udp
--publish 9001:9001/tcp
--volume rustfs-data:/data
--volume rustfs-logs:/logs
-e RUSTFS_ACCESS_KEY="rustfsadmin"
-e RUSTFS_SECRET_KEY="rustfsadmin"
-e RUSTFS_ADDRESS="0.0.0.0:9000"
-e RUSTFS_CONSOLE_ADDRESS="0.0.0.0:9001"
-e RUSTFS_CONSOLE_ENABLE=true
-e RUSTFS_CORS_ALLOWED_ORIGINS=""
-e RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=""
rustfs/rustfs:latest
--access-key "rustfsadmin"
--secret-key "rustfsadmin"
--console-enable
/data
Follow the official github documentation:
Follow below for personalized installation:
- cd linux/selfhost-runner
- vagrant up --provider vmware_desktop
- vagrant ssh Server1
- cd actions-runner
- ./run.sh
Link to Github Actions workflow:
-
https://github.com/jonathan-kee/examTopicScraper/actions/workflows/first-example.yml
-
Click Run Workflow
You need to enable the setting below for vmware to work: Privacy & Security -> Full Disk Access -> vagrant-vmware-utility
- cd linux/selfhost-runner
- vagrant up --provider vmware_desktop
- vagrant provision (You can skip this step, vagrant up will already provision)
- vagrant ssh Server1
- sudo chown -R vagrant:vagrant /home/vagrant/actions-runner
- cd actions-runner
- ./config.sh --url https://github.com// --token YOUR_TOKEN_HERE
*** Enter registration process details ***
./run.sh
Runner Registration Enter the name of the runner group to add this runner to: [press Enter for Default]
Enter the name of runner: [press Enter for Server1] prod-rocky-runner
This runner will have the following labels: 'self-hosted', 'Linux', 'ARM64' Enter any additional labels (ex. label-1,label-2): [press Enter to skip]
√ Runner successfully added
Runner settings
Enter name of work folder: [press Enter for _work]
√ Settings Saved.
- Website on webscraping & crawling:
- I am currently using browser automation instead of http clients:
- https://webscraping.fyi/overview/browser-automation/
- https://webscraping.fyi/overview/languages/#http-clients
- Youtube channels on webscraping & crawling:
- Airflow being used for webscraping:
- Official Reddit Date Engineering website
- Kodekloud
- Architecture for Data
- Workflow Orchestration
Airflow certification link:
Notes on Airflow certification:
- Data Ingestion
Dlt Certification links:
- https://dlthub.learnworlds.com/course/dlt-fundamentals
- https://dlthub.learnworlds.com/course/dlt-advanced
- Data Processing
DBT Certification links:
- https://learn.getdbt.com/learning-paths/dbt-certified-developer
- https://learn.getdbt.com/learning-paths/dbt-certified-cloud-architect
Simple Guide on setup DBT with Postgres:
- https://github.com/prule/dbt-spike/tree/main
- https://paulr70.substack.com/p/getting-started-with-dbt
- https://paulr70.substack.com/p/adding-a-unit-test-to-dbt
Data Engineering with Dbt by Roberto Zagni is a good recommendation: https://www.reddit.com/r/dataengineering/comments/1lx0xia/tips_for_books_other_material_for_self_study_on/
Apache Spark (Data bricks certs)
- Data Analytics
- Excel
- Power BI
Github Actions solves
- Codebase
- Build, Release and Run
^ You have to learn Git before Github Actions to make sense, can try below:
Github's Project Management tool like Trello, Jira:
Very useful feature for Github:
Scrum with GitHub Projects (Very good introduction to Github Projects):
Github Workflow for Issues and Pull Requests:
^
- Create Issues
- Issue can become a branch or link a pull request to the Issue
- Issue become a new branch, compare branch to main and create a pull request
- Creating a pull request will link to the issue
- Merge Pull request will also close the issue
TLDR: Create Issue -> New Branch from Issue -> Create Pull Request (Compare branch to main) -> Merge Pull Request -> Close Issue
GitHub Foundations Certification Course (Most detailed explanation on Issues & Pull Request):
Learn about Github Issue before moving into Github projects:
Github projects training course:
Question: So how does Github Issues, Github Projects fit into CI / CD?
*** Project Idea, have text to speech for recordings / meetings, then translate it Github Projects UI ***
- Install node version manager:
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
^^^^^^ The following wont work if ~/.profile exist, Then you need to manually add to .bashrc by doing the following:
- echo 'export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >> ~/.bashrc
- Make .bashrc take effect immediately by sourcing:
- source ~/.bashrc
- Install Node 24:
- nvm install 24
- Verify the Node.js version:
- node -v
- Verify npm version:
- npm -v
- Install Typescript:
- npm install -g typescript
- Install Dependencies:
- npm install
- Compile Typescript and launch with node with sample arguments:
- tsc && node ./build/index.js
- After npm install, there might be vulnerabilities:
- npm audit fix
Compile Typescript to build folder, run all tests
- tsc --build --clean
- tsc --build
- npm run test
Compile Typescript to build folder, run specific test
- tsc --build --clean
- tsc --build
- npm run unitTest -- build/test/add.test.js
- npm run unitTest -- build/test/env.test.js
- npm run unitTest -- build/test/extractRawDocument.test.js
- npm run unitTest -- build/test/storageBucket.test.js
- npm run unitTest -- build/test/extractAllRawDocument.test.js
- Minio
- Postgres
^ Both of these need to use volume, otherwise they would data would be lost
I am not entirely sure if I should create an image for the webscraper
^ If I am not mistaken, the 12 factor app mentioned about containerize scripts
^ The famous voting app has a image that is not a web server, it's like a script that has loop forevor until it errors
Docker is actually a server, it comes with
- Docker Daemon (dockerd)
- REST API
- Docker CLI (docker)
^ Based on Kodekloud's Docker-Certified-Associate-Exam-Course, You still need indepth knowledge of Linux
Link to Docker Certified Associate (DCA) Exam:
Mirantis bought over the Docker Certification Exam:
An alternative to Kodekloud's docker courses https://labs.iximiuz.com/roadmaps/docker
Not sure what is the importance for the process tree looking like that https://labs.iximiuz.com/challenges/docker-101-container-run-in-background
Download an image without starting a container:
-
docker image pull minio/minio
-
docker image pull postgres:14.21-trixie
-
Build Image for the webscraper
-
Create docker compose file
Question: If Postgres has delta table extension, will it be able to handle DBA SQL as well? Answer:
-
Oracle Database 12c DBA Handbook book is around 700 pages, I will just assume Postgres DBA SQL is that deep as well.
-
I have a folder named DBA1-handouts-16-en for Postgres DBA SQL, I downloaded from https://edu.postgrespro.com/16/DBA1-handouts-16-en.zip
- Install DBT fusion:
- Install DBT extension by "dbt Labs Inc":
- Rescrape pages that result in dirty data, need to update / merge existing data.
^ Partially added for Answers, never needed for question & discussions
-
Error handling for missing src images
-
Workflow Orchestration with Airflow
-
Column lineage with dbt
-
Schema Drift from upstream (The HTML, Javascript from examtopics)
-
SQL backup dump on repository, so when switch computer we get the data back
-
Maybe instead of scraping the data directly, you take all the data first and dump it into a data lake, then only process it at some future point
^ Try to use MINIO as a datalake, Hadoop also works
^ This pattern is good, because you don't have to think about transforming the data, you just focus on extracting as much data as possible
^ Good oppurtunity to use pg_lake for Postgres:
-
Convert the project into a CLI
-
Add Presentation like Excel or PowerBi if possible
Use Playwright for more scalable web scraping:
Indepth book about web scraping: https://github.com/REMitchell/python-scraping
Question: Is SSIS still worth learning just to learn more about ETL patterns?
- Connect Touch&Go Ewallet to Bukku Accounting system
- Interesting site to do data scraping on financial / banking data https://apikijangportal.bnm.gov.my/openapi
Extension link:
- rm -rf ./chrome-puppeteer-profile
- mkdir -p ./chrome-puppeteer-profile
- cp -R "$HOME/Library/Application Support/Google/Chrome/Default" ./chrome-puppeteer-profile/
- /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
--remote-debugging-port=9222
--user-data-dir="$(pwd)/chrome-puppeteer-profile"
- /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
--remote-debugging-port=9222
--user-data-dir="$(pwd)/chrome-puppeteer-profile"
Apparently if you edit class="popup-overlay show" to "popup-overla show", the popup will break
I tried using jsoup but the xpath select result is completely different from puppetter xpath
Answer (Gemini):
Yes, you should definitely avoid XPaths—especially absolute ones—if you want selectors that work consistently across both Jsoup and Puppeteer.
Instead, the best method to use is CSS Selectors targeting attributes, classes, or IDs.
While Jsoup and Puppeteer evaluate against different stages of the DOM (raw vs. JavaScript-rendered), using CSS selectors based on stable elements reduces the chance of broken queries:
- Identical Syntax: CSS selector syntax like
ul.question-listordiv#content > pis standard across both Java (Jsoup) and JavaScript (Puppeteer). - Resilient to Structural Shifts: Unlike XPaths, which break if an extra wrapper
<div>is added by browser JavaScript or an extension, class and ID selectors target the element directly no matter where it sits in the tree.
If the site provides id or data-* attributes, use them. They rarely change between static HTML and dynamic rendering.
#question-list-container
[data-testid="questions-list"]- Jsoup (Java):
Elements q = doc.select("[data-testid='questions-list']");- Puppeteer (Node.js):
const q = await page.waitForSelector('[data-testid="questions-list"]');Combine element types with meaningful class names to narrow down the target.
ul.questions-list
div.main-content ul.items- Jsoup (Java):
Elements q = doc.select("ul.questions-list");- Puppeteer (Node.js):
const q = await page.waitForSelector('ul.questions-list');If the target <ul> has no class or ID, anchor your CSS selector to the closest parent container that does have a unique identifier, then drill down using standard CSS combinators (like > or spaces):
/* Finds the ul inside the .content-body section, avoiding absolute top-level paths */
.content-body > div > ulIf you use the exact same CSS selector (e.g., ul.questions-list) and Jsoup returns nothing while Puppeteer succeeds, the issue isn't the selector syntax—it's that the target HTML element does not exist yet when Jsoup receives the raw response.
In that scenario, you'll need to either stick with Puppeteer for that specific page or scrape the raw background API endpoint Jsoup can't reach.





