Mikubill / pixivpy-async

Pure Python 3 Async Pixiv API
The Unlicense
148 stars 18 forks source link

当使用proxy时,设置cookies发送请求会请求失败 #22

Closed yuyuko-C closed 3 years ago

yuyuko-C commented 3 years ago

不知道应不应该反馈到这里,如果打扰了还请见谅。 因为本项目中的搜索结果的数量很明显少于实际值,我自己基于本项目中的网络请求做了一套浏览器的P站爬虫,因为需要设置cookie来绕过登录,在使用中就发现了如题所述的问题。

设置的cookie是P站返回的 PHPSSEID 。以下是我使用的一些记录。

测试: 1.使用 requests 的 proxies 后设置 cookies ,成功返回P站在已登录情况下的结果。 2.使用 pixivpy-async 的 proxies 后不设置 cookies,成功返回P站在未登录情况下的结果 3.使用 pixivpy-async 的 proxies 后设置 cookies,报错(返回403)。 4.使用 pixivpy-async 的 bypass 后设置 cookies,成功返回P站在已登录情况下的结果。

测试代码如下:

async def search_illusts(self,keyword:str,page:int,order="date_d", s_mode="s_tag_full"):
        url="https://www.pixiv.net/ajax/search/illustrations/{}".format(keyword)
        params={
            "word":keyword,
            "order":order,
            "mode":"all",
            "p":page,
            "s_mode":s_mode,
            "type":"illust_and_ugoira",
            "lang":"zh",
        }
        res = await self.requests_api('GET',url,params=params)

        return res.body.illust.data

本人推测是因为使用了 ProxyConnector 导致的报错,如果 TCPConnector 会不会报错尚不可知,因为不使用代理的情况下无法访问P站。如果能回答我的话将不胜感激。

Mikubill commented 3 years ago

Pixiv在不同环境下会返回不同的结果。我写了一段类似的代码进行测试,如下:

(可以不加改动直接运行,兼容pixivpy_async)

# !/usr/bin/env python
# -*- coding: utf-8 -*-

from pixivpy_async.sync import AppPixivAPI

class TempAPI(AppPixivAPI):
    def check_ck(self):
        url = "https://httpbin.org/cookies"
        return self.requests_('GET', url, auth=False)

    def search_ajax(self, keyword:str, page:int=1, order="date_d", s_mode="s_tag_full"):
        url="https://www.pixiv.net/ajax/search/illustrations/{}".format(keyword)
        params={
            "word":keyword,
            "order":order,
            "mode":"all",
            "p":page,
            "s_mode":s_mode,
            "type":"illust_and_ugoira",
            "lang":"zh",
        }
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) '
                          'AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/63.0.3239.132 Safari/537.36',
            'Referer': 'https://www.pixiv.net',
        }

        return self.requests_('GET', url, params=params, headers=headers, auth=False)

print(TempAPI(cookies={"PHPSESSID": "abc"}).check_ck(), "\n\n")

r = TempAPI().search_ajax("甘雨")
print([k.id for k in r.body.illust.data[:8]], "\n")

r = TempAPI(cookies={"PHPSESSID": "..."}).search_ajax("甘雨")
print([k.id for k in r.body.illust.data[:8]])

r = TempAPI(bypass=True, proxy="socks5://127.0.0.1:9011", cookies={"PHPSESSID": "..."}).search_ajax("甘雨")
print([k.id for k in r.body.illust.data[:8]])

r = TempAPI(bypass=True, cookies={"PHPSESSID": "..."}).search_ajax("甘雨")
print([k.id for k in r.body.illust.data[:8]])

r = TempAPI(proxy="socks5://127.0.0.1:9011", cookies={"PHPSESSID": "..."}).search_ajax("甘雨")
print([k.id for k in r.body.illust.data[:8]])

结果为

['93490933', '93490118', '93490062', '93489377', '93487463', '93487328', '93482209', '93480542'] 

['93491620', '93491551', '93490933', '93490118', '93490062', '93489377', '93487463', '93487328'] 
['93491620', '93491551', '93490933', '93490118', '93490062', '93489377', '93487463', '93487328'] 
['93491620', '93491551', '93490933', '93490118', '93490062', '93489377', '93487463', '93487328'] 
['93491620', '93491551', '93490933', '93490118', '93490062', '93489377', '93487463', '93487328'] 

代理环境为socks5,即 https://github.com/Mikubill/pixivpy-async/blob/master/socks5_server.py 后四次结果与登录状态下搜索结果一致,至少在socks代理环境下pixivpy_async的表现应该没有问题。

