Skip to content

Instantly share code, notes, and snippets.

@upbit
Forked from ZipFile/README.md
Last active February 16, 2025 14:34
Show Gist options
  • Save upbit/6edda27cb1644e94183291109b8a5fde to your computer and use it in GitHub Desktop.
Save upbit/6edda27cb1644e94183291109b8a5fde to your computer and use it in GitHub Desktop.
Pixiv OAuth Flow (with Selenium)

Retrieving Auth Token (with Selenium)

  1. Setup, install Selenium/ChromeDriver
pip install selenium
# download chromedriver from https://chromedriver.storage.googleapis.com/index.html?path=91.0.4472.101/
# eg: wget https://chromedriver.storage.googleapis.com/91.0.4472.101/chromedriver_mac64.zip && unzip chromedriver_mac64.zip
  1. unzip chromedriver (due to Gatekeeper, run it if you use MacOS):
+
|-> pixiv_auth.py
\-> chromedriver      <- place to here
  1. Run the command:
python pixiv_auth.py login

This will open the browser with Pixiv login page.

If you did everything right and Pixiv did not change their auth flow, pair of auth_token and refresh_token should be displayed.

After entering the password, wait for a while, chrome will disappear, and the following information will be output in the console window:

❯ python3 pixiv_auth.py login
[INFO] Get code: 3s3Xc075wd7njPLJBXgXc4qS-...
access_token: Fp9WaXhNapC8myQltgEn...
refresh_token: uXooTT7xz9v4mflnZqJ...
expires_in: 3600

Refresh Tokens

python pixiv_auth.py refresh OLD_REFRESH_TOKEN

使用方法:

  1. 下载 pixiv_auth.py 脚本,以及 chromedriver(放在脚本目录)
  2. pip install selenium
  3. python pixiv_auth.py login

成功后会在窗口内自动显示refresh_token,保存起来以后通过 api.auth(refresh_token=REFRESH_TOKEN) 登录

注:如果在墙内访问,请手动设置 REQUESTS_KWARGS.proxies 的代理,不然获取code后无法正确提交请求到Pixiv (现象是 [INFO] Get code: xxxxx 后一直卡住,未requests配置代理即可)

#!/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 = "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)"
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"
REQUESTS_KWARGS = {
# 'proxies': {
# 'https': 'http://127.0.0.1:1087',
# },
# 'verify': False
}
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",
}
print("[INFO] Gen code_verifier:", code_verifier)
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,
"app-os-version": "14.6",
"app-os": "ios",
},
**REQUESTS_KWARGS
)
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,
"app-os-version": "14.6",
"app-os": "ios",
},
**REQUESTS_KWARGS
)
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()
@zjDrummond
Copy link

Installed that, and now I get this...

PS C:\Users\Zachary\Downloads\pixiv auth> python pixiv_auth.py login
Traceback (most recent call last):
File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 157, in
main()
File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 153, in main
args.func(args)
File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 148, in
login_parser.set_defaults(func=lambda _: login())
^^^^^^^
File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 68, in login
driver = webdriver.Chrome("./chromedriver", desired_capabilities=caps)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: WebDriver.init() got an unexpected keyword argument 'desired_capabilities'

@upbit
Copy link
Author

upbit commented Mar 27, 2024

Installed that, and now I get this...

PS C:\Users\Zachary\Downloads\pixiv auth> python pixiv_auth.py login Traceback (most recent call last): File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 157, in main() File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 153, in main args.func(args) File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 148, in login_parser.set_defaults(func=lambda _: login()) ^^^^^^^ File "C:\Users\Zachary\Downloads\pixiv auth\pixiv_auth.py", line 68, in login driver = webdriver.Chrome("./chromedriver", desired_capabilities=caps) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: WebDriver.init() got an unexpected keyword argument 'desired_capabilities'

See the answer before:
https://gist.github.com/upbit/6edda27cb1644e94183291109b8a5fde?permalink_comment_id=4996990#gistcomment-4996990

@lFitzl
Copy link

lFitzl commented May 3, 2024

@MicrogamerCz
Copy link

Is there a way to auth with geckodriver instead?

@GreenLandisaLie
Copy link

I was having trouble with this first getting unexpected keyword argument 'desired_capabilities' then getting an error related to selenium not being able to detect the chromedriver version or something but I figured out so I'll list here the steps I took in case someone is interested:

  • Open Chrome > click on the 3 dots on the right side of the extension toolbar > Help > About Google Chrome
  • A settings page will open up that should show your Chrome version near the top. Copy it and replace the version in this link with your version: https://storage.googleapis.com/chrome-for-testing-public/132.0.6834.160/win64/chromedriver-win64.zip.
  • Open that edited url and hopeful it will exist and download a .zip file containing the best matching chromedriver for your version of chrome.
  • If the URL does not exist then check out these 2 links for more info and try to find something that should be compatible with your version and/or update Chrome and try again:
  • https://developer.chrome.com/docs/chromedriver/downloads/version-selection
  • https://googlechromelabs.github.io/chrome-for-testing/
  • Once you have chromedriver create a python env using 'requirements.txt' that contain these lines:
wheel
requests
argparse
selenium==4.28.1
  • Note: wheel is there just in case because I've had trouble before in other envs where it was necessary as for selenium - as of the time I'm typing - the version 4.28.1 is the latest and I'm hardcoding it because right now it works and they have broke compatibility many times before so stick with it for as long as possible.

  • In pixiv_auth.py add this line: from selenium.webdriver.chrome.service import Service

  • Also in pixiv_auth.py find this line: driver = webdriver.Chrome("./chromedriver", desired_capabilities=caps) and disable/remove it and in its place add these:

service = Service('./chromedriver.exe')
options = webdriver.ChromeOptions()
options.set_capability('goog:loggingPrefs', {'performance': 'ALL'})
driver = webdriver.Chrome(service=service, options=options)
  • Save the file and it should work now! Good Luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment