4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

pythonのpixivpyを使用してpixivからうごイラをmp4に変換してダウンロードする(番外編2)

Last updated at Posted at 2021-01-08

#2021/2/21追記 ログイン方法を変えることで動くようになりました!!

記事に従ってプログラムを変更してください。

今のこの記事のプログラムは変更していないので動きません。
自分で直してください。m(__)m

#はじめに
@choshicureさんの作った、pixivからうごイラをgifとしてダウンロードするプログラムがあります。

とても使いやすいですが、結構gifが圧縮されてしまい、本物同様のきれいなうごイラは再現できません。
gifについて調べたところ、使用する色の数やディザなど様々な方法で圧縮していることがわかりました。
このことについて詳しく知りたい方は、以下のサイトを見てください。

gifの作成に使用されているPILライブラリを詳しく見ましたが、残念ながら圧縮度合いを変えることはできないみたいです。
そこでOpenCVライブラリを使用してうごイラからmp4動画を作ることにしました。
なお、ffmpegの使用も考えましたが、ダウンロードやpathを通したり大変そうなのでやめました。

#注意
:::note warn
pixivpyはおそらく中国の有志が作ったライブラリであり、公式のものではありません。
そのためログイン時のユーザーid、passwordは流出してもいいように捨て垢を使うなどの方法を使うことをお勧めします。
:::

また乱用厳禁です。
スクレイピングのルールとして1秒当たりリクエリは1回までという暗黙のルールがあります。
これがないとdos攻撃となってしまい、法的にアウトです。
sleepをコメントアウトする際は自己責任でお願いします。

また何か問題が起きても自己責任でお願いします。

#環境
このプログラムはwindows10環境下で動かしています。
Linux、Mac環境下で動作するかはわかりません。
特に、スラッシュやバックスラッシュの扱いが異なってくるので、どうなるか本堂にわかりません。

#ライブラリのインポート

pip install opencv-python
pip install pixivpy

#コード

画像をダウンロードするところまでは
@choshicureさんの作った

と同じです。
変数の説明などはこちらの記事を読んでください。

後半のmp4に変換する部分は以下の記事を参考にしました。

ugoira_mp4_downloader.py
from pixivpy3 import *
from  time import sleep
from PIL import Image
import os, glob
import sys
import cv2

#ログイン処理
aapi = AppPixivAPI()

ID = 'YOUR_ID'
PASS = 'YOUR_PASS'

aapi.login(ID, PASS)

#イラストIDの入力待機
illust_id = int(input('>>'))

illust = aapi.illust_detail(illust_id)
ugoira_url = illust.illust.meta_single_page.original_image_url.rsplit('0', 1)
ugoira = aapi.ugoira_metadata(illust_id)
ugoira_frames = len(ugoira.ugoira_metadata.frames)
ugoira_delay = ugoira.ugoira_metadata.frames[0].delay
dir_name = str(illust_id)+'_ugoira'

#うごイラを保存するフォルダの作成
if not os.path.isdir(dir_name):
    os.mkdir(dir_name)


#うごイラに使われているすべての画像のダウンロード
for frame in range(ugoira_frames):
    print(frame)
    frame_url = ugoira_url[0] + str(frame) + ugoira_url[1]
    aapi.download(frame_url, path=dir_name)
    sleep(2) #ダウンロードした後は一応2秒待機

#-------------------------------------------------
#ここからオリジナル

#動画の作成
frames = glob.glob(f'{dir_name}/*_ugoira*')
frames.sort(key=os.path.getmtime, reverse=False)

#パラメーター
fps = 1000 / ugoira_delay
height = illust.illust.height
width = illust.illust.width



# encoder(for mp4)
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
# output file name, encoder, fps, size(fit to image size)
video = cv2.VideoWriter(f'{dir_name}/{illust_id}.mp4',fourcc, fps, (width, height))

for frame in frames:
    img = cv2.imread(frame)
    video.write(img)

video.release()
print('written')

#まとめ
gifよりもファイルサイズが小さく、きれいなmp4のうごイラがダウンロードできるようになりました。
しかし、欠点としてmp4は再生するソフトでいろいろ設定しないとループができなかったり、圧縮する以上元のうごイラよりも画像が劣化しているという問題があります。

