要約
こちらのスレッドの内容の紹介
ログイン方法が
api = AppPixivAPI()
api.login("username", "password")
aapi = AppPixivAPI()
aapi.login("username", "password")
だったものを
api = AppPixivAPI()
api.auth(refresh_token=REFRESH_TOKEN)
aapi = AppPixivAPI()
aapi.auth(refresh_token=REFRESH_TOKEN)
に変える。
なおREFRESH_TOKEN
は
こちらのスクリプトを使用して取得した。
このスクリプトをは有志が作ったものであり、公式のものではありません。
そのためログイン時のユーザーid、passwordは流出してもいいように捨て垢を使うなどの方法を使うことをお勧めします。
また何か問題が起きても___自己責任___でお願いします。
はじめに
2021年2月上旬ごろにpixiv.pyを使ったスクリプトが動かなくなってしまった。
PixivError: [ERROR] auth() failed! check username and password.
HTTP 400: {"has_error":true,"errors":{"system":{"message":"Invalid grant_type parameter or parameter missing","code":1508}},"error":"invalid_grant"}
こちらによると、pixivpyはもともとandroidアプリを通してログインをしていましたが、アップデートでログイン方法が変わったため、現在ログインできなくなったそうです。
有志によっていろいろ検討がなされた結果、リフレッシュトークンを使用することで今まで通り使用できるようになりました。
しかしトークンを一定期間ごとに手動で取得しなければならない可能性があり、完全自動化から半自動化に変わってしまう恐れがあるとのことです。
まだスレッドの内容を完全に理解していないので、間違いなどありましたらご指摘ください。
前準備
1.chromeが必要なので、ダウンロードしてください。
2.seleniumをダウンロードしてください
pip install selenium
seleniumとは、pythonでchromeを動かすライブラリです。
Python + Selenium で Chrome の自動操作を一通り
こちらを参考にしてください。
3.chromedriver.exeをダウンロードしてください。
chromedriver.exeはseleniumでchromeを動かすために必要なものです。
検索するとバイナリでダウンロードする方法などがあるそうですが、
今回はホームページから直接zipをダウンロードします。
ここからchromeのバージョンとosにあったものをダウンロード、
そして解凍
バージョンについては
こちらを参考に
4.REFRESH_TOKENの取得
こちらに従って実行してください。
ディレクトリ
このような配置にして、
プログラム
サイトのプログラムコピペです。
内容は理解していないうえ、実行してログインしなければならないので、何が起きるかわかりません。
そのためログイン時のユーザーid、passwordは流出してもいいように捨て垢を使うなどの方法を使うことをお勧めします。
自己責任でお願いします。
#!/usr/bin/env python
import time
import json
import re
import requests
from argparse import ArgumentParser
from base64 import urlsafe_b64encode
from hashlib import sha256
from pprint import pprint
from secrets import token_urlsafe
from sys import exit
from urllib.parse import urlencode
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
# Latest app version can be found using GET /v1/application-info/android
USER_AGENT = "PixivAndroidApp/5.0.234 (Android 11; Pixel 5)"
REDIRECT_URI = "https://app-api.pixiv.net/web/v1/users/auth/pixiv/callback"
LOGIN_URL = "https://app-api.pixiv.net/web/v1/login"
AUTH_TOKEN_URL = "https://oauth.secure.pixiv.net/auth/token"
CLIENT_ID = "MOBrBDS8blbauoSck0ZfDbtuzpyT"
CLIENT_SECRET = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj"
def s256(data):
"""S256 transformation method."""
return urlsafe_b64encode(sha256(data).digest()).rstrip(b"=").decode("ascii")
def oauth_pkce(transform):
"""Proof Key for Code Exchange by OAuth Public Clients (RFC7636)."""
code_verifier = token_urlsafe(32)
code_challenge = transform(code_verifier.encode("ascii"))
return code_verifier, code_challenge
def print_auth_token_response(response):
data = response.json()
try:
access_token = data["access_token"]
refresh_token = data["refresh_token"]
except KeyError:
print("error:")
pprint(data)
exit(1)
print("access_token:", access_token)
print("refresh_token:", refresh_token)
print("expires_in:", data.get("expires_in", 0))
def login():
caps = DesiredCapabilities.CHROME.copy()
caps["goog:loggingPrefs"] = {"performance": "ALL"} # enable performance logs
driver = webdriver.Chrome("./chromedriver", desired_capabilities=caps)
code_verifier, code_challenge = oauth_pkce(s256)
login_params = {
"code_challenge": code_challenge,
"code_challenge_method": "S256",
"client": "pixiv-android",
}
driver.get(f"{LOGIN_URL}?{urlencode(login_params)}")
while True:
# wait for login
if driver.current_url[:40] == "https://accounts.pixiv.net/post-redirect":
break
time.sleep(1)
# filter code url from performance logs
code = None
for row in driver.get_log('performance'):
data = json.loads(row.get("message", {}))
message = data.get("message", {})
if message.get("method") == "Network.requestWillBeSent":
url = message.get("params", {}).get("documentURL")
if url[:8] == "pixiv://":
code = re.search(r'code=([^&]*)', url).groups()[0]
break
driver.close()
print("[INFO] Get code:", code)
response = requests.post(
AUTH_TOKEN_URL,
data={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"code": code,
"code_verifier": code_verifier,
"grant_type": "authorization_code",
"include_policy": "true",
"redirect_uri": REDIRECT_URI,
},
headers={"User-Agent": USER_AGENT},
)
print_auth_token_response(response)
def refresh(refresh_token):
response = requests.post(
AUTH_TOKEN_URL,
data={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"grant_type": "refresh_token",
"include_policy": "true",
"refresh_token": refresh_token,
},
headers={"User-Agent": USER_AGENT},
)
print_auth_token_response(response)
def main():
parser = ArgumentParser()
subparsers = parser.add_subparsers()
parser.set_defaults(func=lambda _: parser.print_usage())
login_parser = subparsers.add_parser("login")
login_parser.set_defaults(func=lambda _: login())
refresh_parser = subparsers.add_parser("refresh")
refresh_parser.add_argument("refresh_token")
refresh_parser.set_defaults(func=lambda ns: refresh(ns.refresh_token))
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
実行
python pixiv_auth.py login
実行するとchromeが立ち上がり、ログイン画面が出てくるので、入力してログインしてください。
結果
❯ python3 pixiv_auth.py login
[INFO] Get code: 3s3Xc075wd7njPLJBXgXc4qS-...
access_token: Fp9WaXhNapC8myQltgEn...
refresh_token: uXooTT7xz9v4mflnZqJ...
expires_in: 3600
そうするとこのように表示されるので、
refresh_token:
に続くuXooTT7xz9v4mflnZqJ...
をコピーしてください
注意
refresh_tokenがあれば、だれでもログイン(?)できる状態になってしまいます。
具体的にはフォローを外したり、作品をお気に入りにしたりできます。
詳しくは
のfunctionsを中国語から日本語に訳して見てみてください。
よって、不用意にほかの人の目に触れないようにしましょう。
今回はそのためにリフレッシュトークンの後半を...
に変えています。
プログラムの書き換え
api = AppPixivAPI()
api.login("username", "password")
aapi = AppPixivAPI()
aapi.login("username", "password")
だったものを
api = AppPixivAPI()
api.auth(refresh_token="uXooTT7xz9v4mflnZqJ")
aapi = AppPixivAPI()
aapi.auth(refresh_token="uXooTT7xz9v4mflnZqJ")
に変える。
最後に
有志が集まって新しいログイン方法を見つけだしていたので、まとめました。
何度も言いますが、___自己責任___でよろしくお願いします。
特に中国語の翻訳はgoogleさんにお願いしているので、何か間違っている可能性があります。
間違いなどありましたらご指摘お願いします。m(__)m