שימו לב:
- הפעולה עלולה לקחת קצת זמן מכוון שהיא מתבצעת בקליינט ומדמה את פעולותיו.
- הפעולה עובדת בצורה איטית בכוונה כדי להימנע מריבוי קריאות לאדמין
- אין לצאת מהדפדפן עד שהפעולה לא הסתיימה.
יש להיכנס לאינספקט בדף כל חוות הדעת/ שאלות ותשובות ולהריץ את הקוד הבא בקונסול:
(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();
})();בסוף הפעולה יתקבל החיווי הבא בקונסול
וקובץ האקסל יורד למחשב:
