2022-05-19に更新

漫画Bank / La « mangabank.org » a disparu.

読了目安:15分

À propos de ce qu'était une banque de mangas.

La « mangabank.org » a disparu.
C'est devenu un nouveau site illégal appelé 13dl.me, mais depuis que le style de lecture en ligne a été abandonné et qu'il est devenu un type de portée de téléchargement, cet article a également été mis à jour sur le tableau, comme une nouvelle étape.

Fr:La « mangabank.org » a disparu.
https://rentry.co/hzpue

Eng:The "mangabank.org" has disappeared.
https://rentry.co/vh3py

Русский:«mangabank.org» исчез.
https://rentry.co/oq975

Spanish:El "mangabank.org" ha desaparecido.
https://rentry.co/agr95

Ja
https://rentry.co/mangabankdatta

漫画バンク、だったものについて。

漫画バンクは消えた。
新たに13dl.meという違法サイトになったが、これまでのオンラインリーディング型のスタイルを捨てて、ダウンロードリーチ型になったので、この記事もボードで更新していたのを、新たに節目としてこのようにしてみた。

過去のボード
mangaBank が消えるまで
https://crieit.net/boards/manga-B

さて、Javascript ってどうやって書くんだろうと前々から気になっていたので、この新たな違法サイトについて、どういうものか見てみるのに Javascriptnode.js を使ってプログラムを書いてみた。

Promise についてよくわからなかったので、ずいぶん時間がかかったが、Nim よりは Javascript について書かれたドキュメントは数多くあるので、プログラムが一応動くというところまでは早かった。

node.js をインストールして、npm パッケージマネージャで axios, cheerioをインストールする。

https://nodejs.org/ja/

https://github.com/axios/axios#installing

https://cheerio.js.org/

axios が httpsリクエストするモジュールで、Promise 型の返り値で、リクエストが成功するとresponse を処理できる。cheerio は、html テキストをオブジェクトにして css セレクターとして使う。Ruby での nokogiri に類する、pythonでの bueatifulsoup4 のような、go での goquery のような、Nim での nimquery のような扱いで使用した。こういう便利セレクターはそれぞれに使い方が似ていて違うので、よく調べないとちゃんと使えないということに慣れていない場合、使わなくても正規表現でもなんとかなるので、その場合は言語が違っても、同じ標準的な正規表現スタイルの場合は同じように書ける。go にも標準ライブラリではないけど perl 様式の正規表現モジュールはある。

RE2 is a fast, safe, thread-friendly alternative to backtracking regular expression engines like those used in PCRE, Perl, and Python. It is a C++ library.

License
BSD-3-Clause license
https://github.com/google/re2/

星の高さ正規表現入門

1. Base

Javascript

const cheerio = require('cheerio'),
    axios = require('axios');
//  url = `https://13dl.me/`;

var url = `https://13dl.me/list/popular/`;

var counter = 0;

function recursive(url){
    let temp_url = url;
    axios.get(url)
    .then((response) => {
        if (response.status == 200){
            let $ = cheerio.load(response.data);
            $('a').each(function (i, e) {
                let title = $(e).attr('title');      
                let link = $(e).attr('href');      
                if (title !== undefined){
                    let h = /^Home/.test(title),
                        po = /^Popular\s/.test(title),
                        p = /^Prev/.test(title),
                        pa = /^Page/.test(title),
                        n = /^Next/.test(title);
                    if (h || po || p || pa || n || title === ``){
                        //unless
                    } else {
                        counter++;
                        console.log(counter + `{` + title +`{` + link);
//                      console.log(counter,title);
//                      console.log(`____________________`);
                    }
                }
                if (title === `Next`){
                    url = link;
//                    recursive(url);
                }
            })
            if (url !== temp_url){
                    recursive(url);
            }
        }
    }).catch(function (e) {
        console.log(e);
        recursive(url);
    });
}

recursive(url);

