参考

概述

看P站其实只是个冷门需求,不过本篇文章主要是为了提供一个解决方案:让原本无法在围城内使用的库通过添加socks5代理支持而变成可用。

本地验证

之前的文章里介绍了一个国内可以直接使用的P站API,但是发现有些排行模式没有数据,比如“受男性欢迎”(mode=male),导致搜出来的图不是我想要的那些,于是开始在Github上找其它方案。
很容易就找到了一个pixiv-app-api,是浏览器端和node.js都可以使用的,基于Pixiv官方APP的API。
当然了,由于众所周知的原因,Pixiv是没办法从国内直接访问到的。不过这难不住码农,只要用某乳就可以了。在完全不修改模块内部代码的情况下,只要在调用pixiv-app-api之前,用下面的代码设置一下全局代理即可:

process.env.http_proxy = 'http://127.0.0.1:1080'
process.env.https_proxy = 'http://127.0.0.1:1080'

然后本地验证了一下,嗯,可以正常登录和查询!
那么,下一步就是如何在服务器上也搞起了。

服务器安装某乳客户端

其实某乳的python版中就自带客户端的,没错,就是那个local.py(在子目录里)!
不过注意,要克隆某乳的manyuser分支,master分支不支持部分参数,比如混淆参数。
下面这段shell命令行就可以后台启动客户端了:

python local.py -b 127.0.0.1 -l 1080 -s <某乳服务器IP> -p <服务器端口> -k <密码> -m <加密> -O <协议> -o <混淆> -d start

其中:

  • 某乳服务器IP服务器端口密码加密协议混淆这几个和Windows版客户端的几个参数一一对应即可。
  • -b 127.0.0.1表示本地代理服务只允许服务器本地应用使用。如果你不想你的服务器被人家扫出来然后反复蹂躏,就不要设成0.0.0.0。
  • -l 1080是本地代理的端口号,socks5代理习惯上用1080端口,当然你可以改成别的。

不过我是用pm2启动的,这样以后重启就不需要记这么长的命令行了:

pm2 start "local.py -b 127.0.0.1 -l 1080 -s <某乳服务器IP> -p <服务器端口> -k <密码> -m <加密> -O <协议> -o <混淆> start" --name mySocks5Proxy

启动以后可以在服务器上用curl来验证一下:

curl -x socks5h://localhost:1080 https://www.google.com

上面就是curl中使用socks5代理的方法,socks5h表示由某乳服务器(而不是客户端)负责解析域名。

在node.js中使用socks5代理

如前一步所示,python版某乳客户端只有socks5代理,而没有http代理。也就是说在“本地验证”一节中采用的改环境变量的简单方案是没法直接使用的,有两条路可以走:

  1. 使用privoxy把socks5代理转成http代理,其实最常用的C#版Windows客户端就是这个方案
  2. 魔改pixiv-app-api的代码,直接用socks5代理!

因为我不想引入新的外部依赖,因此选择第二条路,直接改代码。
分析了一下pixiv-app-api的源码,它是使用axios作为http请求库,axios是支持socks5代理的,只要安装socks-proxy-agent这个npm模块即可,示例代码如下:

const SocksProxyAgent = require('socks-proxy-agent')
const httpsAgent = new SocksProxyAgent('socks://127.0.0.1:1080')
axios.get('https://www.google.com', { httpsAgent }).then(console.log)

魔改pixiv-app-api,添加socks5代理支持

这一步就比较简单了:

  1. 首先确保你已经安装了pixiv-app-api和socks-proxy-agent模块

    cnpm install pixiv-app-api socks-proxy-agent
  2. 找到node_modules/pixiv-app-api/dist/index.js
  3. 第一处:加三行代码

    const baseURL = 'https://app-api.pixiv.net/';
    const SocksProxyAgent = require('socks-proxy-agent'); // << 添加一行
    const httpsAgent = new SocksProxyAgent('socks://127.0.0.1:1080'); // << 添加一行
    const instance = axios_1.default.create({
        baseURL,
        headers: {
            'App-OS': 'ios',
            'App-OS-Version': '9.3.3',
            'App-Version': '6.0.9',
        },
        httpsAgent // << 添加一行
    });
  4. 第二处:小小修改,加个参数

    const axiosResponse = await axios_1.default.post('https://oauth.secure.pixiv.net/auth/token', querystring_1.stringify(decamelize_keys_1.default(data)), { headers, httpsAgent }); // << 此行加一个httpsAgent参数

经过以上修改,pixiv-app-api终于可以在服务器上正常使用了,以下是示例代码,注意调用api只是用来查询插画排行榜数据,图片实际上是从墙内可用的CDN即i.pixiv.cat上下载的。

const path = require('path')
const rp = require('request-promise')
const fs = require('fs-extra')
const PixivAppApi = require('pixiv-app-api')

;(async () => {
  const pixiv = new PixivAppApi('pixiv网站账户', 'pixiv网站密码', { camelcaseKeys: true })
  let res = await pixiv.login()
  console.log('login: ', res)
  res = await pixiv.illustRanking({ mode: 'week_r18' })
  const ids = res.illusts.map(o => o.id + '')
  const urls = res.illusts.map(o => o.imageUrls.medium).map(u => u.replace('i.pximg.net', 'i.pixiv.cat'))
  res = await Promise.all(urls.map(u => rp.get(u, { encoding: null })))
  await Promise.all(res.map((buf, i) => {
    const fn = path.join(__dirname, 'pixiv_tmp', ids[i] + '.jpg')
    return fs.writeFile(fn, buf)
  }))
})()

rockswang
1.4k 声望154 粉丝

To play is Human