À 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
ってどうやって書くんだろうと前々から気になっていたので、この新たな違法サイトについて、どういうものか見てみるのに Javascript
で node.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 アドレスによって日本国外からのアクセスは受け付けないようになってましたが、今回はそうなっていないようですし、タイトルもアルファベットで日本語タイトルと対応させていることから海外向けの需要を意識しているのかもしれません。