Skip to content

jonathan-kee/ExamTopicScraper

Repository files navigation

EXAMTOPICSCRAPER

The reason I was doing this is because I don't want to pay the expensive fee to see the certification dumps lol.

Project Highlights

Postgres container working

Postgres container working

Storage bucket working

Storage bucket working

Upload object to Storage Bucket

Upload object to Storage Bucket

Docker containers in workflow created & removed

Docker containers in workflow created & removed

Node & NPM in workflow created & removed

Node & NPM in workflow created & removed

Scape raw document from website using inspect network and code

Scape raw document from website using inspect network and code

ETL failure recovery

  1. What happens if scraping fail, how to recover?
  2. What happens if transformation fail, how to recover?
  3. What happens if presentation fail, how to recover?

Browserless Pipeline commands

  1. npm run unitTest -- build/test/extractAllRawDocument.test.js
  2. java -jar /Users/jonathankee/examTopicScraper/static_page/build/libs/static_page-1.0-SNAPSHOT-all.jar
  3. Run markdown code

Browser Pipeline commands

  • tsc --build --clean
  • tsc --build
  1. tsc && node ./build/commands/scrapeWebsiteLinksIntoPostgres.js
  2. tsc && node ./build/commands/scrapeWebsiteLinksIntoPostgresHardcode.js
  3. tsc && node ./build/commands/scrapeDataIntoPostgres.js
  4. tsc && node ./build/commands/scrapeImages.js
  5. tsc && node ./build/commands/markdown.js

Postgres docker installation

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

Backup Postgres docker data (Incase I lose all data at some point)

docker exec -t postgres-container pg_dump -U postgres -d postgres > ./backup_sql/backup.sql

RustFS (Minio Replacement) docker installation

(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

Installation Guide

Follow the official github documentation:

Follow below for personalized installation:

Vagrant After Setup & Provisioning

  1. cd linux/selfhost-runner
  2. vagrant up --provider vmware_desktop
  3. vagrant ssh Server1
  4. cd actions-runner
  5. ./run.sh

Run Github Actions

Link to Github Actions workflow:

Vagrant Setup & Provisioning

You need to enable the setting below for vmware to work: Privacy & Security -> Full Disk Access -> vagrant-vmware-utility

  1. cd linux/selfhost-runner
  2. vagrant up --provider vmware_desktop
  3. vagrant provision (You can skip this step, vagrant up will already provision)
  4. vagrant ssh Server1
  5. sudo chown -R vagrant:vagrant /home/vagrant/actions-runner
  6. cd actions-runner
  7. ./config.sh --url https://github.com// --token YOUR_TOKEN_HERE

*** Enter registration process details ***

./run.sh

My registration process

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.

Resources to learn webscraping

  1. Website on webscraping & crawling:
  1. I am currently using browser automation instead of http clients:
  1. Youtube channels on webscraping & crawling:
  1. Airflow being used for webscraping:

Resources to learn Data Engineering

  1. Official Reddit Date Engineering website
  1. Kodekloud
  1. Architecture for Data
  1. Workflow Orchestration

Airflow certification link:

Notes on Airflow certification:

  1. Data Ingestion

Dlt Certification links:

  1. Data Processing

DBT Certification links:

Simple Guide on setup DBT with Postgres:

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)

  1. Data Analytics
  • Excel
  • Power BI

12 Factor App Methodology

Github Actions solves

  • Codebase
  • Build, Release and Run

^ You have to learn Git before Github Actions to make sense, can try below:

Project Management Tools

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:

^

  1. Create Issues
  2. Issue can become a branch or link a pull request to the Issue
  3. Issue become a new branch, compare branch to main and create a pull request
  4. Creating a pull request will link to the issue
  5. 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 ***

System Packages / Project Setup

  1. Install node version manager:

^^^^^^ 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
  1. Make .bashrc take effect immediately by sourcing:
  • source ~/.bashrc
  1. Install Node 24:
  • nvm install 24
  1. Verify the Node.js version:
  • node -v
  1. Verify npm version:
  • npm -v
  1. Install Typescript:
  • npm install -g typescript
  1. Install Dependencies:
  • npm install
  1. Compile Typescript and launch with node with sample arguments:
  • tsc && node ./build/index.js

Npm Commands that are relevent

  1. After npm install, there might be vulnerabilities:
  • npm audit fix

Run all tests

Compile Typescript to build folder, run all tests

  • tsc --build --clean
  • tsc --build
  • npm run test

Run specific 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

Github Actions / Forejo Actions Setup

Docker Setup

  • 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:

  1. docker image pull minio/minio

  2. docker image pull postgres:14.21-trixie

  3. Build Image for the webscraper

  4. Create docker compose file

Docker Setup QNA

Question: If Postgres has delta table extension, will it be able to handle DBA SQL as well? Answer:

DBT Setup

  1. Install DBT fusion:
  1. Install DBT extension by "dbt Labs Inc":

Features to add

  • 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:

Use Playwright for more scalable web scraping:

Indepth book about web scraping: https://github.com/REMitchell/python-scraping

QNA

Question: Is SSIS still worth learning just to learn more about ETL patterns?

Projects Ideas

Download Extension to remove AI overview from Google that prevent scraping

Extension link:

Use Extension for Browser Automation

1. Clean up the old, broken profile directory

  • rm -rf ./chrome-puppeteer-profile

2. Create the parent directory structure Chrome expects

  • mkdir -p ./chrome-puppeteer-profile

3. Copy your "Default" profile folder inside that new parent directory

  • cp -R "$HOME/Library/Application Support/Google/Chrome/Default" ./chrome-puppeteer-profile/

4. Launch Chrome pointing to the parent directory

  • /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
    --remote-debugging-port=9222
    --user-data-dir="$(pwd)/chrome-puppeteer-profile"

Launch browser that google does not capcha

  • /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
    --remote-debugging-port=9222
    --user-data-dir="$(pwd)/chrome-puppeteer-profile"

How to remove popup block

Apparently if you edit class="popup-overlay show" to "popup-overla show", the popup will break

HTTP Client vs Headless Browser

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.


Why CSS Selectors Work Best for Both

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:

  1. Identical Syntax: CSS selector syntax like ul.question-list or div#content > p is standard across both Java (Jsoup) and JavaScript (Puppeteer).
  2. 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.

Recommended Selector Methods (Ranked)

1. Data Attributes or Unique IDs (Most Reliable)

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"]');

2. Class Names + Element Tags (Very Reliable)

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');

3. Relative Structural Selectors (When no classes/IDs exist)

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 > ul

When a Shared Selector Still Fails

If 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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages