Help us understand the problem. What is going on with this article?

pixivのフォローユーザーの漫画・イラストを一括DL

やりたかったこと

pixivでフォローしている全ての絵師さんの漫画・イラストをまとめてダウンロードしたかったんです。

1枚1枚手動で保存するのは面倒ですよね。
できれば自動で保存したい。

そんな時に見つけたのが、pixivpyライブラリ
非公式のpixiv APIみたいで、これで今回やりたかったことに近いスクリプトを書いてらっしゃる方もいました。

ただ、上記のスクリプトだと、漫画が1ページ目しか取れなかったので、少しスクリプトの改造が必要でした。
また、pixivpyには自分のフォローユーザーの情報を取得するメソッドがなかったので、そのスクリプトも別で用意する必要がありました。

やったこと

以下の3つのスクリプトを書きました。

  1. 自分のフォローユーザーの絵師IDを取得する
  2. 絵師IDから全ての漫画・イラストをDLする
  3. 自分のフォローユーザー全ての漫画・イラストをDLする

実行環境

  • OS: macOS High Sierra
  • python: 3.6.3
  • pixivpy: 3.3.3 -> 3.3.6

事前準備①: 外部ライブラリのインストール

必要なライブラリをインストールします。

Terminal
$ pip install pixivpy beautifulsoup4 robobrowser lxml

事前準備②: 認証情報をまとめる

コードに直書きしてもいいのですが、外部ファイルにまとめておくと管理しやすいです。

client.json
{
    "pixiv_id": "自分のpixiv ID",
    "password": "自分のpixiv パスワード",
    "user_id":  "自分のユーザID"
}

ファイル構造

作業ディレクトリ
├── client.json
├── get_following_userID.py
├── get_illustrations.py
├── get_all_illustrations.py
└── pixiv_images

ソースコード

1. 自分のフォローユーザーの絵師IDを取得する

フォローユーザー一覧ページより、絵師さんのユーザIDを抽出するスクリプトです。

▷ ソースコード本体

get_following_userID.py
# -*- coding: utf-8 -*-

from pixivpy3 import *
import json
from time import sleep
import sys, io, re, os
from robobrowser import RoboBrowser
from bs4 import BeautifulSoup

f = open("client.json", "r")
client_info = json.load(f)
f.close()

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

# フォローユーザーの総数を取得
self_info = aapi.user_detail(client_info["user_id"])
following_users_num = self_info.profile.total_follow_users

