Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name ExamPrepper Content Dumper
- // @namespace http://tampermonkey.net/
- // @version 1.1
- // @description Dump accordion content from ExamPrepper pages to OPFS (AJAX-aware)
- // @match https://www.examprepper.co/exam/*
- // @grant none
- // ==/UserScript==
- (async function() {
- 'use strict';
- alert('*')
- const FILENAME = 'questions.html';
- const PAGE_SEPARATOR = '\n\n';
- let isProcessing = false;
- let processedPages = new Set();
- console.log('[ExamPrepper Dumper] Script started');
- console.log('[ExamPrepper Dumper] Current URL:', window.location.href);
- // Wait 2 seconds for initial page load
- console.log('[ExamPrepper Dumper] Waiting 2 seconds for page to load...');
- await sleep(2000);
- // Process the first page
- await processPage();
- // Set up MutationObserver to detect AJAX page changes
- const observer = new MutationObserver(async (mutations) => {
- // Check if accordion items have changed (new page loaded)
- for (const mutation of mutations) {
- if (mutation.addedNodes.length > 0) {
- // Check if new accordion items were added
- const hasNewAccordion = Array.from(mutation.addedNodes).some(node => {
- if (node.nodeType === 1) { // Element node
- return node.classList?.contains('chakra-accordion__item') ||
- node.querySelector?.('.chakra-accordion__item');
- }
- return false;
- });
- if (hasNewAccordion && !isProcessing) {
- console.log('[ExamPrepper Dumper] New content detected after AJAX load');
- await sleep(1000); // Give it a moment to settle
- await processPage();
- }
- }
- }
- });
- // Start observing the document body for changes
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- console.log('[ExamPrepper Dumper] Now monitoring for AJAX page changes...');
- async function processPage() {
- if (isProcessing) {
- console.log('[ExamPrepper Dumper] Already processing, skipping...');
- return;
- }
- isProcessing = true;
- try {
- // Get OPFS root
- const root = await navigator.storage.getDirectory();
- // Find all accordion items
- const accordionItems = document.querySelectorAll('.chakra-accordion__item');
- console.log(`[ExamPrepper Dumper] Found ${accordionItems.length} accordion items`);
- if (accordionItems.length === 0) {
- console.warn('[ExamPrepper Dumper] No accordion items found! Check if page loaded correctly or CAPTCHA present.');
- isProcessing = false;
- return;
- }
- // Create a unique page identifier (could use URL + first question text)
- const firstItemText = accordionItems[0]?.textContent?.substring(0, 100) || '';
- const pageId = window.location.href + firstItemText;
- if (processedPages.has(pageId)) {
- console.log('[ExamPrepper Dumper] This page was already processed, skipping...');
- isProcessing = false;
- return;
- }
- processedPages.add(pageId);
- console.log(`[ExamPrepper Dumper] Processing new page (total pages processed: ${processedPages.size})`);
- let pageContent = '';
- // Loop through each accordion item
- for (let i = 0; i < accordionItems.length; i++) {
- const item = accordionItems[i];
- console.log(`[ExamPrepper Dumper] Processing item ${i + 1}/${accordionItems.length}`);
- // Find "Show Answer" button within this item
- const buttons = item.querySelectorAll('button');
- let showAnswerButton = null;
- for (const button of buttons) {
- if (button.textContent.trim() === 'Show Answer') {
- showAnswerButton = button;
- break;
- }
- }
- if (showAnswerButton) {
- console.log(`[ExamPrepper Dumper] Clicking "Show Answer" button for item ${i + 1}`);
- showAnswerButton.click();
- // Wait 1 second after clicking
- await sleep(1000);
- // Append outerHTML to content
- pageContent += item.outerHTML;
- console.log(`[ExamPrepper Dumper] Item ${i + 1} content captured`);
- } else {
- console.warn(`[ExamPrepper Dumper] No "Show Answer" button found in item ${i + 1}`);
- }
- }
- // Append page content to file
- await appendToFile(root, FILENAME, pageContent + PAGE_SEPARATOR);
- console.log('[ExamPrepper Dumper] Content appended to file successfully');
- // Wait 2 more seconds
- console.log('[ExamPrepper Dumper] Waiting 2 seconds before looking for Next button...');
- await sleep(2000);
- // Find and click "Next" button
- const allButtons = document.querySelectorAll('button');
- let nextButton = null;
- for (const button of allButtons) {
- if (button.textContent.trim() === 'Next') {
- nextButton = button;
- break;
- }
- }
- if (nextButton) {
- console.log('[ExamPrepper Dumper] "Next" button found, clicking...');
- nextButton.click();
- console.log('[ExamPrepper Dumper] Waiting for AJAX to load next page...');
- } else {
- console.log('[ExamPrepper Dumper] No "Next" button found. Either all pages exhausted or CAPTCHA/error present.');
- console.log('[ExamPrepper Dumper] ✓ Scraping complete!');
- observer.disconnect(); // Stop observing
- }
- } catch (error) {
- console.error('[ExamPrepper Dumper] Error occurred:', error);
- } finally {
- isProcessing = false;
- }
- }
- // Helper function to sleep
- function sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- // Helper function to append content to OPFS file
- async function appendToFile(root, filename, content) {
- const fileHandle = await root.getFileHandle(filename, { create: true });
- const writable = await fileHandle.createWritable({ keepExistingData: true });
- // Get current file size to append at the end
- const file = await fileHandle.getFile();
- const size = file.size;
- // Seek to end and write
- await writable.write({ type: 'write', position: size, data: content });
- await writable.close();
- console.log(`[ExamPrepper Dumper] Appended ${content.length} characters to ${filename} (total size: ${size + content.length} bytes)`);
- }
- })();
Advertisement
Add Comment
Please, Sign In to add comment