להורדה | סימניה לייצוא שרשורים שלמים מפורום פרוג לייבוא לAI וכדו'
-
בהמשך לתוסף כרום של @NH.LOCAL כאן ולסימניה של @יום_שמח כאן ששניהם מאפשרים ייצוא של שרשור שלם בפורמט JSON במתמחים, יצרתי כזו סימניה גם לפרוג.
הרבה מזה מבוסס ממש על הסימניה של @יום_שמח (אפילו העיצוב של החלונית אותו דבר) אז הקרדיט מגיע לו.
האמת היא שאפשר אולי לאחד את שני הסימניות יחסית בקלות, אבל אין לי כח כרגע, אם מישהו מעוניין הוא מוזמן בכבוד.
@NH.LOCAL אתה יכול להוסיף גם את זה לתוסף
אני אישית לא משתמש אף פעם בפרוג, ובטח לא נצרך לייצוא של השרשורים שם, אז אין לי איך לדעת אם זה עובד חוץ מהבדיקה הראשונית, אם יש באגים תדווחו.
javascript:(async()=>{const config={postSelector:"article.message[data-content]",authorSelector:"h4.message-name a.username",timeSelector:"time[data-time]",contentSelector:".bbWrapper",paginationSelector:".pageNav-main li:last-of-type a",titleSelector:".p-title-value",canonicalUrlSelector:"link[rel='canonical']",csrfTokenSelector:"input[name='_xfToken']"};const downloadJSON=(d,f)=>{const j=JSON.stringify(d,null,2);const b=new Blob([j],{type:"application/json"});const u=URL.createObjectURL(b);const a=document.createElement("a");a.href=u;a.download=f;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(u)};const copyJSON=async d=>{const j=JSON.stringify(d,null,2);try{await navigator.clipboard.writeText(j);alert("✅ הטקסט הועתק ללוח")}catch(e){console.error("שגיאה בהעתקה",e);alert("❌ נכשל בהעתקה ללוח")}};const loadScript=url=>new Promise((resolve,reject)=>{if(document.querySelector(`script[src="${url}"]`)){return resolve()}const script=document.createElement("script");script.src=url;script.onload=resolve;script.onerror=reject;document.head.appendChild(script)});const initTurndown=async()=>{if(typeof TurndownService==="undefined"){console.log("Turndown.js לא טעון, טוען כעת...");await loadScript("https://cdn.jsdelivr.net/npm/turndown@7.1.2/dist/turndown.min.js")}return new TurndownService({headingStyle:"atx",bulletListMarker:"*",codeBlockStyle:"fenced",emDelimiter:"*",strongDelimiter:"**",linkStyle:"inlined",hr:"---",preformattedCode:true})};const parsePostsFromDocument=docObject=>{const postsOnPage=[];const postElements=docObject.querySelectorAll(config.postSelector);postElements.forEach(p=>{const timeEl=p.querySelector(config.timeSelector);const authorEl=p.querySelector(config.authorSelector);const postData={author:authorEl?.innerText.trim()||p.getAttribute("data-author")||"unknown",dateTime:timeEl?.getAttribute("title")||null,contentHTML:p.querySelector(config.contentSelector)?.innerHTML.trim()||""};postsOnPage.push(postData)});return postsOnPage};let turndownService;try{const canonicalLink=document.querySelector(config.canonicalUrlSelector)?.href;if(!canonicalLink||!canonicalLink.includes("/threads/")){alert("נראה שאתה לא בעמוד של שרשור (thread). יש להפעיל את הסימניה מעמוד שרשור תקין.");return}if(document.querySelectorAll(config.postSelector).length===0){alert("❌ לא זוהו פוסטים בעמוד הנוכחי.\nייתכן שתבנית העיצוב של הפורום אינה נתמכת.");return}const csrfToken=document.querySelector(config.csrfTokenSelector)?.value;if(!csrfToken){alert("❌ לא נמצא טוקן אבטחה (CSRF) בעמוד. לא ניתן להמשיך.");return}console.log("זוהה טוקן אבטחה, מתחיל באיסוף...");turndownService=await initTurndown();let baseUrl=new URL(canonicalLink);baseUrl.search="";baseUrl.pathname=baseUrl.pathname.replace(/\/page-\d+(\/)?$/,'');const threadUrl=baseUrl.toString();console.log(`Base URL for requests: ${threadUrl}`);const threadIdMatch=threadUrl.match(/\.(\d+)\/?$/);const threadId=threadIdMatch?threadIdMatch[1]:Date.now();const threadTitle=(document.querySelector(config.titleSelector)?.innerText||"thread").replace(/[\/\\?%*:|"<>\s]/g,"_").toLowerCase();const fileName=`thread_${threadId}_${threadTitle}.json`;const lastPageLink=document.querySelector(config.paginationSelector);const pageCount=lastPageLink?parseInt(lastPageLink.innerText.replace(/,/g,""),10):1;console.log(`זוהו ${pageCount} עמודים בשרשור.`);const fetchPage=async pageNum=>{try{const targetUrl=new URL(threadUrl);if(targetUrl.pathname.endsWith('/')){targetUrl.pathname+=`page-${pageNum}`}else{targetUrl.pathname+=`/page-${pageNum}`};const bodyParams=new URLSearchParams();bodyParams.append('_xfToken',csrfToken);bodyParams.append('_xfResponseType','json');bodyParams.append('page',pageNum);bodyParams.append('_xfWithData','1');bodyParams.append('_xfRequestUri',targetUrl.pathname+targetUrl.search);bodyParams.append('_xfNoRedirect','1');console.log(`Requesting page ${pageNum} via POST to ${targetUrl.toString()}`);const response=await fetch(targetUrl.toString(),{method:'POST',headers:{"X-Requested-With":"XMLHttpRequest","Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"},body:bodyParams.toString(),credentials:'same-origin'});if(!response.ok){const errorText=await response.text();console.error(`Request for page ${pageNum} failed with status ${response.status}`,errorText);throw new Error(`Response not OK: ${response.status}`)}const data=await response.json();if(data.status==="error"){throw new Error(`Server returned error: ${data.errors?.[0]}`)}if(!data.html||!data.html.content){console.warn(`עמוד ${pageNum} החזיר מבנה JSON לא צפוי.`);return[]}const parser=new DOMParser();const doc=parser.parseFromString(data.html.content,"text/html");return parsePostsFromDocument(doc)}catch(err){console.error(`שגיאה בטעינת עמוד ${pageNum}:`,err);return[]}};let allRawPosts=[];if(pageCount===1){console.log("שרשור בן עמוד אחד, מאחזר פוסטים ישירות מהעמוד...");allRawPosts=parsePostsFromDocument(document)}else{console.log(`מאחזר פוסטים מ-${pageCount} עמודים...`);const pagePromises=Array.from({length:pageCount},(_,i)=>fetchPage(i+1));const pagesResult=await Promise.all(pagePromises);allRawPosts=pagesResult.flat()}if(allRawPosts.length===0){alert("❌ האיסוף הסתיים, אך לא נמצאו פוסטים תקינים.");return}const processedPosts=allRawPosts.filter(p=>p&&p.author).map(p=>({author:p.author,dateTime:p.dateTime,contentMarkdown:turndownService.turndown(p.contentHTML)}));const dlg=document.createElement("div");dlg.style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:16px;padding:24px;z-index:999999;width:320px;max-width:90%;text-align:center;font-family:Arial,sans-serif;box-shadow:0 8px 24px rgba(0,0,0,0.25);";dlg.innerHTML=`<h3 style="margin-top:0;color:#333;font-size:18px;">שמירת נתוני השרשור</h3><p style="font-size:14px;color:#555;word-break:break-word;">נאספו ועובדו ${processedPosts.length} פוסטים.</p><div style="display:flex;gap:10px;justify-content:center;margin-top:16px;"><button id="btnDownload" style="flex:1;padding:10px;border:none;border-radius:8px;background:#4CAF50;color:#fff;cursor:pointer;font-size:14px;">⬇️ הורדה</button><button id="btnCopy" style="flex:1;padding:10px;border:none;border-radius:8px;background:#2196F3;color:#fff;cursor:pointer;font-size:14px;">📋 העתקה</button></div><button id="btnClose" style="margin-top:14px;padding:8px 16px;border:none;border-radius:8px;background:#f44336;color:#fff;cursor:pointer;font-size:13px;">❌ סגור</button>`;document.body.appendChild(dlg);dlg.querySelector("#btnDownload").onclick=()=>{downloadJSON(processedPosts,fileName);document.body.removeChild(dlg)};dlg.querySelector("#btnCopy").onclick=()=>{copyJSON(processedPosts);document.body.removeChild(dlg)};dlg.querySelector("#btnClose").onclick=()=>{document.body.removeChild(dlg)}}catch(e){console.error("אירעה שגיאה קריטית במהלך איסוף הפוסטים:",e);alert("אירעה שגיאה. בדוק את חלון המפתחים (F12) לפרטים נוספים.")}})();
-
בהמשך לתוסף כרום של @NH.LOCAL כאן ולסימניה של @יום_שמח כאן ששניהם מאפשרים ייצוא של שרשור שלם בפורמט JSON במתמחים, יצרתי כזו סימניה גם לפרוג.
הרבה מזה מבוסס ממש על הסימניה של @יום_שמח (אפילו העיצוב של החלונית אותו דבר) אז הקרדיט מגיע לו.
האמת היא שאפשר אולי לאחד את שני הסימניות יחסית בקלות, אבל אין לי כח כרגע, אם מישהו מעוניין הוא מוזמן בכבוד.
@NH.LOCAL אתה יכול להוסיף גם את זה לתוסף
אני אישית לא משתמש אף פעם בפרוג, ובטח לא נצרך לייצוא של השרשורים שם, אז אין לי איך לדעת אם זה עובד חוץ מהבדיקה הראשונית, אם יש באגים תדווחו.
javascript:(async()=>{const config={postSelector:"article.message[data-content]",authorSelector:"h4.message-name a.username",timeSelector:"time[data-time]",contentSelector:".bbWrapper",paginationSelector:".pageNav-main li:last-of-type a",titleSelector:".p-title-value",canonicalUrlSelector:"link[rel='canonical']",csrfTokenSelector:"input[name='_xfToken']"};const downloadJSON=(d,f)=>{const j=JSON.stringify(d,null,2);const b=new Blob([j],{type:"application/json"});const u=URL.createObjectURL(b);const a=document.createElement("a");a.href=u;a.download=f;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(u)};const copyJSON=async d=>{const j=JSON.stringify(d,null,2);try{await navigator.clipboard.writeText(j);alert("✅ הטקסט הועתק ללוח")}catch(e){console.error("שגיאה בהעתקה",e);alert("❌ נכשל בהעתקה ללוח")}};const loadScript=url=>new Promise((resolve,reject)=>{if(document.querySelector(`script[src="${url}"]`)){return resolve()}const script=document.createElement("script");script.src=url;script.onload=resolve;script.onerror=reject;document.head.appendChild(script)});const initTurndown=async()=>{if(typeof TurndownService==="undefined"){console.log("Turndown.js לא טעון, טוען כעת...");await loadScript("https://cdn.jsdelivr.net/npm/turndown@7.1.2/dist/turndown.min.js")}return new TurndownService({headingStyle:"atx",bulletListMarker:"*",codeBlockStyle:"fenced",emDelimiter:"*",strongDelimiter:"**",linkStyle:"inlined",hr:"---",preformattedCode:true})};const parsePostsFromDocument=docObject=>{const postsOnPage=[];const postElements=docObject.querySelectorAll(config.postSelector);postElements.forEach(p=>{const timeEl=p.querySelector(config.timeSelector);const authorEl=p.querySelector(config.authorSelector);const postData={author:authorEl?.innerText.trim()||p.getAttribute("data-author")||"unknown",dateTime:timeEl?.getAttribute("title")||null,contentHTML:p.querySelector(config.contentSelector)?.innerHTML.trim()||""};postsOnPage.push(postData)});return postsOnPage};let turndownService;try{const canonicalLink=document.querySelector(config.canonicalUrlSelector)?.href;if(!canonicalLink||!canonicalLink.includes("/threads/")){alert("נראה שאתה לא בעמוד של שרשור (thread). יש להפעיל את הסימניה מעמוד שרשור תקין.");return}if(document.querySelectorAll(config.postSelector).length===0){alert("❌ לא זוהו פוסטים בעמוד הנוכחי.\nייתכן שתבנית העיצוב של הפורום אינה נתמכת.");return}const csrfToken=document.querySelector(config.csrfTokenSelector)?.value;if(!csrfToken){alert("❌ לא נמצא טוקן אבטחה (CSRF) בעמוד. לא ניתן להמשיך.");return}console.log("זוהה טוקן אבטחה, מתחיל באיסוף...");turndownService=await initTurndown();let baseUrl=new URL(canonicalLink);baseUrl.search="";baseUrl.pathname=baseUrl.pathname.replace(/\/page-\d+(\/)?$/,'');const threadUrl=baseUrl.toString();console.log(`Base URL for requests: ${threadUrl}`);const threadIdMatch=threadUrl.match(/\.(\d+)\/?$/);const threadId=threadIdMatch?threadIdMatch[1]:Date.now();const threadTitle=(document.querySelector(config.titleSelector)?.innerText||"thread").replace(/[\/\\?%*:|"<>\s]/g,"_").toLowerCase();const fileName=`thread_${threadId}_${threadTitle}.json`;const lastPageLink=document.querySelector(config.paginationSelector);const pageCount=lastPageLink?parseInt(lastPageLink.innerText.replace(/,/g,""),10):1;console.log(`זוהו ${pageCount} עמודים בשרשור.`);const fetchPage=async pageNum=>{try{const targetUrl=new URL(threadUrl);if(targetUrl.pathname.endsWith('/')){targetUrl.pathname+=`page-${pageNum}`}else{targetUrl.pathname+=`/page-${pageNum}`};const bodyParams=new URLSearchParams();bodyParams.append('_xfToken',csrfToken);bodyParams.append('_xfResponseType','json');bodyParams.append('page',pageNum);bodyParams.append('_xfWithData','1');bodyParams.append('_xfRequestUri',targetUrl.pathname+targetUrl.search);bodyParams.append('_xfNoRedirect','1');console.log(`Requesting page ${pageNum} via POST to ${targetUrl.toString()}`);const response=await fetch(targetUrl.toString(),{method:'POST',headers:{"X-Requested-With":"XMLHttpRequest","Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"},body:bodyParams.toString(),credentials:'same-origin'});if(!response.ok){const errorText=await response.text();console.error(`Request for page ${pageNum} failed with status ${response.status}`,errorText);throw new Error(`Response not OK: ${response.status}`)}const data=await response.json();if(data.status==="error"){throw new Error(`Server returned error: ${data.errors?.[0]}`)}if(!data.html||!data.html.content){console.warn(`עמוד ${pageNum} החזיר מבנה JSON לא צפוי.`);return[]}const parser=new DOMParser();const doc=parser.parseFromString(data.html.content,"text/html");return parsePostsFromDocument(doc)}catch(err){console.error(`שגיאה בטעינת עמוד ${pageNum}:`,err);return[]}};let allRawPosts=[];if(pageCount===1){console.log("שרשור בן עמוד אחד, מאחזר פוסטים ישירות מהעמוד...");allRawPosts=parsePostsFromDocument(document)}else{console.log(`מאחזר פוסטים מ-${pageCount} עמודים...`);const pagePromises=Array.from({length:pageCount},(_,i)=>fetchPage(i+1));const pagesResult=await Promise.all(pagePromises);allRawPosts=pagesResult.flat()}if(allRawPosts.length===0){alert("❌ האיסוף הסתיים, אך לא נמצאו פוסטים תקינים.");return}const processedPosts=allRawPosts.filter(p=>p&&p.author).map(p=>({author:p.author,dateTime:p.dateTime,contentMarkdown:turndownService.turndown(p.contentHTML)}));const dlg=document.createElement("div");dlg.style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:16px;padding:24px;z-index:999999;width:320px;max-width:90%;text-align:center;font-family:Arial,sans-serif;box-shadow:0 8px 24px rgba(0,0,0,0.25);";dlg.innerHTML=`<h3 style="margin-top:0;color:#333;font-size:18px;">שמירת נתוני השרשור</h3><p style="font-size:14px;color:#555;word-break:break-word;">נאספו ועובדו ${processedPosts.length} פוסטים.</p><div style="display:flex;gap:10px;justify-content:center;margin-top:16px;"><button id="btnDownload" style="flex:1;padding:10px;border:none;border-radius:8px;background:#4CAF50;color:#fff;cursor:pointer;font-size:14px;">⬇️ הורדה</button><button id="btnCopy" style="flex:1;padding:10px;border:none;border-radius:8px;background:#2196F3;color:#fff;cursor:pointer;font-size:14px;">📋 העתקה</button></div><button id="btnClose" style="margin-top:14px;padding:8px 16px;border:none;border-radius:8px;background:#f44336;color:#fff;cursor:pointer;font-size:13px;">❌ סגור</button>`;document.body.appendChild(dlg);dlg.querySelector("#btnDownload").onclick=()=>{downloadJSON(processedPosts,fileName);document.body.removeChild(dlg)};dlg.querySelector("#btnCopy").onclick=()=>{copyJSON(processedPosts);document.body.removeChild(dlg)};dlg.querySelector("#btnClose").onclick=()=>{document.body.removeChild(dlg)}}catch(e){console.error("אירעה שגיאה קריטית במהלך איסוף הפוסטים:",e);alert("אירעה שגיאה. בדוק את חלון המפתחים (F12) לפרטים נוספים.")}})();
-
@ע-ה-דכו-ע עובד ממש טוב מכמה נסיונות, גם בשרשורים ממש ארוכים?
יש לך מושג איך זה עובד מאחורי הקלעים, או שזרקת הכל על ה-AI?
@NH.LOCAL כתב בלהורדה | סימניה לייצוא שרשורים שלמים מפורום פרוג לייבוא לAI וכדו':
@ע-ה-דכו-ע עובד ממש טוב מכמה נסיונות, גם בשרשורים ממש ארוכים?
עבד לי גם בשרשור עם 208 פוסטים בערך.
יש לך מושג איך זה עובד מאחורי הקלעים, או שזרקת הכל על ה-AI?
לXenForo אין כנראה API רשמי כ"כ, (לכה"פ לדעת AI STUDIO, לא ניסיתי לחפש לבד, האמת שאת זה היה אפשר לשאול את GPT הוא יותר יכול לדעת כאלו דברים).
עריכה: ודאי שיש API שניתן לשימוש, אבל רק עם מפתח API וכו' לא משהו לקהל הרחב.
האמת שגם השליחה של הבקשה בתור הדפדפן מצריכה טוקן אבטחה, אבל הסקריפט מושך את הטוקן מהדפדפן ומשתמש בו.
הסקריפט עובד ע"י שליחת בקשה כמו הדפדפן עצמו, מה שנקרא API לא רשמי.
-
@NH.LOCAL כתב בלהורדה | סימניה לייצוא שרשורים שלמים מפורום פרוג לייבוא לAI וכדו':
@ע-ה-דכו-ע עובד ממש טוב מכמה נסיונות, גם בשרשורים ממש ארוכים?
עבד לי גם בשרשור עם 208 פוסטים בערך.
יש לך מושג איך זה עובד מאחורי הקלעים, או שזרקת הכל על ה-AI?
לXenForo אין כנראה API רשמי כ"כ, (לכה"פ לדעת AI STUDIO, לא ניסיתי לחפש לבד, האמת שאת זה היה אפשר לשאול את GPT הוא יותר יכול לדעת כאלו דברים).
עריכה: ודאי שיש API שניתן לשימוש, אבל רק עם מפתח API וכו' לא משהו לקהל הרחב.
האמת שגם השליחה של הבקשה בתור הדפדפן מצריכה טוקן אבטחה, אבל הסקריפט מושך את הטוקן מהדפדפן ומשתמש בו.
הסקריפט עובד ע"י שליחת בקשה כמו הדפדפן עצמו, מה שנקרא API לא רשמי.
@ע-ה-דכו-ע כתב בלהורדה | סימניה לייצוא שרשורים שלמים מפורום פרוג לייבוא לAI וכדו':
עבד לי גם בשרשור עם 208 פוסטים בערך.
עבד לי עם 500+, גם בפורום לתורה
זה מעניין שזה עובד מהר יחסית, כי התוכן גם לא נגלל ונטען באותו דף, אלא דורש לעבור על עשרות דפיםג