びしょの日記 (vipshota’s diary)

これはショタコン的視点からの雑多な日記帳です。まじめな記事が5割、ふまじめな記事が5割でできています。将来黒歴史になる予定です。

某画像投稿サイトから、フォロワーの画像を一括ダウンロードする試み

先人たちのソースコードを参考にしています。

get_api.py

from pixivpy3 import *
import json

def get_api():
    with open("client.json") 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"])

    return api, aapi

get_following_userID.py

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


def get_following_userID(api, aapi):

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

    # フォローユーザー一覧ページのページ数を取得
    if(n_following_users % 48 != 0):
        pages = (n_following_users // 48) + 1
    else:
        pages = n_following_users // 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)
    print("# of following users",n_following_users)
    print("# of obtained following users",len(following_users_id))
    with open("following.json", "w") as f:
        json.dump(following_users_id, f, indent=1)
    return following_users_id

get_illustrations.py

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

def get_illustrations(api, aapi, illustrator_pixiv_id, DL_flag=False):

    # 最大作品数
    max_works = 3000

    # 入力された絵師IDから絵師情報を取得
    json_result = api.users_works(illustrator_pixiv_id, per_page=max_works)
    
    # アカウントは停止ユーザー
    if "pagination" not in json_result:
        return

    total_works = json_result.pagination.total

    # 投稿数が0
    if total_works == 0:
        return

    illust = json_result.response[0]

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

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

    # ダウンロード
    #print(f"Artist: {illust.user.name}")
    #print(f"Works: {total_works}")

    results = []

    for n_work in range(0, total_works):
        if n_work >= max_works:
            break
        
        # DL画像を絞り込む場合
        # if target_tag not in illust.tags: #タグによる絞り込み
        #   continue

        illust = json_result.response[n_work]
        print(f"Works: {n_work + 1}/{total_works}, Download: {DL_flag}",end="\r")
        #print(f"Title: {illust.title}")
        #print(f"URL: {illust.image_urls['large']}")
        #print("Caption: %s" % illust.caption)
        #print(f"Tags: {illust.tags}")
        illust.caption = "" # 出力する際、冗長なので削除、コメントアウト可
        results.append(illust)

        image_urls = []
        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]
                image_urls.append(page_info.image_urls)
        else:
            # イラストの場合
            aapi.download(illust.image_urls.large, saving_direcory_path)
            image_urls.append(illust.image_urls)

        if DL_flag:
            for image_url in image_urls:
                exp_path = os.path.join(saving_direcory_path, image_url.large.split("/")[-1])
                #ダウンロード済みを確認
                if not os.path.exists(exp_path):
                    aapi.download(image_url.large,saving_direcory_path)
                    sleep(3) # ページを移動したら一時待機する(マナー)
                else:
                    print(exp_path,"exists.")

    print()

    with open(f"./logs/{illustrator_pixiv_id}.json", "w",encoding="utf-8") as f:
        json.dump(results, f, indent=1, ensure_ascii=False)

main.py

import get_following_userID
import get_illustrations
import get_api
from time import sleep

api, aapi= get_api.get_api()

ids = get_following_userID.get_following_userID()

for i, id in enumerate( ids):
    print(f"Users: {i+1}/{len(ids)}, ID: {id}")
    get_illustrations.get_illustrations(api, aapi, int(id), False)
    sleep(1)

大量の画像をダウンロードすると、サーバーと回線に負荷を与える可能性があります。兵庫県警や神奈川県警等に恣意的な検挙を受けないためにも、負荷を与えないようにして使ってください。