# フォローユーザー一覧ページのページ数を取得
if(following_users_num%48 != 0):
    pages = (following_users_num//48)+1
else:
    pages = following_users_num//48

#タグ除去用
p = re.compile(r"<[^>]*?>")
# [jump:1]形式除去用
jump = re.compile(r"\[jump:.+\]")
#ファイルエンコード設定用
character_encoding = 'utf_8'

# Webスクレイパーのログイン処理
pixiv_url = 'https://www.pixiv.net'
browser = RoboBrowser(parser='lxml', history=True)
browser.open('https://accounts.pixiv.net/login')
form = browser.get_forms('form', class_='')[0]
form['pixiv_id'] = client_info["pixiv_id"]
form['password'] = client_info["password"]
browser.submit_form(form)

# フォローユーザー一覧ページのURLを設定
target_url = 'https://www.pixiv.net/bookmark.php?type=user&rest=show&p='

# 全てのフォローユーザーのユーザIDを取得
following_users_id = []
for i in range(1, pages+1):
    print(target_url + str(i))
    browser.open(target_url + str(i))
    following_users = browser.find(class_='members')
    for user in following_users.find_all("input"):
        following_users_id.append(user.get("value"))
    sleep(3) # ページを移動したら一時待機する(マナー)

print(following_users_id)

▷ 実行例

Terminal
$ python get_following_userID.py
https://www.pixiv.net/bookmark.php?type=user&rest=show&p=1
https://www.pixiv.net/bookmark.php?type=user&rest=show&p=2
https://www.pixiv.net/bookmark.php?type=user&rest=show&p=3
['2246509', '1203800', '220531', ... , '11']

2. 絵師IDから全ての漫画・イラストをDLする

絵師さんのユーザIDより全ての漫画・イラストをダウンロードするスクリプトです。

▷ ソースコード本体

get_illustrations.py
# coding: utf-8

from pixivpy3 import *
import json
from time import sleep
import os

# ログイン処理
f = open("client.json", "r")
client_info = json.load(f)
f.close()
api = PixivAPI()
api.login(client_info["pixiv_id"], client_info["password"])
aapi = AppPixivAPI()
aapi.login(client_info["pixiv_id"], client_info["password"])

# 入力された絵師IDから絵師情報を取得
illustrator_pixiv_id = int(input("Type illustrator pixiv id number:\n>>>"))

# ユーザ情報(作品数、絵師名)を取得
user_info_json = aapi.user_detail(illustrator_pixiv_id)
total_illusts = user_info_json.profile.total_illusts
total_manga = user_info_json.profile.total_manga
illustrator_name = user_info_json.user.name

# イラスト情報を取得(とりあえず300作品取得)
works_info = api.users_works(illustrator_pixiv_id, page=1, per_page=300)

# # 絞り込み条件
# target_tag = "target_tag"
# lower_bookmarks = 0

# 渡されたディレクトリが存在しない場合に作成するLambda関数
mkdirExceptExist = lambda path: "" if os.path.exists(path) else os.mkdir(path)

mkdirExceptExist("./pixiv_images") # 保存用フォルダがない場合は生成

saving_direcory_path = "./pixiv_images/" + illustrator_name + "/"
mkdirExceptExist(saving_direcory_path) # 保存用フォルダがない場合は生成

separator = "============================================================"
print("Artist: %s" % illustrator_name)
print("Works: %d" % works_info.pagination.total)
print(separator)

for i, work_info in enumerate(works_info.response):

    work_type = "manga" if work_info.is_manga else "illust"

    mkdirExceptExist(saving_direcory_path + work_type) # 保存用フォルダがない場合は生成

    # # DL画像を絞り込む場合
    # if target_tag not in work_info.tags:  #タグによる絞り込み
    #   continue
    # works_bookmarks = work_info.stats.favorited_count.private + work_info.stats.favorited_count.public
    # if works_bookmarks < lower_bookmarks:  #ブックマーク数による絞り込み
    #   continue

    # ダウンロード
    work_title = work_info.title.replace("/", "-") # '/'はPathとして扱われるため回避

    print("Procedure: %d/%d" % (i + 1, works_info.pagination.total))
    print("Title: %s" % work_title)
    print("URL: %s" % work_info.image_urls.large)
    print("Caption: %s" % work_info.caption)
    print(separator)

    # 漫画の場合
    if work_info.is_manga:
        mkdirExceptExist(saving_direcory_path + "manga/" + work_title) # 保存用フォルダがない場合は生成

        manga_info = api.works(work_info.id)
        for page_no in range(0, manga_info.response[0].page_count):
            page_info = manga_info.response[0].metadata.pages[page_no]
            num = str(page_no) if len(str(page_no)) > 1 else "0" + str(page_no)
            aapi.download(page_info.image_urls.large, path=saving_direcory_path + "manga/" + work_title, name=num+".jpg")
            sleep(3)
    # イラストの場合
    else:
        aapi.download(work_info.image_urls.large, path=saving_direcory_path + "illust", name=work_title+".jpg")
        sleep(3)

print("\nThat\"s all.")

pixivpy 3.3.3 の場合(クリックで表示)
get_following_userID.py
# -*- coding: utf-8 -*-

from pixivpy3 import *
import json
from time import sleep
import sys, io, re, os
from robobrowser import RoboBrowser
from bs4 import BeautifulSoup

f = open("client.json", "r")
client_info = json.load(f)
f.close()

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

# フォローユーザーの総数を取得
self_info = aapi.user_detail(client_info["user_id"])
following_users_num = self_info.profile.total_follow_users

# フォローユーザー一覧ページのページ数を取得
if(following_users_num%48 != 0):
    pages = (following_users_num//48)+1
else:
    pages = following_users_num//48

#タグ除去用
p = re.compile(r"<[^>]*?>")
# [jump:1]形式除去用
jump = re.compile(r"\[jump:.+\]")
#ファイルエンコード設定用
character_encoding = 'utf_8'

# Webスクレイパーのログイン処理
pixiv_url = 'https://www.pixiv.net'
browser = RoboBrowser(parser='lxml', history=True)
browser.open('https://accounts.pixiv.net/login')
form = browser.get_forms('form', class_='')[0]
form['pixiv_id'] = client_info["pixiv_id"]
form['password'] = client_info["password"]
browser.submit_form(form)

# フォローユーザー一覧ページのURLを設定
target_url = 'https://www.pixiv.net/bookmark.php?type=user&rest=show&p='

# 全てのフォローユーザーのユーザIDを取得
following_users_id = []
for i in range(1, pages+1):
    print(target_url + str(i))
    browser.open(target_url + str(i))
    following_users = browser.find(class_='members')
    for user in following_users.find_all("input"):
        following_users_id.append(user.get("value"))
    sleep(3) # ページを移動したら一時待機する(マナー)

print(following_users_id)

▷ 実行例

Terminal
$ python get_illustrations.py
Type illustrator pixiv id number:
>>>2246509
Artist: カナヘイ
Works: 234
============================================================
Procedure: 1/234
Title: つねー
URL: https://i.pximg.net/img-original/img/2018/02/01/15/19/01/67061760_ugoira0.jpg
Caption: ねーねーねこの動くスタンプ出ましたー!
1と2の中から人気のものを動かしたグレードアップ版です✨動くねーねー達をぜひ見てみてください☺
https://t.co/PX4UFAf72k
============================================================
Procedure: 2/234
Title: ただ単に勢い余ったうさぎ
URL: https://i.pximg.net/img-original/img/2018/02/01/15/17/31/67061749_p0.jpg
Caption: ただ単に勢い余ったうさぎ。
...
..
.
That's all.

3. 自分のフォローユーザー全ての漫画・イラストをDLする

1と2の機能を統合したスクリプトです。

▷ ソースコード本体

get_all_illustrations.py
# -*- coding: utf-8 -*-

from pixivpy3 import *
import json
from time import sleep
import sys, io, re, os

from robobrowser import RoboBrowser
from bs4 import BeautifulSoup

f = open("client.json", "r")
client_info = json.load(f)
f.close()


##### 全てのフォローユーザーのユーザIDを取得 #####

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

# フォローユーザーの総数を取得
self_info = aapi.user_detail(client_info["user_id"])
following_users_num = self_info.profile.total_follow_users

# フォローユーザー一覧ページのページ数を取得
if(following_users_num%48 != 0):
    pages = (following_users_num//48)+1
else:
    pages = following_users_num//48

#タグ除去用
p = re.compile(r"<[^>]*?>")
# [jump:1]形式除去用
jump = re.compile(r"\[jump:.+\]")
#ファイルエンコード設定用
character_encoding = 'utf_8'

# Webスクレイパーのログイン処理
pixiv_url = 'https://www.pixiv.net'
browser = RoboBrowser(parser='lxml', history=True)
browser.open('https://accounts.pixiv.net/login')
form = browser.get_forms('form', class_='')[0]
form['pixiv_id'] = client_info["pixiv_id"]
form['password'] = client_info["password"]
browser.submit_form(form)

# フォローユーザー一覧ページのURLを設定
target_url = 'https://www.pixiv.net/bookmark.php?type=user&rest=show&p='

# 渡されたディレクトリが存在しない場合に作成するLambda関数
mkdirExceptExist = lambda path: "" if os.path.exists(path) else os.mkdir(path)

# 全てのフォローユーザーのユーザIDを取得
following_users_id = []
for i in range(1, pages+1):
    print(target_url + str(i))
    browser.open(target_url + str(i))
    following_users = browser.find(class_='members')
    for user in following_users.find_all("input"):
        following_users_id.append(user.get("value"))
    sleep(3) # ページを移動したら一時待機する(マナー)

print("\n▽▽▽\n")


##### ダウンロード処理 #####

mkdirExceptExist("./pixiv_images/") # 保存用フォルダがない場合は生成

# 絵師IDから絵師情報を取得
for user_id in following_users_id:

    # ユーザ情報(作品数、絵師名)を取得
    user_info_json = aapi.user_detail(int(user_id))
    total_illusts = user_info_json.profile.total_illusts
    total_manga = user_info_json.profile.total_manga
    illustrator_name = user_info_json.user.name

    # イラスト情報を取得(とりあえず300作品取得)
    works_info = api.users_works(int(user_id), page=1, per_page=300)

    # # 絞り込み条件
    # target_tag = "target_tag"
    # lower_bookmarks = 0

    mkdirExceptExist("./pixiv_images/" + illustrator_name + "/") # 保存用フォルダがない場合は生成

    separator = "============================================================"
    print("Artist: %s" % illustrator_name)
    print("Works: %d" % works_info.pagination.total)
    print(separator)

    # ダウンロード
    for i, work_info in enumerate(works_info.response):

        work_type = "manga" if work_info.is_manga else "illust"

        saving_direcory_path = "./pixiv_images/" + illustrator_name + "/"
        mkdirExceptExist(saving_direcory_path + work_type) # 保存用フォルダがない場合は生成

        # # DL画像を絞り込む場合
        # if target_tag not in work_info.tags:  #タグによる絞り込み
        #   continue
        # works_bookmarks = work_info.stats.favorited_count.private + work_info.stats.favorited_count.public
        # if works_bookmarks < lower_bookmarks:  #ブックマーク数による絞り込み
        #   continue

        # ダウンロード
        work_title = work_info.title.replace("/", "-") # '/'はPathとして扱われるため回避

        print("Procedure: %d/%d" % (i + 1, works_info.pagination.total))
        print("Title: %s" % work_title)
        print("URL: %s" % work_info.image_urls.large)
        print("Caption: %s" % work_info.caption)
        print(separator)

        # 漫画の場合
        if work_info.is_manga:
            mkdirExceptExist(saving_direcory_path + "manga/" + work_title) # 保存用フォルダがない場合は生成

            manga_info = api.works(work_info.id)
            for page_no in range(0, manga_info.response[0].page_count):
                page_info = manga_info.response[0].metadata.pages[page_no]
                num = str(page_no) if len(str(page_no)) > 1 else "0" + str(page_no)
                aapi.download(page_info.image_urls.large, path=saving_direcory_path + "manga/" + work_title, name=num+".jpg")
                sleep(3)
        # イラストの場合
        else:
            aapi.download(work_info.image_urls.large, path=saving_direcory_path + "illust", name=work_title+".jpg")
            sleep(3)

print("\nThat\'s all.")

pixivpy 3.3.3 の場合(クリックで表示)
get_all_illustrations.py
#!/usr/bin/env PYTHONIOENCODING=UTF-8 python3
# -*- coding: utf-8 -*-

from pixivpy3 import *
import json
from time import sleep
import sys, io, re, os

from robobrowser import RoboBrowser
from bs4 import BeautifulSoup

f = open("client.json", "r")
client_info = json.load(f)
f.close()


##### 全てのフォローユーザーのユーザIDを取得 #####

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

# フォローユーザーの総数を取得
self_info = aapi.user_detail(client_info["user_id"])
following_users_num = self_info.profile.total_follow_users

# フォローユーザー一覧ページのページ数を取得
if(following_users_num%48 != 0):
    pages = (following_users_num//48)+1
else:
    pages = following_users_num//48

#タグ除去用
p = re.compile(r"<[^>]*?>")
# [jump:1]形式除去用
jump = re.compile(r"\[jump:.+\]")
#ファイルエンコード設定用
character_encoding = 'utf_8'

# Webスクレイパーのログイン処理
pixiv_url = 'https://www.pixiv.net'
browser = RoboBrowser(parser='lxml', history=True)
browser.open('https://accounts.pixiv.net/login')
form = browser.get_forms('form', class_='')[0]
form['pixiv_id'] = client_info["pixiv_id"]
form['password'] = client_info["password"]
browser.submit_form(form)

# フォローユーザー一覧ページのURLを設定
target_url = 'https://www.pixiv.net/bookmark.php?type=user&rest=show&p='

# 全てのフォローユーザーのユーザIDを取得
following_users_id = []
for i in range(1, pages+1):
    print(target_url + str(i))
    browser.open(target_url + str(i))
    following_users = browser.find(class_='members')
    for user in following_users.find_all("input"):
        following_users_id.append(user.get("value"))
    sleep(3) # ページを移動したら一時待機する(マナー)

print("\n▽▽▽\n")


##### ダウンロード処理 #####

# 絵師IDから絵師情報を取得
for user_id in following_users_id:

    json_result = api.users_works(int(user_id), per_page=300) # とりあえず1ユーザにつきmax300作品ダウンロードする
    total_works = json_result.pagination.total
    illust = json_result.response[0]

    # # 絞り込み条件
    # target_tag = "target tag"
    # lower_score = 0

    if not os.path.exists("./pixiv_images"): # 保存用フォルダがない場合は生成
        os.mkdir("./pixiv_images")
    saving_direcory_path = "./pixiv_images/" + illust.user.name + "/"
    separator = "------------------------------------------------------------"

    # ダウンロード
    print("Artist: %s" % illust.user.name)
    print("Works: %d" % total_works)
    print(separator)
    if not os.path.exists(saving_direcory_path):
        os.mkdir(saving_direcory_path)
    for work_no in range(0, total_works):
        # # DLする画像を絞り込む場合
        # if target_tag not in illust.tags: #タグによる絞り込み
        #   continue
        # if illust.stats.score < lower_score:#スコアによる絞り込み
        #   continue

        illust = json_result.response[work_no]
        print("Procedure: %d/%d" % (work_no + 1, total_works))
        print("Title: %s" % illust.title)
        print("URL: %s" % illust.image_urls["large"])
        print("Caption: %s" % illust.caption)
        # 漫画の場合
        if illust.is_manga:
            work_info = api.works(illust.id)
            for page_no in range(0, work_info.response[0].page_count):
                page_info = work_info.response[0].metadata.pages[page_no]
                aapi.download(page_info.image_urls.large, saving_direcory_path)
                sleep(3)
        # イラストの場合
        else:
            aapi.download(illust.image_urls.large, saving_direcory_path)
            sleep(3)
        print(separator)

print("\nThat\'s all.")

▷ 実行例

Terminal
$ python get_all_illustrations.py
https://www.pixiv.net/bookmark.php?type=user&rest=show&p=1
https://www.pixiv.net/bookmark.php?type=user&rest=show&p=2
https://www.pixiv.net/bookmark.php?type=user&rest=show&p=3

▽▽▽

Artist: カナヘイ
Works: 234
============================================================
Procedure: 1/234
Title: つねー
URL: https://i.pximg.net/img-original/img/2018/02/01/15/19/01/67061760_ugoira0.jpg
Caption: ねーねーねこの動くスタンプ出ましたー!
1と2の中から人気のものを動かしたグレードアップ版です✨動くねーねー達をぜひ見てみてください☺
https://t.co/PX4UFAf72k
============================================================
Procedure: 2/234
Title: ただ単に勢い余ったうさぎ
URL: https://i.pximg.net/img-original/img/2018/02/01/15/17/31/67061749_p0.jpg
Caption: ただ単に勢い余ったうさぎ。
...
..
.
That's all.

(※ソースコードを載せましたが、無茶な利用をするとBANされるのでご注意ください)

今後の展望

このスクリプトで好きなイラストを集めて、自分の好みのイラスト判別器でも作ろうかと。
判別器を使って、Tumblrで自動リブログとかしたいです。

でも、まずはシンプルに、似たキャラクターの分類器から始めようと思います。
積んであったゼロ本を読み始める機会がやってきたぞ...。

参考にしたサイト

余談

プログラミングが面倒な人のために、こんな便利ツールもあるみたいです。
pixivダウンローダ

【Updated: 2019-07-08(pixivpy 3.3.3 -> 3.3.6 に移行)】

Hirosaji
ゲーム実況を観ながらプログラミングをするのが好きなWebエンジニアです。
https://bl.ocks.org/hirosaji
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした