const { BaseScraper } = require('./scraper.js'); const sharp = require('sharp'); const fs = require('fs'); const path = require('path'); class ColaMangaScraper extends BaseScraper { constructor() { super(); } async saveBufferAsWebp(buffer, filename, dir = '.') { const dirPath = path.resolve(dir); const filePath = path.join(dirPath, filename); try { await fs.promises.mkdir(dirPath, { recursive: true }); await sharp(buffer).webp({ quality: 80 }).toFormat('webp').toFile(filePath); } catch (error) { console.error(`Failed to save ${filename}:`, error); } } async getMangaInfo(mangaUrl) { const page = await this.loadPage(mangaUrl); await page.waitForSelector('.fed-deta-info', { visible: true }); const mangaName = await page.$eval('.fed-deta-content h1', el => el.textContent); const elements = await page.$$('.fed-deta-content li'); const mangaInfo = { name: mangaName, author: '', nickNames: [], genres: [], status: '', chapters: [] }; for (const el of elements) { const span = await el.$eval('span', el => el.textContent.trim()); if (span === '状态') { mangaInfo.status = await el.$eval('a', el => el.textContent); } else if (span === '作者') { mangaInfo.author = await el.$eval('a', el => el.textContent); } else if (span === '别名') { mangaInfo.nickNames = await el.$$eval('a', els => els.map(el => el.textContent)); } else if (span === '类别') { mangaInfo.genres = await el.$$eval('a', els => els.map(el => el.textContent)); } } const chapterElements = await page.$$('.all_data_list li'); mangaInfo.chapters = await Promise.all(chapterElements.map(async el => { const chapterName = await el.$eval('a', el => el.textContent); const chapterUrl = await el.$eval('a', el => el.getAttribute('href')); return { name: chapterName, url: chapterUrl }; })); while (!this.pages_response[this.pages.indexOf(page)]) { await new Promise(resolve => setTimeout(resolve, 100)); } mangaInfo.coverPic = this.pages_response[this.pages.indexOf(page)]; await this.closePage(page); return mangaInfo; } async downloadChapterPics(chapter, chapterDir = ".") { const directoryPath = path.resolve(chapterDir); if (fs.existsSync(directoryPath)) { console.log(`Skipping ${chapter.name} as it already exists`); return; } fs.mkdirSync(directoryPath, { recursive: true }); const page = await this.loadPage(chapter.url); const pageIndex = this.pages.indexOf(page); await page.waitForSelector('.mh_mangalist', { visible: true }); for (let attempt = 0; attempt < 10; attempt++) { console.log(`Downloading ${chapter.name}, attempt ${attempt + 1}`); await this.scrollPage(page); const loadingElements = await page.$$eval('.mh_loading:not([style*="display: none"])', elements => elements.length); await new Promise(resolve => setTimeout(resolve, 1000)); if (loadingElements === 0 && Object.keys(this.pages_response[pageIndex]).length !== 0) { break; } } const responses = this.pages_response[pageIndex]; for (const [url, response] of Object.entries(responses)) { const fileName = (await this.getImgOrder(page, url)) + '.webp'; const buffer = await (new Blob([response], { type: 'image/webp' })).arrayBuffer(); await this.saveBufferToWebp(buffer, fileName, chapterDir); } await this.closePage(page); } async downloadChapter(chapters, dir = ".") { const dirPath = path.resolve(dir); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } // const chapter = chapters[Math.floor(Math.random() * chapters.length)]; for (const chapter of chapters) { await this.downloadChapterPics(chapter, path.join(dir, chapter.name)); } } async getImgOrder(page, src) { const loadingAttributes = await page.$$eval('.mh_comicpic', (elements, src) => { return elements .filter(el => el.querySelector(`img[src="${src}"]`)) .map(el => el.getAttribute('p')) .map(str => str.padStart(elements.length.toString().length, '0')); }, src); return loadingAttributes; } } (async () => { const scraper = new ColaMangaScraper(); await scraper.init(); const mangaUrl = 'https://www.colamanga.com/manga-od825111/'; const mangaInfo = await scraper.getMangaInfo(mangaUrl); await scraper.saveBufferToWebp(mangaInfo.coverPic, 'cover.webp', 'test'); await scraper.downloadChapter(mangaInfo.chapters, 'test'); await scraper.closeAllPages(); console.log(mangaInfo); })();