first commit

main v0.0.1
aila 2024-02-19 14:53:39 +08:00
parent b8573751dc
commit 61cb3a3f23
6 changed files with 505 additions and 9 deletions

10
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
]
}

8
go.mod
View File

@ -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
View File

@ -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
View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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"` // 发布时间
}