better-ani-scraped 1.6.10 → 1.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCUMENTATION.md +37 -0
- package/examples/animesama/example_usage_getAllAnime.js +1 -1
- package/examples/animesama/example_usage_getAllTitleScans.js +10 -0
- package/examples/animesama/example_usage_getImgScans.js +10 -0
- package/examples/animesama/example_usage_getSeasons.js +2 -2
- package/package.json +1 -1
- package/scrapers/animesama.js +97 -31
- package/scrapers/scrapers.js +17 -0
package/DOCUMENTATION.md
CHANGED
|
@@ -36,6 +36,9 @@ const crunchyroll = new AnimeScraper('crunchyroll') //for Crunchyroll
|
|
|
36
36
|
- [getAllAnime](#animesamagetallanimewantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-page--null-output--anime_listjson-get_seasons--false)
|
|
37
37
|
- [getLatestEpisodes](#animesamagetlatestepisodeslanguagefilter--null)
|
|
38
38
|
- [getRandomAnime](#animesamagetrandomanimewantedlanguages--vostfr-vf-vastfr-wantedtypes--anime-film-maxattempts--null-attempt--0)
|
|
39
|
+
- [getAllTitleScans](#animesamagetalltitlescansmangaurl-numberimg--false)
|
|
40
|
+
- [getImgScans](#animesamagetimgscansmangaurl-wantedchapter)
|
|
41
|
+
|
|
39
42
|
|
|
40
43
|
### `animesama.searchAnime(query, limit = 10, wantedLanguages = ["vostfr", "vf", "vastfr"], wantedTypes = ["Anime", "Film"], page = null)`
|
|
41
44
|
Searches for anime titles that match the given query.
|
|
@@ -254,6 +257,40 @@ Fetches a random anime from the catalogue.
|
|
|
254
257
|
|
|
255
258
|
---
|
|
256
259
|
|
|
260
|
+
### `animesama.getAllTitleScans(mangaUrl, numberImg = false)`
|
|
261
|
+
Scrapes all the scans of a chapter.
|
|
262
|
+
|
|
263
|
+
- **Parameters:**
|
|
264
|
+
- `mangaUrl` *(string)*: The manga URL.
|
|
265
|
+
- `numberImg` *(boolean)*: If `true`, indicates the number of images in each chapter.
|
|
266
|
+
- **Returns:**
|
|
267
|
+
An array of chapter titles if *numberImg = false*
|
|
268
|
+
Else :
|
|
269
|
+
```js
|
|
270
|
+
{
|
|
271
|
+
scans :
|
|
272
|
+
[
|
|
273
|
+
{
|
|
274
|
+
title: string,
|
|
275
|
+
url: string,
|
|
276
|
+
},
|
|
277
|
+
...
|
|
278
|
+
]
|
|
279
|
+
mangaTitle: string,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
### `animesama.getImgScans(mangaUrl, wantedChapter)`
|
|
285
|
+
Scrapes all the scans of a chapter.
|
|
286
|
+
|
|
287
|
+
- **Parameters:**
|
|
288
|
+
- `mangaUrl` *(string)*: The manga URL.
|
|
289
|
+
- `wantedChapter` *(int)*: The number of the chapter you want.
|
|
290
|
+
- **Returns:**
|
|
291
|
+
An array of image URL.
|
|
292
|
+
|
|
293
|
+
---
|
|
257
294
|
|
|
258
295
|
## `AnimeScraper("animepahe")` methods
|
|
259
296
|
|
|
@@ -3,7 +3,7 @@ import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-s
|
|
|
3
3
|
const main = async () => {
|
|
4
4
|
const animesama = new AnimeScraper('animesama');
|
|
5
5
|
|
|
6
|
-
const catalogue = await animesama.getAllAnime(["vostfr", "vf", "vastfr"], ["Anime", "Film"],
|
|
6
|
+
const catalogue = await animesama.getAllAnime(["vostfr", "vf", "vastfr"], ["Anime", "Film", "Scans"], 1);
|
|
7
7
|
console.log(catalogue)
|
|
8
8
|
};
|
|
9
9
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-scraped';"
|
|
2
|
+
|
|
3
|
+
const main = async () => {
|
|
4
|
+
const animesama = new AnimeScraper('animesama');
|
|
5
|
+
const mangaUrl = "https://anime-sama.fr/catalogue/drcl-midnight-children/scan/vf";
|
|
6
|
+
const chapterTitles = await animesama.getAllTitleScans(mangaUrl, true);
|
|
7
|
+
console.log("Titles:", chapterTitles);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-scraped';"
|
|
2
|
+
|
|
3
|
+
const main = async () => {
|
|
4
|
+
const animesama = new AnimeScraper('animesama');
|
|
5
|
+
const scansUrl = "https://anime-sama.fr/catalogue/drcl-midnight-children/scan/vf";
|
|
6
|
+
const scansImgUrl = await animesama.getImgScans(scansUrl, 9);
|
|
7
|
+
console.log("Image scans:", scansImgUrl);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
main().catch(console.error);
|
|
@@ -2,9 +2,9 @@ import { AnimeScraper } from "../../index.js"; // REPLACE BY "from 'better-ani-s
|
|
|
2
2
|
|
|
3
3
|
const main = async () => {
|
|
4
4
|
const animesama = new AnimeScraper('animesama');
|
|
5
|
-
const animeUrl = "https://anime-sama.fr/catalogue/
|
|
5
|
+
const animeUrl = "https://anime-sama.fr/catalogue/one-piece";
|
|
6
6
|
|
|
7
|
-
const seasons = await animesama.getSeasons(animeUrl, ["vostfr", "vf"]);
|
|
7
|
+
const seasons = await animesama.getSeasons(animeUrl, ["vostfr", "vf"], ["Anime", "Scans"]);
|
|
8
8
|
console.log("Seasons:", seasons);
|
|
9
9
|
};
|
|
10
10
|
|
package/package.json
CHANGED
package/scrapers/animesama.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from "fs";
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { exec as execCallback } from 'child_process';
|
|
6
6
|
import { promisify } from 'util';
|
|
7
|
+
import { title } from "process";
|
|
7
8
|
const execAsync = promisify(execCallback);
|
|
8
9
|
|
|
9
10
|
|
|
@@ -124,15 +125,26 @@ export async function searchAnime(
|
|
|
124
125
|
return results;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "va", "vkr", "vcn", "vqc", "vf1", "vf2"]) {
|
|
128
|
+
export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "va", "vkr", "vcn", "vqc", "vf1", "vf2"], wantedTypes=["Anime", "Kai", "Scans"]) {
|
|
128
129
|
const res = await axios.get(animeUrl, { headers: getHeaders(CATALOGUE_URL) });
|
|
129
130
|
const html = res.data;
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
let mainAnimeOnly = html;
|
|
132
|
+
if (wantedTypes.length !== 0) {
|
|
133
|
+
if (!wantedTypes.includes("Anime")) {
|
|
134
|
+
mainAnimeOnly = mainAnimeOnly.replace(/<h2.*?>Anime<\/h2>[\s\S]*?(?=<h2|$)/g, '');
|
|
135
|
+
}
|
|
136
|
+
if (!wantedTypes.includes("Kai")) {
|
|
137
|
+
mainAnimeOnly = mainAnimeOnly.replace(/<h2.*?>Anime Version Kai<\/h2>[\s\S]*?(?=<h2|$)/g, '');
|
|
138
|
+
}
|
|
139
|
+
if (!wantedTypes.includes("Scans")) {
|
|
140
|
+
mainAnimeOnly = mainAnimeOnly.replace(/<h2.*?>Manga<\/h2>[\s\S]*?(?=<h2|$)/g, '');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
132
144
|
const $ = cheerio.load(mainAnimeOnly);
|
|
133
145
|
const scriptTags = $("script")
|
|
134
146
|
.toArray()
|
|
135
|
-
.filter(script => $(script).html().includes(
|
|
147
|
+
.filter(script => ["panneauAnime", "panneauScan"].some(str => $(script).html().includes(str)));
|
|
136
148
|
|
|
137
149
|
const animeName = animeUrl.split("/")[4];
|
|
138
150
|
let seasons = [];
|
|
@@ -140,7 +152,7 @@ export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "
|
|
|
140
152
|
for (const language of languagePriority) {
|
|
141
153
|
seasons = [];
|
|
142
154
|
let languageAvailable = false;
|
|
143
|
-
|
|
155
|
+
let scansLanguage = "";
|
|
144
156
|
for (let script of scriptTags) {
|
|
145
157
|
const content = $(script).html();
|
|
146
158
|
|
|
@@ -148,33 +160,49 @@ export async function getSeasons(animeUrl, languagePriority = ["vostfr", "vf", "
|
|
|
148
160
|
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
149
161
|
.replace(/\/\/.*$/gm, "");
|
|
150
162
|
|
|
151
|
-
const matches = [...uncommentedContent.matchAll(/panneauAnime\("([^"]+)", "([^"]+)"\);/g)];
|
|
163
|
+
const matches = [...uncommentedContent.matchAll(/(panneauAnime|panneauScan)\("([^"]+)", "([^"]+)"\);/g)];
|
|
152
164
|
|
|
153
165
|
for (let match of matches) {
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
const type = match[1];
|
|
167
|
+
const title = match[2];
|
|
168
|
+
const href = match[3].split("/")[0];
|
|
169
|
+
|
|
170
|
+
if (type === "panneauScan") {
|
|
171
|
+
let found = false;
|
|
172
|
+
for (const lang of languagePriority) {
|
|
173
|
+
const fullUrl = `${CATALOGUE_URL}/${animeName}/${href}/${lang}`;
|
|
174
|
+
try {
|
|
175
|
+
const check = await axios.head(fullUrl, { headers: getHeaders(animeUrl) });
|
|
176
|
+
if (check.status === 200) {
|
|
177
|
+
seasons.push({ title, url: fullUrl });
|
|
178
|
+
scansLanguage = lang
|
|
179
|
+
found = true;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
} catch {}
|
|
165
183
|
}
|
|
166
|
-
|
|
167
|
-
|
|
184
|
+
if (found) languageAvailable = true;
|
|
185
|
+
} else {
|
|
186
|
+
const fullUrl = `${CATALOGUE_URL}/${animeName}/${href}/${language}`;
|
|
187
|
+
try {
|
|
188
|
+
const check = await axios.head(fullUrl, { headers: getHeaders(animeUrl) });
|
|
189
|
+
if (check.status === 200) {
|
|
190
|
+
seasons.push({ title, url: fullUrl });
|
|
191
|
+
languageAvailable = true;
|
|
192
|
+
}
|
|
193
|
+
} catch {}
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
196
|
}
|
|
171
|
-
|
|
172
|
-
|
|
197
|
+
if (wantedTypes.includes("Scans") && wantedTypes.length==1 && scansLanguage) {
|
|
198
|
+
return { scansLanguage, seasons };
|
|
199
|
+
}
|
|
200
|
+
else if (languageAvailable) {
|
|
173
201
|
return { language, seasons };
|
|
174
202
|
}
|
|
175
203
|
}
|
|
176
204
|
|
|
177
|
-
return
|
|
205
|
+
return [];
|
|
178
206
|
}
|
|
179
207
|
|
|
180
208
|
|
|
@@ -417,28 +445,37 @@ export async function getAllAnime(
|
|
|
417
445
|
const url = pageNum === 1 ? CATALOGUE_URL : `${CATALOGUE_URL}?page=${pageNum}`;
|
|
418
446
|
const res = await axios.get(url, { headers: getHeaders(CATALOGUE_URL) });
|
|
419
447
|
const $ = cheerio.load(res.data);
|
|
420
|
-
|
|
448
|
+
|
|
421
449
|
const containers = $("div.shrink-0.m-3.rounded.border-2");
|
|
422
|
-
|
|
450
|
+
|
|
423
451
|
containers.each((_, el) => {
|
|
424
452
|
const anchor = $(el).find("a");
|
|
425
453
|
const title = anchor.find("h1").text().trim();
|
|
426
454
|
const link = anchor.attr("href");
|
|
427
455
|
const img = anchor.find("img").attr("src");
|
|
428
|
-
|
|
456
|
+
|
|
429
457
|
const paragraphs = anchor.find("p").toArray().map(p => $(p).text().trim());
|
|
430
|
-
|
|
458
|
+
|
|
431
459
|
const altTitles = paragraphs[0] ? paragraphs[0].split(',').map(name => name.trim()) : [];
|
|
432
460
|
const genres = paragraphs[1] ? paragraphs[1].split(',').map(genre => genre.trim()) : [];
|
|
433
461
|
const type = paragraphs[2] ? paragraphs[2].split(',').map(t => t.trim()) : [];
|
|
434
462
|
const language = paragraphs[3] ? paragraphs[3].split(',').map(lang => lang.trim()) : [];
|
|
435
|
-
|
|
436
|
-
const
|
|
463
|
+
|
|
464
|
+
const filteredTypes = wantedTypes.length === 0
|
|
465
|
+
? type
|
|
466
|
+
: type.filter(t => isWanted(t, wantedTypes));
|
|
467
|
+
|
|
468
|
+
const hasScans = filteredTypes.some(t => t.toLowerCase() === "scans".toLowerCase());
|
|
469
|
+
|
|
470
|
+
const filteredLanguages = (wantedLanguages.length === 0 || hasScans)
|
|
471
|
+
? language
|
|
472
|
+
: language.filter(lang => isWanted(lang, wantedLanguages));
|
|
473
|
+
|
|
437
474
|
if (
|
|
438
475
|
title &&
|
|
439
476
|
link &&
|
|
440
477
|
filteredTypes.length > 0 &&
|
|
441
|
-
filteredLanguages.length > 0
|
|
478
|
+
(filteredLanguages.length > 0 || hasScans)
|
|
442
479
|
) {
|
|
443
480
|
const fullUrl = link.startsWith("http") ? link : `${BASE_URL}${link}`;
|
|
444
481
|
animeLinks.push({
|
|
@@ -452,10 +489,10 @@ export async function getAllAnime(
|
|
|
452
489
|
});
|
|
453
490
|
}
|
|
454
491
|
});
|
|
455
|
-
|
|
492
|
+
|
|
456
493
|
return containers.length > 0;
|
|
457
494
|
};
|
|
458
|
-
|
|
495
|
+
|
|
459
496
|
const enrichWithSeasons = async (list) => {
|
|
460
497
|
for (const anime of list) {
|
|
461
498
|
try {
|
|
@@ -609,3 +646,32 @@ export async function getRandomAnime(
|
|
|
609
646
|
return null;
|
|
610
647
|
}
|
|
611
648
|
}
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
export async function getAllTitleScans(mangaUrl, numberImg = false) {
|
|
652
|
+
const res = await axios.get(mangaUrl, { headers: getHeaders() });
|
|
653
|
+
const $ = cheerio.load(res.data);
|
|
654
|
+
|
|
655
|
+
const title = encodeURIComponent($("#titreOeuvre").text().trim());
|
|
656
|
+
const urlInfo = `https://anime-sama.fr/s2/scans/get_nb_chap_et_img.php?oeuvre=${title}`
|
|
657
|
+
const infoPage = await axios.get(urlInfo, { headers: getHeaders() });
|
|
658
|
+
const titleChapter = Object.keys(infoPage.data)
|
|
659
|
+
|
|
660
|
+
if (numberImg) {
|
|
661
|
+
return {scans :infoPage.data, mangaTitle :title}
|
|
662
|
+
} else {
|
|
663
|
+
return titleChapter
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export async function getImgScans(mangaUrl, wantedChapter) {
|
|
668
|
+
const infoScan = await getAllTitleScans(mangaUrl, true)
|
|
669
|
+
const numberImg = infoScan.scans[wantedChapter.toString()];
|
|
670
|
+
const mangaTitle = infoScan.mangaTitle
|
|
671
|
+
const imgUrls = [];
|
|
672
|
+
for (let i = 1; i <= numberImg; i++) {
|
|
673
|
+
imgUrls.push(`https://anime-sama.fr/s2/scans/${mangaTitle}/${wantedChapter}/${i}.jpg`);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return imgUrls
|
|
677
|
+
}
|
package/scrapers/scrapers.js
CHANGED
|
@@ -104,4 +104,21 @@ export class AnimeScraper {
|
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
async getAllTitleScans(mangaUrl, ...rest) {
|
|
109
|
+
try {
|
|
110
|
+
return await this.source.getAllTitleScans(mangaUrl, ...rest);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error(`This scraper does not have the getAllTitleScans function implemented or an error happened -> ${error}`);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async getImgScans(mangaUrl, ...rest) {
|
|
117
|
+
try {
|
|
118
|
+
return await this.source.getImgScans(mangaUrl, ...rest);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`This scraper does not have the getImgScans function implemented or an error happened -> ${error}`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
107
124
|
}
|