שימו לב: 

  • הפעולה עלולה לקחת קצת זמן מכוון שהיא מתבצעת בקליינט ומדמה את פעולותיו.
  • הפעולה עובדת בצורה איטית בכוונה כדי להימנע מריבוי קריאות לאדמין
  •  אין לצאת מהדפדפן עד שהפעולה לא הסתיימה.


יש להיכנס לאינספקט בדף כל חוות הדעת/ שאלות ותשובות  ולהריץ את הקוד הבא בקונסול:


(async function exportQandAToExcel() {
    console.log("? מתחיל באיסוף נתונים במצב בטוח (איטי מבוקר)...");
    
    let allData = [];
    let pageNum = 1;

    // כותרות העמודות
    const headers = ["מזהה", "תאריך ושעה", "כותרת", "תוכן", "שם המוצר", "דירוג", "מיקום", "אימייל", "סטטוס", "תשובה"];

    // פונקציית השהייה קלה
    const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

    // שליפת התשובה עם מנגנון ניסיון חוזר במידה ומתקבלת חסימה
    async function fetchAnswerWithRetry(detailUrl, retries = 3) {
        if (!detailUrl) return "";

        for (let attempt = 1; attempt <= retries; attempt++) {
            try {
                // השהייה של 1.5 שניות לפני כל פנייה לשרת למניעת חסימה
                await sleep(1500); 

                const res = await fetch(detailUrl);
                
                // אם המערכת חוסמת (429 = Too Many Requests / 503)
                if (res.status === 429 || res.status === 503) {
                    console.warn(`⚠️ זיהוי עומס (קוד ${res.status}). ממתין 10 שניות לפני ניסיון חוזר (${attempt}/${retries})...`);
                    await sleep(10000); // המתנה של 10 שניות לשחרור החסימה
                    continue;
                }

                if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);

                const html = await res.text();
                const doc = new DOMParser().parseFromString(html, "text/html");
                
                // 1. חיפוש תשובה בטבלת הפרטים
                const rows = Array.from(doc.querySelectorAll("tr, div, td"));
                for (let el of rows) {
                    if (el.children.length === 2 && el.children[0].innerText.includes("תשובה")) {
                        return el.children[1].innerText.trim();
                    }
                }

                // 2. חיפוש בתיבת ה-textarea
                const textarea = doc.querySelector("textarea");
                if (textarea && textarea.value.trim()) {
                    return textarea.value.trim();
                }

                return "";
            } catch (err) {
                console.error(`שגיאה בגישה ל- ${detailUrl} (ניסיון ${attempt}):`, err);
                if (attempt < retries) await sleep(5000);
            }
        }
        return ""; // מחזיר ריק אם כל הניסיונות נכשלו
    }

    async function parseCurrentPage() {
        const table = document.getElementById("admin_item_reviews");
        if (!table) return 0;

        const rows = Array.from(table.querySelectorAll("tbody tr"));
        let count = 0;

        for (let row of rows) {
            const cells = row.querySelectorAll("td");
            if (cells.length >= 9) {
                const idCell = cells[0];
                const detailLink = idCell.querySelector("a") ? idCell.querySelector("a").href : null;
                
                let answerText = "";
                if (detailLink) {
                    answerText = await fetchAnswerWithRetry(detailLink);
                }

                const rowData = [
                    cells[0].innerText.trim(), // מזהה
                    cells[1].innerText.trim(), // תאריך ושעה
                    cells[2].innerText.trim(), // כותרת
                    cells[3].innerText.trim(), // תוכן
                    cells[4].innerText.trim(), // שם המוצר
                    cells[5].innerText.trim(), // דירוג
                    cells[6].innerText.trim(), // מיקום
                    cells[7].innerText.trim(), // אימייל
                    cells[8].innerText.trim(), // סטטוס
                    answerText                 // תשובה
                ];

                allData.push(rowData);
                count++;
                console.log(`✔️ [דף ${pageNum}] נאסף מזהה ${cells[0].innerText.trim()} | תשובה: ${answerText ? 'קיימת' : 'אין'}`);
            }
        }
        return count;
    }

    async function processPages() {
        while (true) {
            console.log(`? מעבד עמוד ${pageNum}...`);
            const count = await parseCurrentPage();

            const nextButton = Array.from(document.querySelectorAll("a")).find(a => {
                const text = a.innerText.trim();
                const rel = a.getAttribute("rel");
                return text === "הבא" || text === "Next" || text === "›" || text === "»" || rel === "next";
            });

            if (nextButton && nextButton.href && nextButton.href !== window.location.href) {
                pageNum++;
                console.log(`⏳ מנוחה קצרה ועובר לעמוד ${pageNum}...`);
                await sleep(3000); // המתנה של 3 שניות בין דפים
                
                try {
                    const response = await fetch(nextButton.href);
                    const htmlText = await response.text();
                    
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(htmlText, "text/html");
                    
                    const nextTable = doc.getElementById("admin_item_reviews");
                    if (!nextTable) break;

                    document.getElementById("admin_item_reviews").innerHTML = nextTable.innerHTML;
                    
                    const currentPagination = document.querySelector(".pagination, .pager, .nav");
                    const nextPagination = doc.querySelector(".pagination, .pager, .nav");
                    if (currentPagination && nextPagination) {
                        currentPagination.innerHTML = nextPagination.innerHTML;
                    }
                } catch (err) {
                    console.error("שגיאה בטעינת העמוד הבא:", err);
                    break;
                }
            } else {
                console.log("✅ הגירוד הושלם!");
                break;
            }
        }

        downloadCSV(headers, allData);
    }

   function downloadCSV(headers, data) {
    let csvContent = "\uFEFF";
    csvContent += headers.map(h => `"${h.replace(/"/g, '""')}"`).join(",") + "\n";

    data.forEach(row => {
        const formattedRow = row.map(field => `"${field.replace(/"/g, '""')}"`).join(",");
        csvContent += formattedRow + "\n";
    });

    // זיהוי אוטומטי: אם ה-URL מכיל qa קוראים לזה "שאלות_ותשובות", אחרת "חוות_דעת"
    const isQA = window.location.href.includes("model_type=qa");
    const filePrefix = isQA ? "שאלות_ותשובות" : "חוות_דעת";
    const fileName = `${filePrefix}_${new Date().toISOString().slice(0,10)}.csv`;

    const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.setAttribute("href", url);
    link.setAttribute("download", fileName);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    
    console.log(`? הקובץ "${fileName}" הורד בהצלחה! סה"כ נאספו ${data.length} שורות.`);
}

    processPages();
})();

בסוף הפעולה יתקבל החיווי הבא בקונסול

וקובץ האקסל יורד למחשב: