parent
b8573751dc
commit
61cb3a3f23
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
|
||||
]
|
||||
}
|
8
go.mod
8
go.mod
|
@ -1,9 +1,13 @@
|
|||
module git.xiaozou.top/aila/pixiv
|
||||
|
||||
go 1.18
|
||||
go 1.21.6
|
||||
|
||||
require (
|
||||
github.com/tidwall/gjson v1.17.0
|
||||
golang.org/x/net v0.20.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/tidwall/gjson v1.17.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
)
|
||||
|
|
2
go.sum
2
go.sum
|
@ -4,3 +4,5 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
|
|
414
novel.go
414
novel.go
|
@ -4,14 +4,17 @@ package pixiv
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type PixivAPI struct {
|
||||
|
@ -23,11 +26,65 @@ type PixivAPI struct {
|
|||
|
||||
// 初始化
|
||||
func NewApp() *PixivAPI {
|
||||
Client := &http.Client{
|
||||
Timeout: time.Second * 5,
|
||||
}
|
||||
|
||||
Client := &http.Client{}
|
||||
Client.Timeout = 5000 * time.Millisecond
|
||||
return &PixivAPI{httpClient: Client}
|
||||
|
||||
}
|
||||
|
||||
// 设置http超时时间 毫秒
|
||||
func (p *PixivAPI) SetHttpTimeout(t int) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.httpClient.Timeout = time.Duration(t) * time.Millisecond
|
||||
}
|
||||
|
||||
// 设置http代理 http://127.0.0.1:8080
|
||||
func (p *PixivAPI) SetProxy_Http(address string, user string, pwd string) error {
|
||||
proxyURL, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proxyURL.User = url.UserPassword(user, pwd)
|
||||
transport := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
|
||||
p.httpClient.Transport = transport
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置socks5代理 socks5://proxy.example.com:1080
|
||||
func (p *PixivAPI) SetProxy_Socks5(address string, user string, pwd string) error {
|
||||
proxyURL, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
auth := proxy.Auth{
|
||||
User: user, // Socks5账号
|
||||
Password: pwd, // Socks5密码
|
||||
}
|
||||
dialer, err := proxy.SOCKS5("tcp", proxyURL.Host, &auth, proxy.Direct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transport := &http.Transport{
|
||||
Dial: dialer.Dial,
|
||||
}
|
||||
p.httpClient.Transport = transport
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置代理
|
||||
func (p *PixivAPI) SetProxy(address string, user string, pwd string) error {
|
||||
u, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Scheme == "http" || u.Scheme == "https" {
|
||||
return p.SetProxy_Http(address, user, pwd)
|
||||
}
|
||||
if u.Scheme == "socks5" {
|
||||
return p.SetProxy_Socks5(address, user, pwd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置Refresh_Token
|
||||
|
@ -70,6 +127,40 @@ func (p *PixivAPI) GetAccess_Token() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// 打印Access_Token
|
||||
func (p *PixivAPI) PrintAccess_Token() string {
|
||||
return p.accessToken
|
||||
}
|
||||
|
||||
// 设置Access_Token
|
||||
func (p *PixivAPI) SetAccess_Token(a string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.accessToken = a
|
||||
}
|
||||
|
||||
// 检测Access_Token是否有效
|
||||
func (p *PixivAPI) TestAccess_token() bool {
|
||||
URL := "https://app-api.pixiv.net/v1/illust/recommended?include_privacy_policy=true&filter=for_android&include_ranking_illusts=true"
|
||||
req, err := http.NewRequest("GET", URL, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234 ")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取body内容
|
||||
func GetBody(body io.Reader) string {
|
||||
b, err := ioutil.ReadAll(body)
|
||||
|
@ -78,3 +169,318 @@ func GetBody(body io.Reader) string {
|
|||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// 转换到北京时间格式 2022-09-20T23:50:04+09:00 to 2022-09-20 22:50:04
|
||||
func ToBeiJing(timeStr string) string {
|
||||
// 解析时间字符串
|
||||
t, err := time.Parse(time.RFC3339, timeStr)
|
||||
if err != nil {
|
||||
return timeStr
|
||||
}
|
||||
// 将时间转换为 UTC+8
|
||||
utc8 := t.UTC().Add(8 * time.Hour)
|
||||
// 格式化为指定的时间字符串
|
||||
utc8Str := utc8.Format("2006-01-02 15:04:05")
|
||||
|
||||
return utc8Str
|
||||
}
|
||||
|
||||
// 获取系列小说
|
||||
func (p *PixivAPI) GetNovelSeries(SeriesID string) (Series, error, int) {
|
||||
var (
|
||||
last_order int
|
||||
title string
|
||||
message string
|
||||
user_id int64
|
||||
count int64
|
||||
caption string
|
||||
|
||||
list []NovelList
|
||||
series Series
|
||||
user UserInfo
|
||||
)
|
||||
for {
|
||||
URL := "https://app-api.pixiv.net/v2/novel/series?series_id=" + SeriesID + "&last_order=" + strconv.Itoa(last_order)
|
||||
req, err := http.NewRequest("GET", URL, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234 ")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
if err != nil {
|
||||
return series, err, 0
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return series, err, 0
|
||||
}
|
||||
body := GetBody(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
message = gjson.Get(body, "error.message").String()
|
||||
if message != "" {
|
||||
return series, errors.New(message), resp.StatusCode
|
||||
}
|
||||
message = gjson.Get(body, "error.user_message").String()
|
||||
if message != "" {
|
||||
return series, errors.New(message), resp.StatusCode
|
||||
}
|
||||
if len(title) == 0 {
|
||||
title = gjson.Get(body, "novel_series_detail.title").String()
|
||||
count = gjson.Get(body, "novel_series_detail.content_count").Int()
|
||||
caption = gjson.Get(body, "novel_series_detail.caption").String()
|
||||
user_id = gjson.Get(body, "novel_series_detail.user.id").Int()
|
||||
}
|
||||
novels := gjson.Get(body, "novels")
|
||||
novels.ForEach(func(key, value gjson.Result) bool {
|
||||
var tagArry []string
|
||||
tags := value.Get("tags")
|
||||
tags.ForEach(func(key2, value2 gjson.Result) bool {
|
||||
tag := value2.Get("name").String()
|
||||
tagArry = append(tagArry, tag)
|
||||
return true
|
||||
})
|
||||
list = append(list, NovelList{ID: value.Get("id").String(), Title: value.Get("title").String(), Length: value.Get("text_length").String(), Date: ToBeiJing(value.Get("create_date").String()), Caption: value.Get("caption").String(), Tags: tagArry})
|
||||
return true
|
||||
})
|
||||
if len(novels.Array()) != 30 {
|
||||
break
|
||||
}
|
||||
last_order = last_order + 30
|
||||
|
||||
}
|
||||
|
||||
user, _ = p.GetUserInfo(strconv.FormatInt(user_id, 10))
|
||||
|
||||
series.Title = title
|
||||
series.Caption = caption
|
||||
series.Novels = list
|
||||
series.ContentCount = count
|
||||
series.User = user
|
||||
return series, nil, 200
|
||||
}
|
||||
|
||||
// 获取用户小说列表
|
||||
func (p *PixivAPI) GetUserNovel(UserID string) (UserNovel, error, int) {
|
||||
var (
|
||||
last_order int
|
||||
message string
|
||||
novel UserNovel
|
||||
list []NovelList
|
||||
user UserInfo
|
||||
)
|
||||
for {
|
||||
URL := "https://app-api.pixiv.net/v1/user/novels?user_id=" + UserID + "&offset=" + strconv.Itoa(last_order)
|
||||
|
||||
req, err := http.NewRequest("GET", URL, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234 ")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
if err != nil {
|
||||
return novel, err, 0
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return novel, err, 0
|
||||
}
|
||||
body := GetBody(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
|
||||
message = gjson.Get(body, "error.message").String()
|
||||
if message != "" {
|
||||
return novel, errors.New(message), resp.StatusCode
|
||||
}
|
||||
message = gjson.Get(body, "error.user_message").String()
|
||||
if message != "" {
|
||||
return novel, errors.New(message), resp.StatusCode
|
||||
}
|
||||
|
||||
novels := gjson.Get(body, "novels")
|
||||
// 遍历json数组
|
||||
novels.ForEach(func(key, value gjson.Result) bool {
|
||||
list = append(list, NovelList{ID: value.Get("id").String(), Title: value.Get("title").String(), Length: value.Get("text_length").String(), Date: ToBeiJing(value.Get("create_date").String())})
|
||||
|
||||
return true
|
||||
})
|
||||
if len(novels.Array()) != 30 {
|
||||
break
|
||||
}
|
||||
last_order = last_order + 30
|
||||
}
|
||||
user, _ = p.GetUserInfo(UserID)
|
||||
novel.Novels = list
|
||||
|
||||
novel.User = user
|
||||
|
||||
return novel, nil, 200
|
||||
}
|
||||
|
||||
// 获取系列简介
|
||||
func (p *PixivAPI) GetNovelSeriesDetail(SeriesID string) (Series, error, int) {
|
||||
var (
|
||||
title string
|
||||
message string
|
||||
user_id int64
|
||||
count int64
|
||||
caption string
|
||||
series Series
|
||||
user UserInfo
|
||||
)
|
||||
|
||||
URL := "https://app-api.pixiv.net/v2/novel/series?series_id=" + SeriesID + "&last_order=0"
|
||||
req, err := http.NewRequest("GET", URL, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234 ")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
if err != nil {
|
||||
return series, err, 0
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return series, err, 0
|
||||
}
|
||||
body := GetBody(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
message = gjson.Get(body, "error.message").String()
|
||||
if message != "" {
|
||||
return series, errors.New(message), resp.StatusCode
|
||||
}
|
||||
message = gjson.Get(body, "error.user_message").String()
|
||||
if message != "" {
|
||||
return series, errors.New(message), resp.StatusCode
|
||||
}
|
||||
|
||||
title = gjson.Get(body, "novel_series_detail.title").String()
|
||||
count = gjson.Get(body, "novel_series_detail.content_count").Int()
|
||||
caption = gjson.Get(body, "novel_series_detail.caption").String()
|
||||
user_id = gjson.Get(body, "novel_series_detail.user.id").Int()
|
||||
|
||||
user, _ = p.GetUserInfo(strconv.FormatInt(user_id, 10))
|
||||
|
||||
series.Title = title
|
||||
series.Caption = caption
|
||||
series.ContentCount = count
|
||||
series.User = user
|
||||
return series, nil, 200
|
||||
}
|
||||
|
||||
// 获取小说简介
|
||||
func (p *PixivAPI) GetNovelCaption(NovelID string) (NovelInfo, error) {
|
||||
var (
|
||||
message string
|
||||
novel NovelInfo
|
||||
user UserInfo
|
||||
)
|
||||
url := "https://app-api.pixiv.net/v2/novel/detail?novel_id=" + NovelID
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234 ")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
|
||||
if err != nil {
|
||||
return novel, err
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return novel, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body := GetBody(resp.Body)
|
||||
|
||||
message = gjson.Get(body, "error.message").String()
|
||||
if message != "" {
|
||||
return novel, errors.New(message)
|
||||
}
|
||||
message = gjson.Get(body, "error.user_message").String()
|
||||
if message != "" {
|
||||
return novel, errors.New(message)
|
||||
|
||||
}
|
||||
// 简介
|
||||
novel.Caption = gjson.Get(body, "novel.caption").String()
|
||||
// id
|
||||
novel.ID = NovelID
|
||||
// 字数
|
||||
novel.Length = gjson.Get(body, "novel.text_length").String()
|
||||
// 发布时间
|
||||
novel.Title = ToBeiJing(gjson.Get(body, "novel.create_date").String())
|
||||
// 标题
|
||||
novel.Title = gjson.Get(body, "novel.title").String()
|
||||
// 用户名
|
||||
user.Account = gjson.Get(body, "novel.user.account").String()
|
||||
// 用户简介
|
||||
user.Caption = ""
|
||||
// 用户id
|
||||
user.UserId = gjson.Get(body, "novel.user.id").Int()
|
||||
// 用户名
|
||||
user.UserName = gjson.Get(body, "novel.user.name").String()
|
||||
novel.User = user
|
||||
|
||||
return novel, nil
|
||||
}
|
||||
|
||||
// 获取小说内容
|
||||
func (p *PixivAPI) GetNovelContent(NovelID string) (string, error, int) {
|
||||
var (
|
||||
message string
|
||||
)
|
||||
url := "https://app-api.pixiv.net/v1/novel/text?novel_id=" + NovelID
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234 ")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
if err != nil {
|
||||
return "", err, 0
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err, 0
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body := GetBody(resp.Body)
|
||||
|
||||
message = gjson.Get(body, "error.message").String()
|
||||
if message != "" {
|
||||
return "", errors.New(message), resp.StatusCode
|
||||
}
|
||||
message = gjson.Get(body, "error.user_message").String()
|
||||
if message != "" {
|
||||
return "", errors.New(message), resp.StatusCode
|
||||
}
|
||||
return gjson.Get(body, "novel_text").String(), nil, resp.StatusCode
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
func (p *PixivAPI) GetUserInfo(UserId string) (UserInfo, error) {
|
||||
var (
|
||||
message string
|
||||
user UserInfo
|
||||
)
|
||||
URL := "https://app-api.pixiv.net/v1/user/detail?filter=for_android&user_id=" + UserId
|
||||
req, err := http.NewRequest("GET", URL, nil)
|
||||
req.Header.Set("accept-language", "zh_CN")
|
||||
req.Header.Set("User-Agent", "PixivAndroidApp/5.0.234")
|
||||
req.Header.Set("Authorization", "Bearer "+p.accessToken)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
body := GetBody(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
|
||||
message = gjson.Get(body, "error.message").String()
|
||||
if message != "" {
|
||||
return user, errors.New(message)
|
||||
}
|
||||
message = gjson.Get(body, "error.user_message").String()
|
||||
if message != "" {
|
||||
return user, errors.New(message)
|
||||
}
|
||||
user.Account = gjson.Get(body, "user.account").String()
|
||||
user.Caption = gjson.Get(body, "user.comment").String()
|
||||
user.TotalNovels = gjson.Get(body, "profile.total_novels").Int()
|
||||
user.UserId = gjson.Get(body, "user.id").Int()
|
||||
user.UserName = gjson.Get(body, "user.name").String()
|
||||
return user, nil
|
||||
}
|
||||
|
|
|
@ -3,16 +3,45 @@ package pixiv
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 2024年2月19日14:06:04
|
||||
// efO864tOWGoWGkG2001hFcXb3a_2lWBVbmA8V-Jx0lA
|
||||
|
||||
func TestPixiv(t *testing.T) {
|
||||
|
||||
app := NewApp()
|
||||
app.SetRefresh_Token("JMfu2g_lzwHS7ojLnUA3mifsBRPQLgbfqQLZ9W1M73E")
|
||||
err := app.GetAccess_Token()
|
||||
|
||||
// app.GetAccess_Token()
|
||||
// fmt.Println(app.PrintAccess_Token())
|
||||
// return
|
||||
app.SetAccess_Token("efO864tOWGoWGkG2001hFcXb3a_2lWBVbmA8V-Jx0lA")
|
||||
|
||||
// fmt.Println(app.GetUserInfo("62508240"))
|
||||
// info, err, code := app.GetUserNovel("9114206")
|
||||
// if err != nil {
|
||||
// fmt.Println(code, err)
|
||||
// return
|
||||
// }
|
||||
// fmt.Println(info.Novels)
|
||||
|
||||
// err := app.GetAccess_Token()
|
||||
// if err != nil {
|
||||
// fmt.Println("获取token失败", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
list, err, code := app.GetNovelSeries("10628953")
|
||||
if err != nil {
|
||||
fmt.Println("获取token失败", err)
|
||||
fmt.Println("获取系列失败", code, err)
|
||||
return
|
||||
}
|
||||
fmt.Println(app.accessToken)
|
||||
|
||||
for _, value := range list.Novels {
|
||||
fmt.Println(value.Title, value.Tags)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
45
struct.go
45
struct.go
|
@ -1,2 +1,47 @@
|
|||
// struct
|
||||
package pixiv
|
||||
|
||||
// 小说列表
|
||||
type NovelList struct {
|
||||
ID string `json:"id"` // 小说id
|
||||
Title string `json:"title"` // 小说标题
|
||||
Length string `json:"length"` // 小说字数
|
||||
Caption string `json:"caption"` // 小说简介
|
||||
Date string `json:"date"` // 发布时间
|
||||
Tags []string `json:"tags"` // 标签
|
||||
}
|
||||
|
||||
// 用户小说
|
||||
type UserNovel struct {
|
||||
Novels []NovelList `json:"novels"` //小说列表
|
||||
User UserInfo `json:"user"` //用户信息
|
||||
}
|
||||
|
||||
// 系列信息
|
||||
type Series struct {
|
||||
Title string `json:"title"` //系列标题
|
||||
Caption string `json:"caption"` //系列简介
|
||||
ContentCount int64 `json:"content_count"` //系列小说数量
|
||||
Novels []NovelList `json:"novels"` //小说列表
|
||||
User UserInfo `json:"user"` //用户信息
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
type UserInfo struct {
|
||||
UserId int64 `json:"id"` //用户id
|
||||
UserName string `json:"name"` //用户名
|
||||
Caption string `json:"caption"` //用户简介
|
||||
Account string `json:"account"` //账户名
|
||||
TotalNovels int64 `json:"total_novels"` //用户小说数量
|
||||
|
||||
}
|
||||
|
||||
// 单篇小说信息
|
||||
type NovelInfo struct {
|
||||
User UserInfo `json:"user"` //用户信息
|
||||
ID string `json:"id"` // 小说id
|
||||
Title string `json:"title"` // 小说标题
|
||||
Length string `json:"length"` // 小说字数
|
||||
Caption string `json:"caption"` // 小说简介
|
||||
Date string `json:"date"` // 发布时间
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue