8
13

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から特定のユーザーの作品をすべて一括でダウンロードする(うごイラ含む)(その1)

Last updated at Posted at 2020-12-31

2021/11/25追記 こちらの記事は残しますが、動きません。

apiの変更などによりプログラムを一新したため、イラストのダウンロードの記事をこちら

をメインとして書いていくことにします。
現在こちらの記事に書いているプログラムはバージョン1.0であり、念のため残しておきますが、動きません。

なお、新しい記事のバージョン2.0において、特定のイラストレーターさんの作品をダウンロードしたい場合は、

.py
for user_id in client_info["ids"]:

となっているところを、

.py
for user_id in [11,12848282]:

というようにダウンロードしたいユーザーのid書き換えてに書き換えてください。
上の例ですと、ユーザーidが1112848282のユーザーの作品をダウンロードするようになります。

よろしくお願いします。

概要

pixivからイラストをいちいち1枚ずつダウンロードするのは面倒くさいですよね
pythonを使って特定のユーザーの作品を自動でダウンロードできるプログラムを作りました。
画像だけでなく漫画、うごイラ、にも対応しています。
特にうごイラはそれぞれの画像をダウンロードし、gifも作成します。
タグや閲覧数による絞り込みや、ダウンロードする上限などを決めることができます。

参考にしたサイト

自動でダウンロードしてくれるソフトがないか調べてみましたが、なかなか見つかりません。
そこで見つけたのが

こんなサイトです。

主に

を参考にして作ったプログラムです

注意

pixivpyはおそらく中国の有志が作ったライブラリであり、公式のものではありません。
そのためログイン時のユーザーid、passwordは流出してもいいように捨て垢を使うなどの方法を使うことをお勧めします。
また乱用厳禁です。
スクレイピングのルールとして1秒当たりリクエリは1回までという暗黙のルールがあります。
これがないとdos攻撃となってしまい、法的にアウトです。
sleepをコメントアウトする際は自己責任でお願いします。
また何か問題が起きても自己責任でお願いします。

目次

動作環境

このプログラムはwindows10環境下で動かしています。
Linux、Mac環境下で動作するかはわかりません。
2020/12/30現在でのanacondaの最新のバージョンで動きます

  • python3.8.5
  • pixivpy3.5.10
  • pillow8.0.1

ディレクトリ

こんな感じに用意してください

.
├── pixiv_downloader.py
├── img
│   ├── 
├── client.json

画像だとこんな感じ
ディレクトリ.png

pythonを実行する前に、このディレクトリまで移動してください。
移動の仕方

ライブラリのインポート

pip install pixivpy

インストールできないときは管理者でコマンドプロンプトを開いてやってみてください

使用したライブラリ:pixivpy
中国語(オリジナル)

日本語

client.jsonの作成

ログイン用に使う情報を書いてください
メモ帳などで下の内容をコピペして自分の情報を書き、保存時に拡張子を.txtではなく.jsonにしてclient.jsonという名前で保存して下さい。

client.json
{
  "pixiv_id": "pixiv_id",
  "password": "password",
}

ログイン.png
"pixiv_id"、"password"は画像のところに普段入力している文字列です。
"pixiv_id": "12345"のように""で囲って記述してください。

コード

以下をメモ帳などにコピペし、pixiv_downloader.pyという名前で保存してください。

pixiv_downloader.py

from pixivpy3 import *
import json
import os
from PIL import Image
import glob
from time import sleep


#ここは各個人にやってもらう設定です

#ダウンロードしたいユーザーのid(webでユーザーのページに行った際のurlの最後にある数字です)
id_search = 11
#一人当たりダウンロードする作品の最大数、すべてダウンロードしたい場合はできるだけ大きくすること(一人の作者の作品数を超えること)
#新しい順にダウンロードされます
works=10
#ブックマーク数によるフィルター、最小値を設定、0ならすべて
score=0
#閲覧数によるフィルター、最小値を設定、0ならすべて
view=0

#タグによるフィルター 書き方 target_tag = ["Fate/GrandOrder","FGO","FateGO","Fate/staynight"]
target_tag = [] #target_tag内に複数書くと、そのうち少なくとも一つあればダウンロード
target_tag2 = []#さらにtarget_tag2に書くとtarget_tagを満たし、かつtarget_tag2を満たすものだけダウンロード
extag = ["R-18"]#extag内のタグが一つでも入っていればダウンロードしない

#画像を保存するディレクトリ
main_saving_direcory_path = "./img/"




#事前処理

#事前に作ったアカウント情報が書かれたファイルを読み込む
with open("client.json", "r") as f:
    client_info = json.load(f)

# pixivpyのログイン処理
api = PixivAPI()
api.login(client_info["pixiv_id"], client_info["password"])
aapi = AppPixivAPI()
aapi.login(client_info["pixiv_id"], client_info["password"])




#ここからダウンロードについて

#入力されたidの作品数を取得
illustrator_id = api.users_works(id_search, per_page=works)


#アカウントだけで絵が投稿されていない人を避けるため
if not illustrator_id.count == 0:
    total_works = illustrator_id.pagination.total
    if works < total_works:
        total_works=works

    #ユーザーの情報を得るために最初の作品のデータをとってくる
    illust = illustrator_id.response[0]

    #user名でフォルダを作るため、windowsのフォルダ名に使えない文字を置換
    username = illust.user.name.translate(str.maketrans({'/': '_' , ':': '', ',': '_', ';': '' ,'*': '_', '?': '', '"': "'", '>': ')', '<': '(', '|': ''}))
    username = username.rstrip(".")
    username = username.lstrip(".")
    username = username.rstrip(" ")
    username = username.rstrip(" ")

    #ユーザー名(id)の形でフォルダを作成
    
    saving_direcory_path = main_saving_direcory_path + username + ("(") +str(illust.user.id) + (")") + "/"

    #フォルダ名を最新のユーザー名に直す
    saving_direcory_name = saving_direcory_path[:-1]
    present_folder_list = glob.glob(main_saving_direcory_path + "*")

    for present_dir in present_folder_list:
        num = present_dir.rsplit("(", 1)[-1][:-1]
        #print(num)
        name = present_dir.rsplit("\\", 1)[-1]
        name = name.rsplit("(", 1)[0]
        #print(name)
        #print("--------------------------------------")
        if num == str(illust.user.id) and username != name:
            print(present_dir + "を次に変更" + saving_direcory_name)
            print("--------------------------------------")
            os.rename(present_dir, saving_direcory_name)





    #フォルダが存在しないときは作る
    if not os.path.exists(saving_direcory_path):
        os.mkdir(saving_direcory_path)
    separator = "------------------------------------------------------------"

    #Display information of illustrator and the number of illustrations
    print("Illustrator: {}".format(illust.user.name))
    print("Works number: {}".format(illustrator_id.pagination.total))
    print(separator)




    #Download
    for work_no in range(0, total_works):

        illust = illustrator_id.response[work_no]

        #フィルター

        #タグによるフィルター
        if len(list(set(target_tag)&set(illust.tags))) == 0 and target_tag != []:
            continue
        if len(list(set(target_tag2)&set(illust.tags))) == 0 and target_tag2 != []:
            continue
        #exタグが一つでも入っていたらスキップ
        if len(list(set(extag)&set(illust.tags))) > 0 :
            continue
        #score以上の作品だけダウンロード
        if illust.stats.favorited_count.private + illust.stats.favorited_count.public < score :
            continue
        #view以上の作品だけダウンロード
        if illust.stats.views_count < view :
            continue



        #if the illustration has already downloaded, skip downloading it
        #pixivに投稿できるのはpng,jpg,gif,うごイラ
        #ここではあくまで1ページ目がダウンロードされているかで判断しているので、ダウンロード中に通信が途切れ、2ページ目以降がダウンロードされていないかはわからない

        if os.path.exists(saving_direcory_path+str(illust.id)+"_p0.png") or os.path.exists(saving_direcory_path+str(illust.id)+"_p0.jpg") or os.path.exists(saving_direcory_path+str(illust.id)+'_ugoira') or os.path.exists(saving_direcory_path+str(illust.id)+"_p0.gif"):
            print("Title:"+str(illust.title)+" has already downloaded.")
            print(separator)
            continue

        #ダウンロード前のスリープ
        sleep(1)
        print("Now: {0}/{1}".format(work_no + 1, total_works))
        print("Title: {}".format(illust.title))


        #うごイラ
        if illust.type == "ugoira":
        #イラストIDの入力待機
            illust_id = illust.id
            ugoira_url = aapi.illust_detail(illust_id).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 = saving_direcory_path + str(illust_id)+'_ugoira'


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

            #うごイラに使われているすべての画像のダウンロード
            for frame in range(ugoira_frames):
                #ダウンロード中のsleep
                sleep(1)
                frame_url = ugoira_url[0] + str(frame) + ugoira_url[1]
                aapi.download(frame_url, path=dir_name)


            #保存した画像をもとにgifを作成
            #力不足でgifにする際に結構画像が劣化します
            #pythonでgif作成のライブラリを探しましたが、圧縮度合いを設定できるものが見つかりませんでした。
            frames = glob.glob(f'{dir_name}/*')
            frames.sort(key=os.path.getmtime, reverse=False)
            ims = []
            for frame in frames:
                ims.append(Image.open(frame))
            ims[0].save(f'{dir_name}/{illust_id}.gif', save_all=True, append_images=ims[1:], optimize=False, duration=ugoira_delay, loop=0)

        # illustrations with more than one picture
        elif illust.is_manga:
            work_info = api.works(illust.id)
            for page_no in range(0, work_info.response[0].page_count):
                #ダウンロード中のsleep
                sleep(1)
                page_info = work_info.response[0].metadata.pages[page_no]
                aapi.download(page_info.image_urls.large, saving_direcory_path)

        # illustrations with only one picture
        else:
            aapi.download(illust.image_urls.large, saving_direcory_path)

        print(separator)


    print("Download complete! Thanks to {0}{1}!!".format(illust.user.id, illust.user.name))

使用方法

pixiv_downloader.pyの最初のほうにあるところ.py
#ここは各個人にやってもらう設定です
#ダウンロードしたいユーザーのid(webでユーザーのページに行った際のurlの最後にある数字です)
id_search = 11
#一人当たりダウンロードする作品の最大数、すべてダウンロードしたい場合はできるだけ大きくすること(一人の作者の作品数を超えること)
#新しい順にダウンロードされます
works=10
#ブックマーク数によるフィルター、最小値を設定、0ならすべて
score=0
#閲覧数によるフィルター、最小値を設定、0ならすべて
view=0
#タグによるフィルター 書き方 target_tag = ["Fate/GrandOrder","FGO","FateGO","Fate/staynight"]
target_tag = [] #target_tag内に複数書くと、そのうち少なくとも一つあればダウンロード
target_tag2 = []#さらにtarget_tag2に書くとtarget_tagを満たし、かつtarget_tag2を満たすものだけダウンロード
extag = ["R-18"]#extag内のタグが一つでも入っていればダウンロードしない

個々のコメントにあるように設定してください
上記の状態では、pixiv公式のアカウントからR-18のタグが付いた作品を除く中から新しい順に10こダウンロードされます。
詳しく解説します。

id_search

id_searchはダウンロードしたいユーザーのユーザーidです。
ユーザーのページに行き、urlの最後の番号です。
InkedDesktop Screenshot 2020.12.31 - 21.22.03.74-1_LI.jpg

https://www.pixiv.net/users/11

のようにあったら11です
その後実行すればimgフォルダー内にユーザーごとに分かれたフォルダが作成され、中に画像がダウンロードされます
今回はpixiv事務局(11)というフォルダ内にダウンロードされます。

works

今回works=10となっていますが、これは新しい順に10作品(漫画などはまとめて1とカウント)ダウンロードされます。
なおすでにダウンロードされている画像もカウントします。
ダウンロードする数を制限しない場合はworks=10000000000など、膨大な数にしてください。

score

scoreはブックマークの数を表しています。
今回のscore=0ではすべての作品をダウンロードします。
例えばscre=100とすれば100未満の作品はダウンロードされません。

view

viewは閲覧数を指します。
動作はscoreと同じで、その値以上の作品だけダウンロードします。

tag

tagの挙動は面倒くさいです。
target_tag = ["Fate/GrandOrder","FGO","FateGO","Fate/staynight"]の時、
タグの中にFate/GrandOrder __または__FGO __または__FateGO __または__Fate/staynightが含まれていればダウンロードします。
さらに、
target_tag = ["Fate/GrandOrder"] target_tag2 = ["FGO"]
の時はFate/GrandOrder __かつ__FGOが含まれている作品がダウンロードされます。

extagは要素が一つでも含まれていたらダウンロードしません。
extag = ["R-18","R-15"]の場合、R-18 __または__R-15というタグが含まれていればダウンロードしません。

エラーについて

実行中に

PixivError: requests GET https://public-api.secure.pixiv.net/v1/works/58652797.json 
error: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))

と出た場合は短時間にダウンロードしすぎた場合だと思われます。
時間をおいてもう一度実行してください
今のところアカウントがbanされたことはありません。

まとめ

今回はpythonを使ってユーザーの作品をフィルターをかけてダウンロードする方法を解説しました。
自分がこれを作り始めたきっかけは2、3年前に好きだった絵師さんが作品を全部消してしまったことがあったからです。
あの時全部ダウンロードしとけば、でも手動では時間が足りない、と思って作り出しました。
2年間自分で使ってみて、バグを直したり、gifダウンロードできるようにしたりしていました。
初期のころはユーザーさんが名前を変えると作品を全部ダウンロードしなおすこともあり、大変でした。
最近完成してきたかな、と思い公開しました。
追加してほしい機能、バグなどありましたらコメントしてください。
ありがとうございました。

展望

pixivpyについてもっと解説しようかな
フォローしているユーザーの作品をすべてダウンロードするプログラムも作ってあるので、時間があったら書きます
書きました

破損したファイルの検出、すべてダウンロードできなかった場合の発見方法なども作ってあるので時間があれば

こんなのを見つけてしまった。
同じくpixivpyを使っている
exif(画像のメタデータ)を書き込む機能があるみたい
gifをmp4にしている ← その手があったか
ffmpegを使っているぽい
folder.jpgという画像を用意すればフォルダのサムネを指定できるらしい

このプログラムでは作者の画像をこうしてダウンロードしている。

編集履歴のうち重要なもの

2020/1/4追記
2021/1/4以前のプログラム等、間違っていて動作しませんでした。
すみませんでした。
直したのでもう一度使ってみてください。

8
13
4

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
8
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?