From 5aa4bfddb3552980e6013bd8150edc3923c33219 Mon Sep 17 00:00:00 2001 From: Danipulok <45077699+Danipulok@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:34:43 +0200 Subject: [PATCH] chore(formatting): use f-strings instead of old python formatting & access fields as attrs, not as dicts & update `README.md` (#380) --- README.md | 181 ++++++++++++++++++-------------- demo.py | 108 +++++++++---------- download_illusts.py | 12 ++- example_bypass_sni.py | 4 +- example_tag_translations.py | 4 +- notebooks/pixivCrawler.ipynb | 2 +- notebooks/pixivDownloader.ipynb | 6 +- pixivpy3/aapi.py | 84 +++++++-------- pixivpy3/api.py | 17 +-- tests/conftest.py | 5 +- 10 files changed, 224 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index 1b82d30..90a82b9 100644 --- a/README.md +++ b/README.md @@ -119,34 +119,37 @@ Requirements: [requests](https://pypi.python.org/pypi/requests) ### Example: ```python -from pixivpy3 import * +from pixivpy3 import AppPixivAPI + +access_token = "..." +refresh_token = "..." api = AppPixivAPI() -# api.login("username", "password") # Not required +api.set_auth(access_token, refresh_token) # get origin url json_result = api.illust_detail(59580629) illust = json_result.illust -print(">>> origin url: %s" % illust.image_urls['large']) +print(f">>> origin url: {illust.image_urls.large}") # get ranking: 1-30 # mode: [day, week, month, day_male, day_female, week_original, week_rookie, day_manga] json_result = api.illust_ranking('day') for illust in json_result.illusts: - print(" p1 [%s] %s" % (illust.title, illust.image_urls.medium)) + print(f" p1 [{illust.title}] {illust.image_urls.medium}") # next page: 31-60 next_qs = api.parse_qs(json_result.next_url) json_result = api.illust_ranking(**next_qs) for illust in json_result.illusts: - print(" p2 [%s] %s" % (illust.title, illust.image_urls.medium)) + print(f" p2 [{illust.title}] {illust.image_urls.medium}") # get all page: next_qs = {"mode": "day"} while next_qs: json_result = api.illust_ranking(**next_qs) for illust in json_result.illusts: - print("[%s] %s" % (illust.title, illust.image_urls.medium)) + print(f"[{illust.title}] {illust.image_urls.medium}") next_qs = api.parse_qs(json_result.next_url) ``` @@ -160,6 +163,7 @@ while next_qs: 2. Call `api.download()` like the below: ```python +from pixivpy3 import AppPixivAPI aapi = AppPixivAPI() json_result = aapi.illust_ranking() for illust in json_result.illusts[:3]: @@ -172,16 +176,16 @@ for illust in json_result.illusts[:3]: 2. Change deprecated SPAI call to Public-API call ```python -print(">>> new ranking_all(mode='daily', page=1, per_page=50)") -#rank_list = api.sapi.ranking("all", 'day', 1) -rank_list = api.ranking_all('daily', 1, 50) +from pixivpy3 import AppPixivAPI +api = AppPixivAPI() +rank_list = api.illust_ranking('day') print(rank_list) # more fields about response: https://github.com/upbit/pixivpy/wiki/sniffer ranking = rank_list.response[0] for img in ranking.works: - # print(img.work) - print("[%s/%s(id=%s)] %s" % (img.work.user.name, img.work.title, img.work.id, img.work.image_urls.px_480mw)) + # print(img.work) + print(f"[{img.work.user.name}/{img.work.title}(id={img.work.id})] {img.work.image_urls.px_480mw}") ``` ### About @@ -199,56 +203,63 @@ Find Pixiv API in **Objective-C**? You might also like ### App-API (6.0 - app-api.pixiv.net) ```python +from __future__ import annotations +from typing import Any +from pixivpy3.utils import ParsedJson + +from pixivpy3.api import BasePixivAPI + + class AppPixivAPI(BasePixivAPI): # 返回翻页用参数 - def parse_qs(next_url): + def parse_qs(cls, next_url: str | None) -> dict[str, Any] | None: ... # 用户详情 - def user_detail(user_id): + def user_detail(self, user_id: int | str) -> ParsedJson: ... # 用户作品列表 ## type: [illust, manga] - def user_illusts(user_id, type="illust"): + def user_illusts(self, user_id: int | str, type="illust") -> ParsedJson: ... # 用户收藏作品列表 # tag: 从 user_bookmark_tags_illust 获取的收藏标签 - def user_bookmarks_illust(user_id, restrict="public"): + def user_bookmarks_illust(self, user_id: int | str, restrict="public") -> ParsedJson: ... # 用户收藏作品列表中的小说 - def user_bookmarks_novel(user_id, restrict="public"): + def user_bookmarks_novel(self, user_id: int | str, restrict="public") -> ParsedJson: ... - def user_related(seed_user_id): + def user_related(self, seed_user_id: int | str) -> ParsedJson: ... # 关注用户的新作 # restrict: [public, private] - def illust_follow(restrict="public"): + def illust_follow(self, restrict="public") -> ParsedJson: ... # 作品详情 (类似PAPI.works(),iOS中未使用) - def illust_detail(illust_id): + def illust_detail(self, illust_id: int | str) -> ParsedJson: ... # 作品评论 - def illust_comments(illust_id, include_total_comments=None): + def illust_comments(self, illust_id: int | str, include_total_comments=None) -> ParsedJson: ... # 相关作品列表 - def illust_related(illust_id): + def illust_related(self, illust_id: int | str) -> ParsedJson: ... # 插画推荐 (Home - Main) # content_type: [illust, manga] - def illust_recommended(content_type="illust"): + def illust_recommended(self, content_type="illust") -> ParsedJson: ... # 小说推荐 - def novel_recommended(): + def novel_recommended(self) -> ParsedJson: ... # 作品排行 # mode: [day, week, month, day_male, day_female, week_original, week_rookie, day_manga] # date: '2016-08-01' # mode (Past): [day, week, month, day_male, day_female, week_original, week_rookie, # day_r18, day_male_r18, day_female_r18, week_r18, week_r18g] - def illust_ranking(mode="day", date=None): + def illust_ranking(self, mode="day", date=None) -> ParsedJson: ... # 趋势标签 (Search - tags) - def trending_tags_illust(): + def trending_tags_illust(self) -> ParsedJson: ... # 搜索 (Search) # search_target - 搜索类型 @@ -258,7 +269,15 @@ class AppPixivAPI(BasePixivAPI): # sort: [date_desc, date_asc, popular_desc] - popular_desc为会员的热门排序 # duration: [within_last_day, within_last_week, within_last_month] # start_date, end_date: '2020-07-01' - def search_illust(word, search_target="partial_match_for_tags", sort="date_desc", duration=None, start_date=None, end_date=None): + def search_illust( + self, + word: str, + search_target="partial_match_for_tags", + sort="date_desc", + duration=None, + start_date=None, + end_date=None, + ) -> ParsedJson: ... # 搜索小说 (Search Novel) # search_target - 搜索类型 @@ -268,148 +287,156 @@ class AppPixivAPI(BasePixivAPI): # keyword - 关键词 # sort: [date_desc, date_asc] # start_date/end_date: 2020-06-01 - def search_novel(word, search_target="partial_match_for_tags", sort="date_desc", start_date=None, end_date=None): + def search_novel( + self, + word: str, + search_target="partial_match_for_tags", + sort="date_desc", + start_date=None, + end_date=None, + ) -> ParsedJson: ... - def search_user(word, sort='date_desc', duration=None): + def search_user(self, word: str, sort='date_desc', duration=None) -> ParsedJson: ... # 作品收藏详情 - def illust_bookmark_detail(illust_id): + def illust_bookmark_detail(self, illust_id: int | str) -> ParsedJson: ... # 新增收藏 - def illust_bookmark_add(illust_id, restrict="public", tags=None): + def illust_bookmark_add(self, illust_id: int | str, restrict="public", tags=None) -> ParsedJson: ... # 删除收藏 - def illust_bookmark_delete(illust_id): + def illust_bookmark_delete(self, illust_id: int | str) -> ParsedJson: ... # 关注用户 - def user_follow_add(user_id, restrict="public"): + def user_follow_add(self, user_id: int | str, restrict="public") -> ParsedJson: ... # 取消关注用户 - def user_follow_delete(user_id): + def user_follow_delete(self, user_id: int | str) -> ParsedJson: ... # 用户收藏标签列表 - def user_bookmark_tags_illust(restrict="public"): + def user_bookmark_tags_illust(self, restrict="public") -> ParsedJson: ... # Following用户列表 - def user_following(user_id, restrict="public"): + def user_following(self, user_id: int | str, restrict="public") -> ParsedJson: ... # Followers用户列表 - def user_follower(user_id): + def user_follower(self, user_id: int | str) -> ParsedJson: ... # 好P友 - def user_mypixiv(user_id): + def user_mypixiv(self, user_id: int | str) -> ParsedJson: ... # 黑名单用户 - def user_list(user_id): + def user_list(self, user_id: int | str) -> ParsedJson: ... # 获取ugoira信息 - def ugoira_metadata(illust_id): + def ugoira_metadata(self, illust_id: int | str) -> ParsedJson: ... # 用户小说列表 - def user_novels(user_id): + def user_novels(self, user_id: int | str) -> ParsedJson: ... # 小说系列详情 - def novel_series(series_id, last_order=None): + def novel_series(self, series_id: int | str, last_order=None) -> ParsedJson: ... # 小说详情 - def novel_detail(novel_id): + def novel_detail(self, novel_id: int | str) -> ParsedJson: ... # 小说 (包含正文) - def webview_novel(novel_id): + def webview_novel(self, novel_id: int | str) -> ParsedJson: ... # 小说评论 - def novel_comments(novel_id): + def novel_comments(self, novel_id: int | str) -> ParsedJson: ... # 大家的新作 # content_type: [illust, manga] - def illust_new(content_type="illust", max_illust_id=None): + def illust_new(self, content_type="illust", max_illust_id=None) -> ParsedJson: ... - def novel_new(max_novel_id=None): + def novel_new(self, max_novel_id=None) -> ParsedJson: ... # 特辑详情 (无需登录,调用Web API) - def showcase_article(showcase_id): + def showcase_article(self, showcase_id) -> ParsedJson: ... ``` [Usage](https://github.com/upbit/pixivpy/blob/aec177aa7a1979f7ec4c5bbbeed9085cc256bdbd/demo.py#L306): ```python -aapi = AppPixivAPI() +from pixivpy3 import AppPixivAPI +api = AppPixivAPI() # 作品推荐 -json_result = aapi.illust_recommended() +json_result = api.illust_recommended() print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 作品相关推荐 -json_result = aapi.illust_related(57065990) +json_result = api.illust_related(57065990) print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 作品相关推荐-下一页 (.parse_qs(next_url) 用法) -next_qs = aapi.parse_qs(json_result.next_url) -json_result = aapi.illust_related(**next_qs) +next_qs = api.parse_qs(json_result.next_url) +json_result = api.illust_related(**next_qs) print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 用户详情 -json_result = aapi.user_detail(660788) +json_result = api.user_detail(660788) print(json_result) user = json_result.user -print("%s(@%s) region=%s" % (user.name, user.account, json_result.profile.region)) +print(f"{user.name}(@{user.account}) region={json_result.profile.region}") # 用户作品列表 -json_result = aapi.user_illusts(660788) +json_result = api.user_illusts(660788) print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 用户收藏列表 -json_result = aapi.user_bookmarks_illust(2088434) +json_result = api.user_bookmarks_illust(2088434) print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 用户收藏列表中的小说 -json_result = aapi.user_bookmarks_novel(42862448) +json_result = api.user_bookmarks_novel(42862448) print(json_result) novel = json_result.novels[0] -print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) +print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") # 2016-07-15 日的过去一周排行 -json_result = aapi.illust_ranking('week', date='2016-07-15') +json_result = api.illust_ranking('week', date='2016-07-15') print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 关注用户的新作 (需要login) -json_result = aapi.illust_follow(req_auth=True) +json_result = api.illust_follow(req_auth=True) print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 标签 "水着" 搜索 -json_result = aapi.search_illust('水着', search_target='partial_match_for_tags') +json_result = api.search_illust('水着', search_target='partial_match_for_tags') print(json_result) illust = json_result.illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 用户 "gomzi" 搜索 -json_result = aapi.search_user("gomzi") +json_result = api.search_user("gomzi") print(json_result) illust = json_result.user_previews[0].illusts[0] -print(">>> %s, origin url: %s" % (illust.title, illust.image_urls['large'])) +print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 展示小说评论区 -json_result = aapi.novel_comments(16509454, include_total_comments=True) -print("Total comments = {}".format(json_result["total_comments"])) -for comment in json_result["comments"]: - if comment["parent_comment"] != dict(): - print("{user} replied to {target} at {time} : {content}".format(user = comment["user"]["name"], time = comment["date"], content = comment["comment"], target = comment["parent_comment"]["user"]["name"])) +json_result = api.novel_comments(16509454, include_total_comments=True) +print(f"Total comments = {json_result.total_comments}") +for comment in json_result.comments: + if comment.parent_comment: + print(f"{comment.user.name} replied to {comment.parent_comment.user.name} at {comment.date} : {comment.comment}") else: - print("{user} at {time} : {content}".format(user=comment["user"]["name"], time=comment["date"], content=comment["comment"])) + print(f"{comment.user.name} at {comment.date} : {comment.comment}") ``` ## Package Publishing Instructions diff --git a/demo.py b/demo.py index d014e73..a9c2d52 100644 --- a/demo.py +++ b/demo.py @@ -29,7 +29,7 @@ def appapi_illust(aapi): json_result = aapi.illust_detail(59580629) print(json_result) illust = json_result.illust - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") json_result = aapi.illust_comments(59580629) print(json_result) @@ -38,14 +38,14 @@ def appapi_illust(aapi): # json_result = aapi.ugoira_metadata(51815717) # print(json_result) # metadata = json_result.ugoira_metadata - # print(">>> frames=%d %s" % (len(metadata.frames), metadata.zip_urls.medium)) + # print(f">>> frames={len(metadata.frames)} {metadata.zip_urls.medium}") def appapi_recommend(aapi): json_result = aapi.illust_recommended(bookmark_illust_ids=[59580629]) print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -53,12 +53,12 @@ def appapi_recommend(aapi): json_result = aapi.illust_recommended(**next_qs) # print(json_result) illust = json_result.illusts[0] - print(" > {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f" > {illust.title}, origin url: {illust.image_urls.large}") json_result = aapi.illust_related(59580629) print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -66,19 +66,19 @@ def appapi_recommend(aapi): json_result = aapi.illust_related(**next_qs) # print(json_result) illust = json_result.illusts[0] - print(" > {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f" > {illust.title}, origin url: {illust.image_urls.large}") def appapi_users(aapi): json_result = aapi.user_detail(275527) print(json_result) user = json_result.user - print("{}(@{}) region={}".format(user.name, user.account, json_result.profile.region)) + print(f"{user.name}(@{user.account}) region={json_result.profile.region}") json_result = aapi.user_illusts(275527) print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -86,29 +86,29 @@ def appapi_users(aapi): json_result = aapi.user_illusts(**next_qs) # print(json_result) illust = json_result.illusts[0] - print(" > {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f" > {illust.title}, origin url: {illust.image_urls.large}") json_result = aapi.user_bookmarks_illust(2088434) print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f" > {illust.title}, origin url: {illust.image_urls.large}") json_result = aapi.user_bookmarks_novel(42862448) print(json_result) novel = json_result.novels[0] - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") json_result = aapi.user_following(7314824) print(json_result) user_preview = json_result.user_previews[0] - print(">>> {}(@{})".format(user_preview.user.name, user_preview.user.account)) + print(f">>> {user_preview.user.name}(@{user_preview.user.account})") next_qs = aapi.parse_qs(json_result.next_url) if next_qs is not None: json_result = aapi.user_following(**next_qs) # print(json_result) user_preview = json_result.user_previews[0] - print(" > {}(@{})".format(user_preview.user.name, user_preview.user.account)) + print(f">>> {user_preview.user.name}(@{user_preview.user.account})") json_result = aapi.user_follower(275527) print(json_result) @@ -129,12 +129,12 @@ def appapi_search(aapi): for trend_tag in response.trend_tags[:10]: if not first_tag: first_tag = trend_tag.tag - print("{} - {}(id={})".format(trend_tag.tag, trend_tag.illust.title, trend_tag.illust.id)) + print(f"{trend_tag.tag} - {trend_tag.illust.title}(id={trend_tag.illust.id})") json_result = aapi.search_illust(first_tag, search_target="partial_match_for_tags") print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -142,13 +142,13 @@ def appapi_search(aapi): json_result = aapi.search_illust(**next_qs) # print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # novel json_result = aapi.search_novel("FGO", search_target="keyword") print(json_result) novel = json_result.novels[0] - print(">>> {}, origin url: {}".format(novel.title, novel.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -156,23 +156,23 @@ def appapi_search(aapi): json_result = aapi.search_novel(**next_qs) # print(json_result) novel = json_result.novels[0] - print(">>> {}, origin url: {}".format(novel.title, novel.image_urls["large"])) + print(f">>> {novel.title}, origin url: {novel.image_urls['large']}") json_result = aapi.search_illust("AI生成", search_target="exact_match_for_tags", search_ai_type=0) # 关闭AI搜索选项后,将过滤掉所有illust_ai_type=2的插画,而illust_ai_type=1 or 0 的插画将被保留 # 但是,加入了"AI生成"的tag却没有在作品提交时打开“AI生成”的开关的作品不会被筛选出结果列表 - print(json_result["illusts"][0]) + print(json_result.illusts[0]) def appapi_user_search(aapi): json_result = aapi.illust_ranking("day_male") name = json_result.illusts[0].user.name - print(">>> %s" % name) + print(f">>> {name}") json_result = aapi.search_user(name) print(json_result) illust = json_result.user_previews[0].illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -180,14 +180,14 @@ def appapi_user_search(aapi): json_result = aapi.search_user(**next_qs) # print(json_result) illust = json_result.user_previews[0].illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") def appapi_ranking(aapi): json_result = aapi.illust_ranking("day_male") print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -195,20 +195,20 @@ def appapi_ranking(aapi): json_result = aapi.illust_ranking(**next_qs) # print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # 2016-07-15 日的过去一周排行 json_result = aapi.illust_ranking("week", date="2016-07-15") print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") def appapi_auth_api(aapi): json_result = aapi.illust_follow(req_auth=True) print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") # get next page next_qs = aapi.parse_qs(json_result.next_url) @@ -216,100 +216,86 @@ def appapi_auth_api(aapi): json_result = aapi.illust_follow(req_auth=True, **next_qs) # print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") json_result = aapi.illust_recommended(req_auth=True) print(json_result) illust = json_result.illusts[0] - print(">>> {}, origin url: {}".format(illust.title, illust.image_urls["large"])) + print(f">>> {illust.title}, origin url: {illust.image_urls.large}") def appapi_bookmark_add(aapi): illust_id = 74187223 tags = ["Fate/GO", "50000users入り", "私服"] - json_result = aapi.illust_bookmark_add(illust_id, tags=tags) + aapi.illust_bookmark_add(illust_id, tags=tags) json_result = aapi.illust_bookmark_detail(illust_id) print(json_result.bookmark_detail) - print( - ">>> %s, tags added: %s" - % ( - illust_id, - [tag.name for tag in json_result.bookmark_detail.tags if tag.is_registered], - ) - ) + tags_added = [tag.name for tag in json_result.bookmark_detail.tags if tag.is_registered] + print(f">>> {illust_id}, tags added: {tags_added}") def appapi_novel(aapi): json_result = aapi.novel_recommended() print(json_result) novel = json_result.novels[0] - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") # get next page next_qs = aapi.parse_qs(json_result.next_url) if next_qs is not None: json_result = aapi.novel_recommended(**next_qs) novel = json_result.novels[0] - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") json_result = aapi.user_novels(59216290) print(json_result) novel = json_result.novels[0] - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") # get next page next_qs = aapi.parse_qs(json_result.next_url) if next_qs is not None: json_result = aapi.user_novels(**next_qs) novel = json_result.novels[0] - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") json_result = aapi.novel_series(1206600) print(json_result) detail = json_result.novel_series_detail - print(">>> {}, total_character_count: {}".format(detail.title, detail.total_character_count)) + print(f">>> {detail.title}, total_character_count: {detail.total_character_count}") # get next page next_qs = aapi.parse_qs(json_result.next_url) if next_qs is not None: json_result = aapi.novel_series(**next_qs) detail = json_result.novel_series_detail - print(">>> {}, total_character_count: {}".format(detail.title, detail.total_character_count)) + print(f">>> {detail.title}, total_character_count: {detail.total_character_count}") novel_id = 12438689 json_result = aapi.novel_detail(novel_id) print(json_result) novel = json_result.novel - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") json_result = aapi.novel_text(novel_id) print(json_result) - print(">>> {}, novel_text: {}".format(novel.title, json_result.novel_text)) + print(f">>> {novel.title}, novel_text: {json_result.novel_text}") json_result = aapi.novel_follow() print(json_result) novel = json_result.novels[0] - print(">>> {}, text_length: {}, series: {}".format(novel.title, novel.text_length, novel.series)) + print(f">>> {novel.title}, text_length: {novel.text_length}, series: {novel.series}") # List the comments of the novel json_result = aapi.novel_comments(16509454, include_total_comments=True) - print("Total comments = {}".format(json_result["total_comments"])) - for comment in json_result["comments"]: - if comment["parent_comment"]: - print( - "{user} replied to {target} at {time} : {content}".format( - user=comment["user"]["name"], - time=comment["date"], - content=comment["comment"], - target=comment["parent_comment"]["user"]["name"], - ) - ) + print(f"Total comments = {json_result.total_comments}") + for comment in json_result.comments: + if comment.parent_comment: + text = f"{comment.user.name} replied to {comment.parent_comment.user.name} at {comment.date} : {comment.comment}" + print(text) else: - print( - "{user} at {time} : {content}".format( - user=comment["user"]["name"], time=comment["date"], content=comment["comment"] - ) - ) + text = f"{comment.user.name} at {comment.date} : {comment.comment}" + print(text) def main(): diff --git a/download_illusts.py b/download_illusts.py index 3a75755..a753801 100644 --- a/download_illusts.py +++ b/download_illusts.py @@ -31,8 +31,8 @@ def main(): # download top3 day rankings to 'illusts' dir for idx, illust in enumerate(json_result.illusts[:4]): - image_url = illust.meta_single_page.get("original_image_url", illust.image_urls.large) - print("{}: {}".format(illust.title, image_url)) + image_url = illust.meta_single_pageoriginal_image_url or illust.image_urls.large + print(f"{illust.title}: {image_url}") # try four args in MR#102 if idx == 0: @@ -40,16 +40,18 @@ def main(): elif idx == 1: url_basename = os.path.basename(image_url) extension = os.path.splitext(url_basename)[1] - name = "illust_id_%d_%s%s" % (illust.id, illust.title, extension) + name = f"illust_id_{illust.id}_{illust.title}{extension}" api.download(image_url, path=directory, name=name) elif idx == 2: - api.download(image_url, path=directory, fname="illust_%s.jpg" % (illust.id)) + fname = f"illust_{illust.id}.jpg" + api.download(image_url, path=directory, fname=fname) else: # path will not work due to fname is a handler + fname = f"{directory}/illust_{illust.id}.jpg" api.download( image_url, path="/foo/bar", - fname=open("{}/illust_{}.jpg".format(directory, illust.id), "wb"), + fname=open(fname, "wb"), ) diff --git a/example_bypass_sni.py b/example_bypass_sni.py index 3fd3b53..849e2d9 100644 --- a/example_bypass_sni.py +++ b/example_bypass_sni.py @@ -19,7 +19,9 @@ def main(): # api.login(_USERNAME, _PASSWORD) print(api.auth(refresh_token=_REFRESH_TOKEN)) - json_result = api.illust_ranking("day", date=(datetime.now() - timedelta(days=5)).strftime("%Y-%m-%d")) + date = datetime.now() - timedelta(days=5) + date_str = date.strftime("%Y-%m-%d") + json_result = api.illust_ranking("day", date=date_str) print("Printing image titles and tags with English tag translations present when available") diff --git a/example_tag_translations.py b/example_tag_translations.py index cea5bf7..8d34a41 100644 --- a/example_tag_translations.py +++ b/example_tag_translations.py @@ -19,7 +19,9 @@ def main(): aapi.set_accept_language("en-us") # zh-cn aapi.auth(refresh_token=_REFRESH_TOKEN) - json_result = aapi.illust_ranking("day", date=(datetime.now() - timedelta(days=5)).strftime("%Y-%m-%d")) + date = datetime.now() - timedelta(days=5) + date_str = date.strftime("%Y-%m-%d") + json_result = aapi.illust_ranking("day", date=date_str) print("Printing image titles and tags with English tag translations present when available") diff --git a/notebooks/pixivCrawler.ipynb b/notebooks/pixivCrawler.ipynb index c5fdd77..d358f0d 100644 --- a/notebooks/pixivCrawler.ipynb +++ b/notebooks/pixivCrawler.ipynb @@ -114,7 +114,7 @@ " elif type == \"manga\":\n", " total = user.profile.total_manga\n", " else:\n", - " raise Exception(\"Unsupported type=%d\" % type)\n", + " raise Exception(f\"Unsupported type={type}\")\n", " self.randSleep(0.1)\n", "\n", " with tqdm(total=total, desc=\"api.user_illusts\") as pbar:\n", diff --git a/notebooks/pixivDownloader.ipynb b/notebooks/pixivDownloader.ipynb index 48cf232..9fc66f7 100644 --- a/notebooks/pixivDownloader.ipynb +++ b/notebooks/pixivDownloader.ipynb @@ -13,13 +13,13 @@ "metadata": {}, "outputs": [], "source": [ + "import json\n", "import os\n", "import random\n", "import sqlite3 as lite\n", "import time\n", "\n", "import pandas as pd\n", - "from sqlalchemy import create_engine\n", "\n", "from pixivpy3 import *\n", "\n", @@ -53,9 +53,9 @@ "\n", " def getImageUrl(self, illust, origin=True):\n", " if origin:\n", - " return illust[\"meta_single_page\"].get(\"original_image_url\", illust[\"image_urls\"][\"large\"])\n", + " return illust.meta_single_page.original_image_url or illust.image_urls.large\n", " else: # square\n", - " return illust[\"image_urls\"][\"square_medium\"]\n", + " return illust.image_urls.square_medium\n", "\n", " def StartDownload(self, path, origin=True):\n", " if not os.path.exists(path):\n", diff --git a/pixivpy3/aapi.py b/pixivpy3/aapi.py index a2d4d6f..98f6eba 100644 --- a/pixivpy3/aapi.py +++ b/pixivpy3/aapi.py @@ -88,14 +88,15 @@ def no_auth_requests_call( return self.requests_call(method, url, headers_, params, data) else: self.require_auth() - headers_["Authorization"] = "Bearer %s" % self.access_token + headers_["Authorization"] = f"Bearer {self.access_token}" return self.requests_call(method, url, headers_, params, data) def parse_result(self, res: Response) -> ParsedJson: try: return self.parse_json(res.text) except Exception as e: - raise PixivError("parse_json() error: %s" % e, header=res.headers, body=res.text) + msg = f"parse_json() error: {e}" + raise PixivError(msg, header=res.headers, body=res.text) @classmethod def format_bool(cls, bool_value: bool | str | None) -> _BOOL: @@ -132,7 +133,7 @@ def user_detail( filter: _FILTER = "for_ios", req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/detail" % self.hosts + url = f"{self.hosts}/v1/user/detail" params = { "user_id": user_id, "filter": filter, @@ -150,7 +151,7 @@ def user_illusts( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/illusts" % self.hosts + url = f"{self.hosts}/v1/user/illusts" params = { "user_id": user_id, "filter": filter, @@ -173,7 +174,7 @@ def user_bookmarks_illust( tag: str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/bookmarks/illust" % self.hosts + url = f"{self.hosts}/v1/user/bookmarks/illust" params = { "user_id": user_id, "restrict": restrict, @@ -196,7 +197,7 @@ def user_bookmarks_novel( tag: str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/bookmarks/novel" % self.hosts + url = f"{self.hosts}/v1/user/bookmarks/novel" params = { "user_id": user_id, "restrict": restrict, @@ -216,7 +217,7 @@ def user_related( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/related" % self.hosts + url = f"{self.hosts}/v1/user/related" params = { "filter": filter, # Pixiv warns to put seed_user_id at the end -> put offset here @@ -232,7 +233,7 @@ def user_recommended( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/recommended" % self.hosts + url = f"{self.hosts}/v1/user/recommended" params = { "filter": filter, } @@ -250,7 +251,7 @@ def illust_follow( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v2/illust/follow" % self.hosts + url = f"{self.hosts}/v2/illust/follow" params: dict[str, str | int] = { "restrict": restrict, } @@ -261,7 +262,7 @@ def illust_follow( # 作品详情 (类似PAPI.works(),iOS中未使用) def illust_detail(self, illust_id: int | str, req_auth: bool = True) -> ParsedJson: - url = "%s/v1/illust/detail" % self.hosts + url = f"{self.hosts}/v1/illust/detail" params = { "illust_id": illust_id, } @@ -276,7 +277,7 @@ def illust_comments( include_total_comments: str | bool | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/illust/comments" % self.hosts + url = f"{self.hosts}/v1/illust/comments" params = { "illust_id": illust_id, } @@ -297,7 +298,7 @@ def illust_related( viewed: str | list[str] | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v2/illust/related" % self.hosts + url = f"{self.hosts}/v2/illust/related" params: dict[str, Any] = { "illust_id": illust_id, "filter": filter, @@ -331,9 +332,9 @@ def illust_recommended( req_auth: bool = True, ) -> ParsedJson: if req_auth: - url = "%s/v1/illust/recommended" % self.hosts + url = f"{self.hosts}/v1/illust/recommended" else: - url = "%s/v1/illust/recommended-nologin" % self.hosts + url = f"{self.hosts}/v1/illust/recommended-nologin" params: dict[str, Any] = { "content_type": content_type, "include_ranking_label": self.format_bool(include_ranking_label), @@ -373,7 +374,7 @@ def novel_comments( include_total_comments: str | bool | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/novel/comments" % self.hosts + url = f"{self.hosts}/v1/novel/comments" params = { "novel_id": novel_id, } @@ -396,7 +397,7 @@ def novel_recommended( include_privacy_policy: str | list[int | str] | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/novel/recommended" % self.hosts + url = f"{self.hosts}/v1/novel/recommended" params: dict[str, Any] = { "include_ranking_label": self.format_bool(include_ranking_label), "filter": filter, @@ -431,7 +432,7 @@ def illust_ranking( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/illust/ranking" % self.hosts + url = f"{self.hosts}/v1/illust/ranking" params: dict[str, Any] = { "mode": mode, "filter": filter, @@ -445,7 +446,7 @@ def illust_ranking( # 趋势标签 (Search - tags) def trending_tags_illust(self, filter: _FILTER = "for_ios", req_auth: bool = True) -> ParsedJson: - url = "%s/v1/trending-tags/illust" % self.hosts + url = f"{self.hosts}/v1/trending-tags/illust" params = { "filter": filter, } @@ -474,7 +475,7 @@ def search_illust( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/search/illust" % self.hosts + url = f"{self.hosts}/v1/search/illust" params: dict[str, Any] = { "word": word, "search_target": search_target, @@ -517,7 +518,7 @@ def search_novel( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/search/novel" % self.hosts + url = f"{self.hosts}/v1/search/novel" params: dict[str, Any] = { "word": word, "search_target": search_target, @@ -546,7 +547,7 @@ def search_user( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/search/user" % self.hosts + url = f"{self.hosts}/v1/search/user" params: dict[str, Any] = { "word": word, "sort": sort, @@ -561,7 +562,7 @@ def search_user( # 作品收藏详情 def illust_bookmark_detail(self, illust_id: int | str, req_auth: bool = True) -> ParsedJson: - url = "%s/v2/illust/bookmark/detail" % self.hosts + url = f"{self.hosts}/v2/illust/bookmark/detail" params = { "illust_id": illust_id, } @@ -576,7 +577,7 @@ def illust_bookmark_add( tags: str | list[str] | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v2/illust/bookmark/add" % self.hosts + url = f"{self.hosts}/v2/illust/bookmark/add" data = { "illust_id": illust_id, "restrict": restrict, @@ -591,7 +592,7 @@ def illust_bookmark_add( # 删除收藏 def illust_bookmark_delete(self, illust_id: int | str, req_auth: bool = True) -> ParsedJson: - url = "%s/v1/illust/bookmark/delete" % self.hosts + url = f"{self.hosts}/v1/illust/bookmark/delete" data = { "illust_id": illust_id, } @@ -605,21 +606,21 @@ def user_follow_add( restrict: _RESTRICT = "public", req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/follow/add" % self.hosts + url = f"{self.hosts}/v1/user/follow/add" data = {"user_id": user_id, "restrict": restrict} r = self.no_auth_requests_call("POST", url, data=data, req_auth=req_auth) return self.parse_result(r) # 取消关注用户 def user_follow_delete(self, user_id: int | str, req_auth: bool = True) -> ParsedJson: - url = "%s/v1/user/follow/delete" % self.hosts + url = f"{self.hosts}/v1/user/follow/delete" data = {"user_id": user_id} r = self.no_auth_requests_call("POST", url, data=data, req_auth=req_auth) return self.parse_result(r) # 设置用户选项中是否展现AI生成作品 def user_edit_ai_show_settings(self, setting: _BOOL, req_auth: bool = True) -> ParsedJson: - url = "%s/v1/user/ai-show-settings/edit" % self.hosts + url = f"{self.hosts}/v1/user/ai-show-settings/edit" data = {"show_ai": setting} r = self.no_auth_requests_call("POST", url, data=data, req_auth=req_auth) return self.parse_result(r) @@ -632,7 +633,7 @@ def user_bookmark_tags_illust( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/bookmark-tags/illust" % self.hosts + url = f"{self.hosts}/v1/user/bookmark-tags/illust" params: dict[str, Any] = { "user_id": user_id, "restrict": restrict, @@ -650,7 +651,7 @@ def user_following( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/following" % self.hosts + url = f"{self.hosts}/v1/user/following" params = { "user_id": user_id, "restrict": restrict, @@ -669,7 +670,7 @@ def user_follower( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/follower" % self.hosts + url = f"{self.hosts}/v1/user/follower" params = { "user_id": user_id, "filter": filter, @@ -687,7 +688,7 @@ def user_mypixiv( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/mypixiv" % self.hosts + url = f"{self.hosts}/v1/user/mypixiv" params = { "user_id": user_id, } @@ -705,7 +706,7 @@ def user_list( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v2/user/list" % self.hosts + url = f"{self.hosts}/v2/user/list" params = { "user_id": user_id, "filter": filter, @@ -718,7 +719,7 @@ def user_list( # 获取ugoira信息 def ugoira_metadata(self, illust_id: int | str, req_auth: bool = True) -> ParsedJson: - url = "%s/v1/ugoira/metadata" % self.hosts + url = f"{self.hosts}/v1/ugoira/metadata" params = { "illust_id": illust_id, } @@ -734,7 +735,7 @@ def user_novels( offset: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/user/novels" % self.hosts + url = f"{self.hosts}/v1/user/novels" params = { "user_id": user_id, "filter": filter, @@ -752,7 +753,7 @@ def novel_series( last_order: str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v2/novel/series" % self.hosts + url = f"{self.hosts}/v2/novel/series" params = { "series_id": series_id, "filter": filter, @@ -764,7 +765,7 @@ def novel_series( # 小说详情 def novel_detail(self, novel_id: int | str, req_auth: bool = True) -> ParsedJson: - url = "%s/v2/novel/detail" % self.hosts + url = f"{self.hosts}/v2/novel/detail" params = { "novel_id": novel_id, } @@ -778,7 +779,7 @@ def novel_new( max_novel_id: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/novel/new" % self.hosts + url = f"{self.hosts}/v1/novel/new" params: dict[str, Any] = { "filter": filter, } @@ -795,7 +796,7 @@ def novel_follow( offset: int | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/novel/follow" % self.hosts + url = f"{self.hosts}/v1/novel/follow" params: dict[str, Any] = {"restrict": restrict, "offset": offset} r = self.no_auth_requests_call("GET", url, params=params, req_auth=req_auth) return self.parse_result(r) @@ -804,7 +805,7 @@ def novel_follow( # raw=True, return html content directly def webview_novel(self, novel_id: int | str, raw: bool = False, req_auth: bool = True) -> ParsedJson: # change new endpoint due to #337 - url = "%s/webview/v2/novel" % self.hosts + url = f"{self.hosts}/webview/v2/novel" params = { "id": novel_id, "viewer_version": "20221031_ai", @@ -818,7 +819,8 @@ def webview_novel(self, novel_id: int | str, raw: bool = False, req_auth: bool = json_str = re.search(r"novel:\s({.+}),\s+isOwnWork", r.text).groups()[0].encode() # type: ignore return self.parse_json(json_str) except Exception as e: - raise PixivError("Extract novel content error: %s" % e, header=r.headers, body=r.text) + msg = f"Extract novel content error: {e}" + raise PixivError(msg, header=r.headers, body=r.text) # 小说正文 (deprecated) def novel_text(self, novel_id: int | str, req_auth: bool = True) -> ParsedJson: @@ -836,7 +838,7 @@ def illust_new( max_illust_id: int | str | None = None, req_auth: bool = True, ) -> ParsedJson: - url = "%s/v1/illust/new" % self.hosts + url = f"{self.hosts}/v1/illust/new" params: dict[str, Any] = { "content_type": content_type, "filter": filter, diff --git a/pixivpy3/api.py b/pixivpy3/api.py index 7762776..d5b0344 100644 --- a/pixivpy3/api.py +++ b/pixivpy3/api.py @@ -95,9 +95,11 @@ def requests_call( **self.requests_kwargs, ) else: - raise PixivError("Unknown method: %s" % method) + msg = f"Unknown method: {method}" + raise PixivError(msg) except Exception as e: - raise PixivError("requests {} {} error: {}".format(method, url, e)) + msg = f"requests {method} {url} error: {e}" + raise PixivError(msg) def set_auth(self, access_token: str, refresh_token: str | None = None) -> None: self.access_token = access_token @@ -135,7 +137,7 @@ def auth( # noinspection PyUnresolvedReferences auth_hosts = self.hosts # BAPI解析成IP的场景 headers_["host"] = "oauth.secure.pixiv.net" - url = "%s/auth/token" % auth_hosts + url = f"{auth_hosts}/auth/token" data = { "get_secure_url": 1, "client_id": self.client_id, @@ -155,14 +157,16 @@ def auth( r = self.requests_call("POST", url, headers=headers_, data=data) if r.status_code not in {200, 301, 302}: if data["grant_type"] == "password": + msg = f"[ERROR] auth() failed! check username and password.\nHTTP {r.status_code}: {r.text}" raise PixivError( - "[ERROR] auth() failed! check username and password.\nHTTP {}: {}".format(r.status_code, r.text), + msg, header=r.headers, body=r.text, ) else: + msg = f"[ERROR] auth() failed! check refresh_token.\nHTTP {r.status_code}: {r.text}" raise PixivError( - "[ERROR] auth() failed! check refresh_token.\nHTTP {}: {}".format(r.status_code, r.text), + msg, header=r.headers, body=r.text, ) @@ -175,8 +179,9 @@ def auth( self.access_token = token.response.access_token self.refresh_token = token.response.refresh_token except json.JSONDecodeError: + msg = f"Get access_token error! Response: {token}" raise PixivError( - "Get access_token error! Response: %s" % token, + msg, header=r.headers, body=r.text, ) diff --git a/tests/conftest.py b/tests/conftest.py index 4389791..d8f5435 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -37,9 +37,8 @@ def pixiv_url_common(): @pytest.fixture def pixiv_image_url(): - return "https://i.pximg.net/c/600x1200_90/img-master/img/2023/01/01/12/38/38/{}_p0_master1200.jpg".format( - random.randint(100_000_000, 999_999_999) - ) + img_id = random.randint(100_000_000, 999_999_999) + return f"https://i.pximg.net/c/600x1200_90/img-master/img/2023/01/01/12/38/38/{img_id}_p0_master1200.jpg" @pytest.fixture