À 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/

Rf. https://rentry.co/i542t

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/

プログラミング言語も正規表現をゼロからという場合は、正規表現をテストできる rubular のようなサイトでパズルゲームとして始めるのがいいと思います。正規表現は言語と独立して面白い分野だと思います。
https://rubular.com/

なんか正規表現を推してる?

Rf.鬼車 正規表現 Version 6.9.8 2022/04/11
Rf.鬼雲 (鬼車改) 正規表現 Version 6.1.0 2016/12/25

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 を読み込んで保存すればデータベースファイルになる。
https://rentry.co/i542t

Ruby

似た感じで Ruby で書くと ...
_why? ... Ruby

iRuby binder https://mybinder.org/v2/gh/RubyData/binder/master?filepath=../lab

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')

    if xx && xx !='' &&  xx !='Home' && xx !='Popular Manga' then
      pa = /^Page/.match?("#{xx}")
      pr = /^Prev/.match?("#{xx}")

      unless pa | p then
        nex = /^Next/.match?("#{xx}")
        link = ''
        if nex 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)

Rf.Oga | YorickPeterse https://github.com/YorickPeterse/oga/blob/master/doc/migrating_from_nokogiri.mdv

recursive

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

PHP

https://rentry.co/5rwmw/
https://rentry.co/ppif2
https://controlc.com/1db2e26a

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')

    if xx && xx !='' &&  xx !='Home' && xx !='Popular Manga' then
      pa = /^Page/.match?("#{xx}")
      pr = /^Prev/.match?("#{xx}")

      unless pa | pr then
        nex = /^Next/.match?("#{xx}")
        link = ''
        if nex 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

for loop
https://rentry.co/8o583

Go

Gorutine, SQLite3
https://rentry.co/b79ht


プログラムとしては、スマートフォン等でアプリの 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

my $body_text = system (curl -s '.$target_url.' | grep "hogehoge" )

というように https リクエストを perl の ssl モジュールを使わず shell に任せて cURL を使ったら問題なさそうです(termuxのデレクトリの配置構造が Linux とちがうためにインストールされている openssl 3 を使う perl モジュールのインストールに失敗するだけなので)。cURL で出力させた文字を perl にテキストとして変数に入れるには qx{} が使えました。
perl qx{}
https://perldoc.perl.org/perlop#Quote-Like-Operators
shell 2>&1
Cp:https://stackoverflow.com/questions/818255/in-the-shell-what-does-21-mean

Making system call that returns the STDOUT outpyt as string

Ruby
using backticks %x system-call cURL and recursive count

#Ruby : call cURL
#_________________________________________

#test1 = `curl -s https://google.com`

url = "https://google.com/"
ua = 'Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95
.0.4638.54 Safari/537.36r'

#test2 = `curl --user-agent "#{ua}" "#{url}"`

test3 = %x(curl -s --user-agent "#{ua}" "#{url}")

puts test3

cURL を使うように書き換えるとこうなりました。

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 アドレスによって日本国外からのアクセスは受け付けないようになってましたが、今回はそうなっていないようですし、タイトルもアルファベットで日本語タイトルと対応させていることから海外向けの需要を意識しているのかもしれません。

Edit
Pub: 09 Jun 2022 03:25 UTC
Edit: 17 Sep 2022 12:21 UTC
Views: 267