#2022/2/21 追記

色々問題点があったので、修正しました。
具体的には、

  1. OpenCVが全角文字のパスに対応していない
  2. OpenCVで動画を作る際、αチャンネルを含んだpng画像に対応していない
  3. フォルダ内に画像以外があるとエラーになる
  4. 画像のソートをダウンロード時間ではなく、数字の大きさ順にした

の3点です。

##1. OpenCVが全角文字のパスに対応していない

オリジナルのプログラムでは、プレゼントディレクトリ直下の半角文字のみのフォルダに画像をダウンロードしているため、問題がありません。
しかし、プログラムをいじって保存場所を変えてディレクトリを指定する際に全角文字が入ってしまうと、エラーになってしまいます。
そこで、以下のサイトを参考にNumpyを使用して読み込むことにしました。

##2. OpenCVで動画を作る際、αチャンネルを含んだpng画像に対応していない

詳しくは以下の記事に書いています。

要約すると、Numpyを経由したことで、αチャンネルを含んだpng画像がそのまま読み込まれてしまったので、αチャンネルを削除するようにしました。

##3. フォルダ内に画像以外があるとエラーになる
うごイラを初めてダウンロードするなら問題がありません。
しかし、ダウンロードしなおしたり、ほかの方法でダウンロードするとき、フォルダ内のファイルをすべて取得する際に、以前作成したhtmlなどが混在しているとエラーになってしまいます。
そこで、画像ファイルのみを取得するようにしました。

##4. 画像のソートをダウンロード時間ではなく、数字の大きさ順にした
題名の通りです。
以下のサイトを参考にしました。

##プログラム
以上の修正を加えたプログラムが以下になります。

ugoira_mp4_downloader.py
from pixivpy3 import *
from  time import sleep
from PIL import Image
import os, glob
import sys
import cv2

#ログイン処理
aapi = AppPixivAPI()
aapi.auth(refresh_token = "YOUR_refresh_token")

#イラストIDの入力待機
illust_id = int(input('>>'))

illust = aapi.illust_detail(illust_id)
ugoira_url = illust.illust.meta_single_page.original_image_url.rsplit('0', 1)
ugoira = aapi.ugoira_metadata(illust_id)
ugoira_frames = len(ugoira.ugoira_metadata.frames)
ugoira_delay = ugoira.ugoira_metadata.frames[0].delay
dir_name = str(illust_id)+'_ugoira'

#うごイラを保存するフォルダの作成
if not os.path.isdir(dir_name):
    os.mkdir(dir_name)


#うごイラに使われているすべての画像のダウンロード
for frame in range(ugoira_frames):
    print(frame)
    frame_url = ugoira_url[0] + str(frame) + ugoira_url[1]
    aapi.download(frame_url, path=dir_name)
    sleep(2) #ダウンロードした後は一応2秒待機

#-------------------------------------------------
#ここからオリジナル

#動画の作成
#jpg以外の画像あるのかわからない
frames = glob.glob(f'{dir_name}/*.jpg')
frames += glob.glob(f'{dir_name}/*.jpeg')
frames += glob.glob(f'{dir_name}/*.png')
#https://note.nkmk.me/python-sort-num-str/
frames.sort(key=lambda s: int(re.findall(r'\d+', s)[-1]))
#frames.sort(key=os.path.getmtime, reverse=False)

#パラメーター
fps = 1000 / ugoira_delay
height = illust.illust.height
width = illust.illust.width



# encoder(for mp4)
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
# output file name, encoder, fps, size(fit to image size)
video = cv2.VideoWriter(f'{dir_name}/{illust_id}.mp4',fourcc, fps, (width, height))

for frame in frames:
    #https://imagingsolution.net/program/python/opencv-python/read_save_image_files_in_japanese/
    #numpyで開いてopencvに渡すことで全角文字のパスでも動く
    buf = np.fromfile(frame, np.uint8)
    img = cv2.imdecode(buf, cv2.IMREAD_UNCHANGED)
    #アルファチャンネルの削除
    if img.shape[2] == 4:
        img = np.delete(img, 3, axis=2)
    video.write(img)

video.release()
print('written')

#参考サイト

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?