【2025最新】pixivの削除済み・非公開作品を復元・サルベージ・見る方法 【ナツぬい】

未分類

ナツぬいと一緒にpixivのブクマを見返していました。

削除済み 非公開となってそこに何があったのかすら思い出せない、泣きゲーの結末のような儚さがありますよね。ですが、往生際が悪いのがオタクの性というものです。なんとかしてサルベージする方法を検討してみました。 

まず、ブックマークした作品であるとF12で開発者ツールを開いて該当箇所にカーソルを合わせれば作品IDが分かります。(スマホの場合は長押しすると分かったりします。)作品IDを取得できたら pixiv artworks 1145141919(作品ID) みたいな感じでgoogle検索をしてみてください。運が良ければNozomi.la sankaku complex danbooru等に無断転載されたものが見つかります。見つからなかった場合はyandexとbaiduでも一応検索してみてください。もしかしたらweiboにあるかもしれません。あるいはXで検索するのも一つの手です。作者がそっちで公開している可能性もミリ単位であります。

もしそれでも見つからなかった場合は、魚拓が取られている可能性に賭けて、archive.isとmegalodonで検索してみてください。ナツぬい編集部ではpixivの投稿をarchive.todayでアーカイブしよう運動を奨励しております。

まあここまでのサルベージ方法なら知ってる方が大半だと思います。 なのでもう少し踏み込んだ内容を。

海外のギーク・ナードなら何かしら新規性のある方法を見つけているのではないと思いredditを読んでいたところ気になる書き込みを見つけました。

pixivの画像は削除されたとしてもサーバーには保存されていることがたまにあるようです。基準は不明。もしかしたら非公開とかマイピク限定公開なら見れて削除された場合は見れないのかも。

ttps://i.pximg.net/img-original/img/year/month/day/hours/mins/secs/<ID>.{extension}

これがpximg側のCDNのアドレスらしいですね。 ブクマの削除された作品もIDは取得できるので、その前後IDの投稿時間から絞り込みを行って総当たりを行えばワンチャン復元できる可能性があります。

ナツぬいと一緒にこの手法を試してみました。

https://www.pixiv.net/artworks/128906393 が削除済みで、これの投稿時間を前後128906392-128906394の投稿時間で絞ってスクリプトで総当たりしたところ、

https://i.pximg.net/img-original/img/2025/04/03/13/50/19/128906393_p0.jpg

を得ました。

有能な読者の方が自動発掘スクリプトを書いてくださったので共有します!

https://gitlab.com/-/snippets/4893870

それではよきpixivライフを。

以下改定前の駄文です 

(2025-08-06追記:リファラーをpximg.netにしないと弾かれます )

chromeならこのリンクからTampermonkeyっていうuserscriptを実行できる拡張を入れた後、

これをコピペして適用してください。たぶんいけます。上のアロナのえっちな絵が出てきたら勝ちです。

10%くらいの確率で復元できると思います。 それでは、良きpixivライフを

以下、チャッピー製の自動発掘スクリプトです。 

import tkinter as tk

from tkinter import messagebox

from datetime import datetime, timedelta

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

from time import sleep

driver = None  # ← グローバルで保持!

def generate_candidate_times(before_str, after_str):

    before = datetime.strptime(before_str, “%Y/%m/%d/%H/%M/%S”)

    after = datetime.strptime(after_str, “%Y/%m/%d/%H/%M/%S”)

    times = []

    t = before  # ← 開始を「beforeちょうど」に変更

    while t <= after:  # ← 「after秒」も含める

        times.append(t.strftime(“%Y/%m/%d/%H/%M/%S”))

        t += timedelta(seconds=1)

    return times

def open_urls():

    global driver  # ← ここで global 宣言

    try:

        pixiv_id = int(entry_id.get())

        before_time = entry_before.get()

        after_time = entry_after.get()

        extensions = [“.jpg”, “.png”]

        before_id = pixiv_id – 1

        after_id = pixiv_id + 1

        candidate_times = generate_candidate_times(before_time, after_time)

        options = Options()

        options.add_argument(“–start-maximized”)

        driver = webdriver.Chrome(options=options)

        driver.get(“https://www.pixiv.net/”)

        sleep(2)

        for t in candidate_times:

            for ext in extensions:

                url = f”https://i.pximg.net/img-original/img/{t}/{pixiv_id}_p0{ext}”

                driver.execute_script(f”window.open(‘{url}’, ‘_blank’);”)

                sleep(1.2)

        messagebox.showinfo(“完了”, f”Pixiv ID {pixiv_id} の候補URLをすべて開きました!\nChromeウィンドウは閉じません。”)

    except Exception as e:

        messagebox.showerror(“エラー”, str(e))

# GUI構築

root = tk.Tk()

root.title(“Pixiv CDN探索ツール”)

tk.Label(root, text=”Pixiv ID:”).grid(row=0, column=0, padx=10, pady=5, sticky=”e”)

entry_id = tk.Entry(root, width=30)

entry_id.grid(row=0, column=1, padx=10, pady=5)

tk.Label(root, text=”前の投稿時刻 (YYYY/MM/DD/HH/MM/SS):”).grid(row=1, column=0, padx=10, pady=5, sticky=”e”)

entry_before = tk.Entry(root, width=30)

entry_before.grid(row=1, column=1, padx=10, pady=5)

tk.Label(root, text=”後の投稿時刻 (YYYY/MM/DD/HH/MM/SS):”).grid(row=2, column=0, padx=10, pady=5, sticky=”e”)

entry_after = tk.Entry(root, width=30)

entry_after.grid(row=2, column=1, padx=10, pady=5)

tk.Button(root, text=”探索開始”, command=open_urls, width=20).grid(row=3, column=0, columnspan=2, pady=15)

root.mainloop()

コメント

  1. mol より:

    スクリプトを改善(というより作り直し)してみました。
    IDだけで絞り込めます。GUIは実装してません。
    Google Colabに貼り付けるだけで動きます。

    import itertools
    from datetime import datetime, timedelta
    import time
    from typing import List, Optional, Tuple

    import requests

    PIXIV_ARTWORKS_URL = “https://www.pixiv.net/artworks/”
    ARTWORKS_DETAIL_ENDPOINT = “https://www.pixiv.net/ajax/illust/”
    ARTWORKS_EXT_LIST = (“jpg”, “png”, “gif”) #複数の拡張子を混ぜるとjpgに変換されるのでjpg優先
    ARTWORKS_TEMPLATE = (
    “https://i.pximg.net/img-original/img/{timestamp}/{id}_p{number}.{ext}”
    )

    session = requests.Session()
    session.headers.update(
    {
    “Referer”: “https://pixiv.net/”,
    “User-Agent”: “Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/135.0.7049.53 Mobile/15E148 Safari/604.1″,
    }
    )

    def get_artwork_timestamp(artwork_id: int) -> Optional[str]:
    try:
    response = session.get(f”{ARTWORKS_DETAIL_ENDPOINT}{artwork_id}”, timeout=10)
    response.raise_for_status()
    except requests.RequestException as e:
    print(f”IDの取得に失敗しました {artwork_id}: {e}”)
    return None

    try:
    artwork_data = response.json().get(“body”, {}).get(“userIllusts”, {})
    timestamp = artwork_data.get(str(artwork_id), {}).get(“updateDate”)
    if not timestamp:
    print(f”タイムスタンプが見つかりませんでした {artwork_id}”)
    return timestamp
    except ValueError as e:
    print(f”Jsonのパースに失敗しました {artwork_id}: {e}”)
    return None

    def find_valid_id(
    start_id: int, step: int, max_attempts: int = 30, interval: float = 0.5
    ) -> Optional[Tuple[int, str]]:
    current_id = start_id
    for attempt in range(max_attempts):
    timestamp = get_artwork_timestamp(current_id)
    if timestamp is not None:
    print(f”有効なIDを見つけました: {current_id} タイムスタンプ: {timestamp}”)
    return current_id, timestamp
    else:
    print(f”タイムスタンプの取得に失敗しました: {current_id}, 次のIDを取得します…”)
    current_id += step
    time.sleep(interval)

    print(
    f”ID {start_id} から {max_attempts} 回試行しましたが、有効なIDを見つけられませんでした”
    )
    return None

    def generate_timestamps(start: datetime, end: datetime):
    current = start
    while current Optional[Tuple[str, str, str]]:
    for ext in ARTWORKS_EXT_LIST:
    for ts in generate_timestamps(start, end):
    url = ARTWORKS_TEMPLATE.format(
    timestamp=ts, id=artwork_id, number=0, ext=ext
    )
    response = session.head(url, timeout=10)

    if response.status_code == 200:
    print(“ベースリンクの取得に成功しました:” + url)
    return url, ts, ext
    else:
    print(“URLが見つかりませんでした:” + url)

    time.sleep(interval)

    return None

    def get_all_artworks_images(artwork_id: int, timestamp: str, ext: str, interval: float = 0.5) -> List[str]:
    urls = []
    for i in itertools.count():
    url = ARTWORKS_TEMPLATE.format(
    timestamp=timestamp, id=artwork_id, number=i, ext=ext
    )
    response = session.head(url, timeout=10)
    if response.status_code == 200:
    urls.append(url)
    print(“取得に成功しました:” + url)
    time.sleep(interval)
    else:
    break

    return urls

    def salvage_pixiv(artwork_id: int, interval: float = 0.5) -> Optional[List[str]]:
    start_result = find_valid_id(start_id=artwork_id – 1, step=-1, interval=interval)
    if start_result is None:
    return None
    start_id, start_timestamp = start_result

    end_result = find_valid_id(start_id=artwork_id + 1, step=1)
    if end_result is None:
    return None
    end_id, end_timestamp = end_result

    start_datetime = datetime.fromisoformat(start_timestamp)
    end_datetime = datetime.fromisoformat(end_timestamp)

    result = check_all_pattern_artworks(start_datetime, end_datetime, artwork_id, interval)
    if result is None:
    return None
    url, ts, ext = result
    return get_all_artworks_images(artwork_id, ts, ext, interval)

    if “__main__” == __name__:
    artwork_id = int(input(“IDを入力してください:”).strip())
    interval = 0.75 #HEADがメインなので0.5秒くらいでも多分大丈夫。ただ長ければ長いほど安全
    result = salvage_pixiv(artwork_id, interval=interval)

    if result is not None:
    BAR_LENGTH = 80
    print(“=” * BAR_LENGTH)
    print(f”{len(result)}件の画像をサルベージしました”)
    print(“-” * BAR_LENGTH)

    for i, url in enumerate(result, start=1):
    print(f”[{i:02d}] {url}”)

    print(“=” * BAR_LENGTH)
    else:
    print(“サルベージに失敗しました”)

  2. mol より:

    インデント崩れたのでpastebinに記載します
    https://pastebin.com/SX7KZgj1

    • natsu natsu より:

      おお まさかこのコードを改善してくださる方が現れるとは…
      ありがとうございます。前後のidの絞り込みまで自動で行えるようになると効率爆上がりなので大変嬉しいコードです。
      webアプリ的な感じで実行できるようにしてみたいですね チャッピーと格闘してみます!

  3. mol より:

    せっかくなのでGUIも作ってみました。
    tkinterは初めてなので、UIはClaudeに作ってもらいました。ついでにZip保存も積んでます。
    ファイルを1つにまとめようとしたのですが長過ぎたので分割してます。

    あと、前のコメントで書き忘れていたんですが、リクエスト間隔を設定できるようにしたのと、IDから全ての画像を取得するようにしています。
    https://gitlab.com/-/snippets/4893870

    • natsu natsu より:

      ありがとうございます!
      プログラミングは全くの門外漢なので本当に助かります!

タイトルとURLをコピーしました