Fitur:
· home, list, completed, manhwaRaw, genre, search, author, artist, detail, chapter · Pagination + filter (latest, alphabet, rating, trending) · Chapter list lengkap (semua chapter per slug) · Rating fix di semua halaman · 20+ User-Agent, retry, proxy support
Note
jan lupa npm install axios cheerio
qrtz
javascript
5
9
https://manga18.me
24 Jun 2026
Code
const axios = require('axios');
const cheerio = require('cheerio');
const https = require('https');
const { randomInt } = require('crypto');
const USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0",
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Android 14; Mobile; rv:133.0) Gecko/133.0 Firefox/133.0",
"Mozilla/5.0 (Android 14; Mobile; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Vivaldi/6.9.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Vivaldi/6.8.0.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Vivaldi/6.9.0.0",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0",
];
const BASE = "https://manga18.me";
const MAX_RETRIES = 5;
const RETRY_DELAY = 1000;
class Manga18 {
constructor(options = {}) {
this.proxy = options.proxy || process.env.HTTP_PROXY || process.env.https_proxy || null;
this.timeout = options.timeout || 30000;
this.agent = new https.Agent({ rejectUnauthorized: false, keepAlive: true });
}
_ua() {
return USER_AGENTS[randomInt(0, USER_AGENTS.length)];
}
_headers(referer = BASE + "/") {
const ua = this._ua();
return {
"User-Agent": ua,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Cache-Control": "max-age=0",
"Sec-Ch-Ua": '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": '"Windows"',
"Referer": referer,
"Origin": BASE,
};
}
_getProxyConfig() {
if (!this.proxy) return {};
const url = new URL(this.proxy);
return {
proxy: {
host: url.hostname,
port: parseInt(url.port, 10),
auth: url.username && url.password ? { username: url.username, password: url.password } : undefined,
protocol: url.protocol.replace(":", ""),
},
};
}
async _fetch(url, referer = BASE + "/", retries = MAX_RETRIES) {
for (let i = 0; i < retries; i++) {
try {
const config = {
headers: this._headers(referer),
timeout: this.timeout,
withCredentials: true,
httpsAgent: this.agent,
...this._getProxyConfig(),
};
const res = await axios.get(url, config);
if (res.status === 403 || res.status === 503) throw new Error(`Blocked (${res.status})`);
return res.data;
} catch (e) {
if (i === retries - 1) throw e;
const delay = RETRY_DELAY * Math.pow(2, i) + randomInt(0, 500);
await new Promise(r => setTimeout(r, delay));
}
}
}
_cleanUrl(url) {
if (!url) return null;
if (url.startsWith("//")) return "https:" + url;
if (url.startsWith("/")) return BASE + url;
if (url.startsWith("http")) return url;
return null;
}
_extractMangaItems($, container = ".page-item-detail") {
const items = [];
$(container).each((i, el) => {
const $el = $(el);
const title = $el.find(".item-title a, .title a, h3 a, .manga-title a").first().text().trim();
const link = $el.find(".item-title a, .title a, h3 a, .manga-title a").first().attr("href");
const thumbnail = $el.find(".item-thumb img, .thumb img, .post-thumbnail img").first().attr("src") || "";
let rating = null;
const ratingSelectors = [
".item-rate .num",
".rating .num",
".score .num",
".rate .num",
".my-rating .num",
".item-rate",
".rating",
".score",
".rate",
".my-rating",
".average",
".avg"
];
for (const sel of ratingSelectors) {
const elRating = $el.find(sel).first();
if (elRating.length) {
const text = elRating.text().trim();
const numMatch = text.match(/([\d.]+)/);
if (numMatch) {
rating = parseFloat(numMatch[1]);
break;
}
}
}
if (rating === null) {
const parentText = $el.text().trim();
const match = parentText.match(/Average\s*([\d.]+)/i) || parentText.match(/([\d.]+)\s*\/\s*5/);
if (match) rating = parseFloat(match[1]);
}
const chapters = $el.find(".item-chapters a, .chapter a, .meta-chapter a").last().text().trim() || "";
const slug = link ? link.replace("/manga/", "").replace("/", "") : "";
if (title) {
items.push({
title,
slug,
link: this._cleanUrl(link),
thumbnail: this._cleanUrl(thumbnail),
rating,
latestChapter: chapters,
});
}
});
return items;
}
async _extractDetail(slug) {
const url = `${BASE}/manga/${slug}`;
const html = await this._fetch(url);
const $ = cheerio.load(html);
const metaDesc = $('meta[name="description"]').attr("content") || "";
const ogDesc = $('meta[property="og:description"]').attr("content") || "";
const summary = (metaDesc || ogDesc || "").replace(/\s+/g, " ").trim();
const title = ($('meta[property="og:title"]').attr("content") || $("h1").first().text().trim() || "")
.replace(/\s*\|\s*Manga18\.ME$/, "").trim();
const thumbnail = $('meta[property="og:image"]').attr("content") || "";
const author = $('.summary-content a[href*="/author/"]').first().text().trim() || "";
const artist = $('.summary-content a[href*="/artist/"]').first().text().trim() || "";
let rating = null;
$(".summary-content").each((i, el) => {
const text = $(el).text().trim();
if (text.includes("Average")) {
const match = text.match(/Average\s*([\d.]+)/i);
if (match) rating = parseFloat(match[1]);
}
});
if (!rating) {
const ratingText = $(".item-rate .num, .rating .num, .score .num").first().text().trim();
if (ratingText) rating = parseFloat(ratingText);
}
const genres = [];
$('.summary-content a[href*="/genre/"]').each((i, el) => {
const text = $(el).text().trim();
if (text && !genres.includes(text) && !text.includes("orderby")) genres.push(text);
});
let releaseDate = null, updateDate = null;
$('script[type="application/ld+json"]').each((i, el) => {
try {
const data = JSON.parse($(el).html());
if (data.datePublished) releaseDate = data.datePublished;
if (data.dateModified) updateDate = data.dateModified;
} catch(e) {}
});
const chapters = [];
const seen = new Set();
const targetSlug = `/${slug}/chapter-`;
$('a[href*="/chapter-"]').each((i, el) => {
const href = $(el).attr("href");
const text = $(el).text().trim();
if (href && href.includes(targetSlug) && !text.includes("Read First") && !text.includes("Read Last")) {
const num = href.match(/chapter-(\d+)/i)?.[1] || "";
if (num && !seen.has(num)) {
seen.add(num);
chapters.push({
number: num,
title: text || `Chapter ${num}`,
link: this._cleanUrl(href),
});
}
}
});
chapters.sort((a, b) => parseInt(a.number) - parseInt(b.number));
return {
title,
thumbnail: this._cleanUrl(thumbnail),
summary,
author,
artist,
rating,
genres,
releaseDate,
updateDate,
totalChapters: chapters.length,
chapters,
};
}
_extractChapterImages($) {
const images = [];
$('img[src*=".jpg"], img[src*=".png"], img[src*=".webp"]').each((i, el) => {
const src = $(el).attr("src");
if (src && !src.includes("logo") && !src.includes("icon") && !src.includes("banner") && !src.includes("avatar") && !src.includes("thumbnail")) {
images.push(this._cleanUrl(src));
}
});
const prevChapter = $('a:contains("Prev"), a:contains("Previous")').first().attr("href") || null;
const nextChapter = $('a:contains("NEXT"), a:contains("Next")').first().attr("href") || null;
return {
images: images.filter(Boolean),
prevChapter: this._cleanUrl(prevChapter),
nextChapter: this._cleanUrl(nextChapter),
};
}
async _genericList(path, page = 1, orderby = "") {
let url = BASE + path;
if (page > 1) url += "/" + page;
if (orderby) url += (url.includes("?") ? "&" : "?") + "orderby=" + orderby;
const html = await this._fetch(url);
const $ = cheerio.load(html);
const items = this._extractMangaItems($);
const total = items.length;
const hasNext = $(`.pagination a[href*="${path}/${page + 1}"]`).length > 0;
return {
creator: "rynaqrtz",
page: path.replace("/", ""),
currentPage: page,
total,
hasNext,
orderby: orderby || "default",
items,
};
}
async home() {
const html = await this._fetch(BASE + "/");
const $ = cheerio.load(html);
const popularItems = this._extractMangaItems($, ".page-item-detail");
let latestItems = this._extractMangaItems($, ".page-item-detail:not(.popular)");
if (latestItems.length === 0) latestItems = popularItems;
return {
creator: "rynaqrtz",
page: "home",
popular: popularItems.slice(0, 24),
latest: latestItems.slice(0, 24),
};
}
async list(page = 1, orderby = "") {
return this._genericList("/manga", page, orderby);
}
async completed(page = 1, orderby = "") {
return this._genericList("/completed", page, orderby);
}
async manhwaRaw(page = 1, orderby = "") {
return this._genericList("/manhwa-raw", page, orderby);
}
async genre(slug, page = 1, orderby = "") {
let url = BASE + "/genre/" + slug;
if (page > 1) url += "/" + page;
if (orderby) url += (url.includes("?") ? "&" : "?") + "orderby=" + orderby;
const html = await this._fetch(url);
const $ = cheerio.load(html);
const items = this._extractMangaItems($);
const total = items.length;
const hasNext = $(`.pagination a[href*="/genre/${slug}/${page + 1}"]`).length > 0;
return {
creator: "rynaqrtz",
page: "genre",
genre: slug,
currentPage: page,
total,
hasNext,
orderby: orderby || "default",
items,
};
}
async search(query, limit = 20) {
const url = BASE + "/search?q=" + encodeURIComponent(query);
const html = await this._fetch(url);
const $ = cheerio.load(html);
let items = this._extractMangaItems($);
if (limit > 0) items = items.slice(0, limit);
return {
creator: "rynaqrtz",
page: "search",
query,
total: items.length,
items,
};
}
async author(slug) {
const url = BASE + "/author/" + slug;
const html = await this._fetch(url);
const $ = cheerio.load(html);
const name = $("h1").first().text().trim().replace("Author: ", "").trim();
const items = this._extractMangaItems($);
return {
creator: "rynaqrtz",
page: "author",
name,
slug,
total: items.length,
items,
};
}
async artist(slug) {
const url = BASE + "/artist/" + slug;
const html = await this._fetch(url);
const $ = cheerio.load(html);
const name = $("h1").first().text().trim().replace("Artist: ", "").trim();
const items = this._extractMangaItems($);
return {
creator: "rynaqrtz",
page: "artist",
name,
slug,
total: items.length,
items,
};
}
async detail(slug) {
const data = await this._extractDetail(slug);
return {
creator: "rynaqrtz",
page: "detail",
slug,
...data,
};
}
async chapter(slug, number) {
const url = BASE + "/manga/" + slug + "/chapter-" + number;
const html = await this._fetch(url);
const $ = cheerio.load(html);
const title = $('meta[property="og:title"]').attr("content") || $("title").text().trim() || "";
const images = this._extractChapterImages($);
return {
creator: "rynaqrtz",
page: "chapter",
mangaSlug: slug,
chapterNumber: number,
title: title.replace(/\s*\|\s*Manga18\.ME$/, "").trim(),
...images,
};
}
}
// ---- CLI ----
function runCLI() {
const args = process.argv.slice(2);
const cmd = args[0];
const params = args.slice(1);
let proxy = null;
const proxyIdx = params.indexOf('--proxy');
if (proxyIdx !== -1) {
proxy = params[proxyIdx + 1];
params.splice(proxyIdx, 2);
}
const scraper = new Manga18({ proxy });
const commands = {
home: () => scraper.home(),
list: () => scraper.list(parseInt(params[0]) || 1, params[1] || ''),
completed: () => scraper.completed(parseInt(params[0]) || 1, params[1] || ''),
manhwaRaw: () => scraper.manhwaRaw(parseInt(params[0]) || 1, params[1] || ''),
genre: () => {
if (!params[0]) throw new Error('Usage: genre <slug> [page] [orderby]');
return scraper.genre(params[0], parseInt(params[1]) || 1, params[2] || '');
},
search: () => {
const q = params.join(' ');
if (!q) throw new Error('Usage: search <query>');
return scraper.search(q);
},
author: () => {
if (!params[0]) throw new Error('Usage: author <slug>');
return scraper.author(params[0]);
},
artist: () => {
if (!params[0]) throw new Error('Usage: artist <slug>');
return scraper.artist(params[0]);
},
detail: () => {
if (!params[0]) throw new Error('Usage: detail <slug>');
return scraper.detail(params[0]);
},
chapter: () => {
if (!params[0] || !params[1]) throw new Error('Usage: chapter <slug> <number>');
return scraper.chapter(params[0], params[1]);
}
};
if (!cmd || !commands[cmd]) {
console.error(`Usage: node manga18.js [${Object.keys(commands).join('|')}] [...args]`);
console.error(' node manga18.js home');
console.error(' node manga18.js list [page] [orderby]');
console.error(' node manga18.js completed [page] [orderby]');
console.error(' node manga18.js manhwaRaw [page] [orderby]');
console.error(' node manga18.js genre <slug> [page] [orderby]');
console.error(' node manga18.js search <query>');
console.error(' node manga18.js author <slug>');
console.error(' node manga18.js artist <slug>');
console.error(' node manga18.js detail <slug>');
console.error(' node manga18.js chapter <slug> <number>');
console.error(' node manga18.js --proxy http://user:pass@proxy:port');
process.exit(1);
}
commands[cmd]()
.then(result => console.log(JSON.stringify(result, null, 2)))
.catch(err => {
console.error('[error]', err.message);
process.exit(1);
});
}
if (require.main === module) {
runCLI();
}
module.exports = Manga18;Rating
Gimana snippet ini menurutmu?
Embed ke website / blog
<iframe src="https://qrtzcode.vercel.app/api/embed/manga/manga18" style="width:100%;height:400px;border:none;border-radius:12px;"></iframe>