请问可以提供一下网络环境信息吗(如代理类型等)

yuyuko-C commented 3 years ago

感谢回答,经过测试后你的代码能正常使用,基于你的帮助我定位到了问题。 本条回复中包含三段信息:1.使用代理报错问题所在 2.搜索图片少的问题是否无法解决 3.一个可能为bug的地方

1. 你好 ,我已经定位到问题所在了。问题出在 headers 上,与网络代理类型并无关系。 因为我最开始基于 requests 制作时,不设置 headers 可以返回到所有结果,所以我误认为在 pixiv 中不设置 headers 并不影响结果。所以当在 aiohttp 出现问题时我并未意识到是 headers 所导致的。不过我依然不能理解的是:同样是使用代理,为何在 requests 上 与 aiohttp 会产生差别。

2. “Pixiv在不同环境下会返回不同的结果。”—— 这句话是指在 ios 环境下 pixiv 只能返回那些图片信息吗?也就是,搜索图片过少的问题是无法解决的?

3. 我将代码复制运行后发现编译器给出了错误信息

Traceback (most recent call last):
  File "e:/Python_Project/Github/pypixiv/test.py", line 53, in <module>
    print([k.id for k in r.body.illust.data[:8]])
AttributeError: 'str' object has no attribute 'body'

经过测试后发现,这个问题出现在 utils.py 文件的 paser_json 方法中,通过在 TempAPI 类中重写方法后即可正常使用,已经确认过了 Github 中依然是原代码,也许是你本地已经修复了但未同步到仓库,所以这代码在你的电脑中无需修改即可使用。 原代码:

    @staticmethod
    def parse_json(_):
        return json.loads(json.dumps(_), object_hook=JsonDict)

修改后:

    def parse_json(self,value:str):
        return json.loads(value, object_hook=JsonDict)
Mikubill commented 3 years ago
  1. 本库的网络请求都会加上一个 PixivIOSApp/7.6.2 (iOS 12.2; iPhone9,1)的Header。request和aiohttp会出现不同结果,应该是因为这个导致的服务器返回结果不一致吧
  2. 搜索数量过少是指没有搜索到正确的作品吗?或许它们在后面的页面中
  3. parse_json处理的是JSON对象,所以需要json.dumps。应该没有需要处理str的情况... https://github.com/Mikubill/pixivpy-async/blob/958783cca3204f99cf7efc698176acb988bc36c0/pixivpy_async/net.py#L58
yuyuko-C commented 3 years ago

1.我之前没有考虑到这个,但经测试后发现在设置的 PHPSESSIDE 后发送请求时,如果 User-Agent 为空或不存在的 UA 以及 PixivIOSApp/7.6.2 (iOS 12.2; iPhone9,1) 时,返回的确会出现问题。我是基于 Net 类制作的,并未使用 Utils 类中的 set_headers 函数,所以导致我出问题是因为 headers 为空。 2.搜索数量过少是通过遍历得到的。也就是一直到 next_url 为空为止的统计数量,不过这个 API 有 offset 的限制,不能大于5000。经过我的大量测试,返回数量是由P站不同客户端的机制不同导致的,不过我也没摸清机制。可能是我运气好,选择了"搜索"恶毒。在浏览器和 pixiv_async 搜索 “恶毒” 返回的数量差异及其大,网站上返回数量为2,027,pixiv_async 返回数量为219,所以我发现了这个问题。 3.这个bug是我在调试返回结果的时候改动了源代码导致的。因为错误结果无法使用 res.json(),我临时改为 res.text() 没有改回来。 框架不存在问题,打扰到你了不好意思。

Mikubill commented 3 years ago

就搜索数量差异的问题,我在pixiv上搜索“恶毒”(简体,中文)时网页API返回结果数量也是219,不过如果用ル・マラン(日语)来搜索网页API的返回数量就和你描述的比较接近了(~2010)。同时aapi也有类似的结果。

有可能是pixiv的网页API在部分时候会合并同类词搜索结果)

yuyuko-C commented 3 years ago

我在两端搜索ル・マラン(日语)返回的结果数量是一模一样的,差别就是在搜索“恶毒”(简体,中文)的时候。不过有个意外的发现:如果搜索的是“恶毒”(简体,中文),那排除掉的都是tags不存在“恶毒”(简体,中文)的画作。如果将网页端的搜索规则改为:tags 完全一致,数量则和pixivpy_sync中tags部分一致的数量特别相近,但也不是完全相同。

这问题也是头大)

感谢恋恋的耐心解答,这 Issue 尴尬地想删了 orz。