どういうプログラムかというと、サイトにあるコンテンツを列挙していくものだ。
関数を再帰的に呼ぶのだけど、Javascript でこういう書き方するのかは、しらないまま書いたら、一応動いたので、列挙していくコンテンツを SQLite データベースに記録しよう。awk と併用すればこの base プログラムでも SQLite3 のデータベースファイルは作れますが。区切り文字 {csv にして、SQLite3 で csv を読み込んで保存すればデータベースファイルになる。

Ruby

似た感じで Ruby で書くと

require 'nokogiri'
require 'open-uri'

url = 'https://13dl.me/list/popular/'
counter = 0
threads = []

def recursive(counter,url,threads) 
  html = URI.open(url).read
  doc = Nokogiri::HTML.parse(html)
  doc.css('a').each do |x|
    xx = x.attr('title')
    pa = /^Page/.match?("#{xx}")
    p = /^Prev/.match?("#{xx}")

    if xx && xx !='' &&  xx !='Home' && xx !='Popular Manga' then

      unless pa | p then
        n = /^Next/.match?("#{xx}")
        link = ''
        if n then
          link = x.attr('href')
          threads << Thread.new do
            recursive(counter,link,threads)
          end
        else
          counter += 1
          puts "#{counter} #{xx}"
        end
      end

    end

  end
end

recursive(counter,url,threads)

threads.each(&:join)

python

recursive
https://rentry.co/5ibqk

while
https://rentry.co/ir4b3

Nim

while
https://rentry.co/856s2

threadpool ... Work in Progress
https://rentry.co/ze8f4

perl5

https://rentry.co/perl5_manga

Go

recursive
https://rentry.co/f55r8

while ( for )
https://rentry.co/75gch

&sync.wait.Group{}, sync.Mutex, channel ... Work in Progress とってもスピーディー
https://rentry.co/wikwa

contents 数とpage 数の関係 | channel, sync.Mutex
https://rentry.co/o8fp4

Elixir

https://rentry.co/nzo6g

2. SQLite3

Javascript

const cheerio = require('cheerio'), 
    axios = require('axios');
const sqlite3 = require('sqlite3').verbose();

const db = new sqlite3.Database('13dlme0.db',(err) => {
    if(err) {
        return console.error(err.message);
    }
    console.log('Connect to SQLite database.');
});

db.serialize(() => {
    db.run(`CREATE TABLE manga(id interger,title string,link string)`)
});

var url = `https://13dl.me/list/popular/`;

var counter = 0;
function recursive(url){
    let temp_url = url;
    axios.get(url)
    .then((response) => {
        if (response.status == 200){
            let $ = cheerio.load(response.data);
            $('a').each(function (i, e) {
                let title = $(e).attr('title');      
                let link = $(e).attr('href');      
                if (title !== undefined){
                    let h = /^Home/.test(title),
                        po = /^Popular\s/.test(title),
                        p = /^Prev/.test(title),
                        pa = /^Page/.test(title),
                        n = /^Next/.test(title);
                    if (h || po || p || pa || n || title === ``){
                        //unless
                    } else {
                        counter++;
//                        console.log(counter + ` ** ` + title + ` ** ` + link);
                        db.run(`insert into manga(id,title) VALUES(?,?)`,[counter,title]);
                        console.log(counter,title);
                        console.log(`____________________`);
                    }
                }
                if (title === `Next`){
                    url = link;
                }
            })
            if (temp_url != url){
                recursive(url);
            };
        }
    }).catch(function (e) {
        console.log(e);
        recursive(url);
    });
}
recursive(url);

db.close()
してない。
どうやって close() すべきだろうか ?

3. SQLite3 + db.close()

Javascript

const cheerio = require('cheerio'), 
    axios = require('axios');
const sqlite3 = require('sqlite3').verbose();

const db = new sqlite3.Database('13dlme_test1-1.db',(err) => {
    if(err) {
        return console.error(err.message);
    }
    console.log('Connect to SQLite database.');
});

db.serialize(() => {
    db.run(`CREATE TABLE manga(id interger,title string,link string)`)
});

var url = `https://13dl.me/list/popular/`;
var counter = 0;
function recursive(url){
    const temp_url = url;
    const promise1 = axios.get(url)
    const promiseData1 = promise1.then((response) => {
        if (response.status == 200){
            let $ = cheerio.load(response.data);
            $('a').each(function (i, e) {
                const title = $(e).attr('title');      
                const link = $(e).attr('href');      
                if (title !== undefined){
                    const h = /^Home/.test(title),
                        po = /^Popular\s/.test(title),
                        p = /^Prev/.test(title),
                        pa = /^Page/.test(title),
                        n = /^Next/.test(title);
                    if (h || po || p || pa || n || title === ``){
                        //unless
                    } else {
                        counter++;
//                        console.log(counter + ` ** ` + title + ` ** ` + link);
                        db.serialize(() => {
                            db.run(`insert into manga(id,title) VALUES(?,?)`,[counter,title]);
                        });
                        console.log(counter,title);
                        console.log(`____________________`);
                    }
                }
                if ((title === `Next`) && (link != undefined)){
                    url = link;
                }
            })
        } else {
            console.log(response);
        }

        if (temp_url !== url){
            return recursive(url);
//          `:keep going:`;
        } else {
            console.log('Close SQLite database.');
            return `:stop:`;
        }

    }).catch(function (e) {
        console.log(e);
    });

    Promise.all([promiseData1]).then((value) => {
        if (value[0] === `:stop:`){
            db.close((err) => {
            if(err) {
                console.error(err.message);
                }
            });
        }
    });
}

recursive(url);

db.close() できたかな?

4. async/await

Javascript

const cheerio = require('cheerio'), 
    axios = require('axios');
const sqlite3 = require('sqlite3').verbose();

const db = new sqlite3.Database('13dlme_test2.db',(err) => {
    if(err) {
        return console.error(err.message);
    }
    console.log('Connect to SQLite database.');
});

db.serialize(() => {
    db.run(`CREATE TABLE manga(id interger,title string,link string)`)
});

var url = `https://13dl.me/list/popular/`;

var counter = 0;
const recursive = async (url) => {
    console.log(url);
    const temp_url = url;
    try {
        const {data} = await axios.get(url);
        const $ = cheerio.load(data)
        $('a').each(function (i, e) {
            const title = $(e).attr('title');      
            const link = $(e).attr('href');      
            if (title !== undefined){
                const h = /^Home/.test(title),
                    po = /^Popular\s/.test(title),
                    p = /^Prev/.test(title),
                    pa = /^Page/.test(title),
                    n = /^Next/.test(title);
                if (h || po || p || pa || n || title === ``){
                    //unless
                } else {
                    counter++;
//                    console.log(counter + ` ** ` + title + ` ** ` + link);
                    db.run(`insert into manga(id,title) VALUES(?,?)`,[counter,title]);
                    console.log(counter,title);
                    console.log(`____________________`);
                }
            }
            if ((title === `Next`) && (link != undefined)){
                url = link;
            }
        });

        if (temp_url !== url) {
        recursive(url);
        //`:keep going:`;
    }else{
            console.log('Close SQLite database.');
            db.close();
        //`:stop:`;
    }

    } catch(error) {
        throw error;
    }
}

recursive(url);

ただ、2 ... の場合でも、クローズしてなくともデータベースには書き込めているようなので、commit できているよう。

ハードディスクにツクツク書き込む古いPCの場合、SQLite のファイルに書き込むのに、1つづつコミットしたり、オープンクローズを繰り返すとスピードが犠牲になり遅くなるが、メモリ上にテーブル作って、データを書き込んで、できたものを最後に .db ファイルに書き込むとコミットは最後だけで済むのでいいかもしれない。 3 , 4 のプログラムコードはそれぞれ、すべてのデータが書き込まれた場合、データベースをクローズするように書いたつもりではあるが、これではそうならないケースがあるはずだが、きれいにクローズされた。おかしいが、ノンブロッキング。追求の余地があるが、これで結果がエラーにならないから難しい。

解説すべきことは特にない。

Ruby

require 'nokogiri'
require 'open-uri'
require 'sqlite3'

url = 'https://13dl.me/list/popular/'
counter = 0
threads = []

SQL =<<EOS                                                                     
create table manga(
    id INTEGER PRIMARY KEY,
    title text
    );
EOS

db = SQLite3::Database.open("13dlme.db")
db.execute(SQL)

def recursive(counter,url,threads,db) 
  html = URI.open(url).read
  doc = Nokogiri::HTML.parse(html)
  doc.css('a').each do |x|
    xx = x.attr('title')
    pa = /^Page/.match?("#{xx}")
    p = /^Prev/.match?("#{xx}")

    if xx && xx !='' &&  xx !='Home' && xx !='Popular Manga' then

      unless pa | p then
        n = /^Next/.match?("#{xx}")
        link = ''
        if n then
          link = x.attr('href')
          threads << Thread.new do
            recursive(counter,link,threads,db)
          end
        else
          counter += 1
          puts "#{counter} #{xx}"
          db.execute("insert into manga(id,title) values('#{counter}','#{xx}') ;")
        end
      end

    end

  end
end

recursive(counter,url,threads,db)
threads.each(&:join)
db.close

プログラムとしては、スマートフォン等でアプリの termux 、あるいは userland が動作するのであれば root 化されていなくても node.js がパッケージでインストール可能なので動作します。
いま現在 openssl 3 に依存する Perl5 Net::SSLeay は termux で普通にインストールするとコンパイルエラーになるため perl 5 だけ https 未対応。

Rf.
https://github.com/radiator-software/p5-net-ssleay/pull/391

https://debimate.jp/2019/03/16/androidにlinux環境を構築するuserlandがソースリーディング環/

termux の場合は、node.js をインストール、npm パッケージマネージャーで axios , cheerio, sqlite3 をインストールすれば上記のプログラムを書けるわけですが、userland は、まず OS を選ぶところからですが、OS によって apt だったり apk だったりの違いがありますが node.js をインストールできれば、あとは termux とほぼ同じ行程です。
iOS の場合 iSh というターミナルエミュレーターのアプリがありますが、iOS ではテストしていません。mac を持っていないのと、iOS の iphone のお古をもらったけれども、apple という会社に生理的嫌悪があるのでなかなか触れない。

Ruby や python だとライブラリのインストールまでに OS によって色々と違いがありますが、node.js の場合、そう違いが発生しないのはメリットかもしれないですね。javascript がいいのかどうかはさておいて、ブラウザだけで動くようにも書き換えれるはずなので。

著作権管理している人としては、リストの中に管理してるタイトルがあれば、テイクダウンの手続きを。

漫画 Bank であったときは、cloudflare のプロクシー上の画像データは IP アドレスによって日本国外からのアクセスは受け付けないようになってましたが、今回はそうなっていないようですし、タイトルもアルファベットで日本語タイトルと対応させていることから海外向けの需要を意識しているのかもしれません。

ツイッターでシェア
みんなに共有、忘れないようにメモ

view_list マンガサイトにつひて
第1回 ある一つのサイト についての
第2回 スキャンレーション
第3回 ある一つのサイトについての
第4回 タイトルから書籍情報を探す。
第5回 漫画Bank / La « mangabank.org » a disparu.

OooooO

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント