Skip to content

Commit

Permalink
Merge pull request #18 from mzdluo123/master
Browse files Browse the repository at this point in the history
add bypass sni
  • Loading branch information
Mikubill authored Aug 25, 2021
2 parents a508287 + d790daa commit c66f723
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 23 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ await client.close()
# Or Following Standard Usage
papi = PixivAPI()
aapi = AppPixivAPI()

# 绕过SNI检测
papi = PixivAPI(bypass=True)
aapi = AppPixivAPI(bypass=True)

```

## Proxy
Expand Down
4 changes: 4 additions & 0 deletions README.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ await client.close()
# Or Following Standard Usage
papi = PixivAPI()
aapi = AppPixivAPI()

# 绕过SNI检测
papi = PixivAPI(bypass=True)
aapi = AppPixivAPI(bypass=True)
```

## 代理说明
Expand Down
4 changes: 2 additions & 2 deletions pixivpy_async/aapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@


class AppPixivAPI(BasePixivAPI):
def __init__(self, **requests_kwargs):
def __init__(self, bypass=False, **requests_kwargs):
"""
initialize requests kwargs if need be
"""
super(AppPixivAPI, self).__init__(**requests_kwargs)
super(AppPixivAPI, self).__init__(bypass=bypass, **requests_kwargs)

# 用户详情
async def user_detail(
Expand Down
4 changes: 2 additions & 2 deletions pixivpy_async/bapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


class BasePixivAPI(Net, Utils):
def __init__(self, **requests_kwargs):
def __init__(self, bypass, **requests_kwargs):
self.additional_headers = {}
self.client_id = 'MOBrBDS8blbauoSck0ZfDbtuzpyT'
self.client_secret = 'lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj'
Expand All @@ -32,7 +32,7 @@ def __init__(self, **requests_kwargs):
'image/png': '.png',
'image/gif': '.gif',
}
super().__init__(**requests_kwargs)
super().__init__(bypass=bypass, **requests_kwargs)

async def requests_(
self,
Expand Down
112 changes: 112 additions & 0 deletions pixivpy_async/bypass_sni.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import asyncio
import logging
import json
import socket
from typing import List, Dict, Any
import ssl
import aiohttp
import re

from aiohttp.abc import AbstractResolver
from aiohttp import ClientTimeout


class ByPassResolver(AbstractResolver):

async def resolve(self, host: str, port, family) -> List[Dict[str, Any]]:
new_host = host
if host in ["app-api.pixiv.net", "public-api.secure.pixiv.net", "www.pixiv.net", "oauth.secure.pixiv.net"]:
new_host = "www.pixivision.net"

ips = await self.require_appapi_hosts(new_host)
result = []

for i in ips:
result.append({
"hostname": "",
"host": i,
"port": port,
"family": family,
"proto": 0,
"flags": socket.AI_NUMERICHOST,
})
return result

async def close(self) -> None:
pass

async def fetch(self, client_session: aiohttp.ClientSession, url: str, params, timeout) -> list:
async with client_session.get(url, params=params, timeout=timeout) as rsp:
response = await rsp.text()
obj = json.loads(response)
pattern = re.compile(
"((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)")
result = []
for i in obj["Answer"]:
ip = i["data"]

if pattern.match(ip) is not None:
result.append(ip)
return result

async def require_appapi_hosts(self, hostname, timeout=3) -> List[str]:
"""
通过 Cloudflare 的 DNS over HTTPS 请求真实的 IP 地址。
"""
URLS = (
"https://1.0.0.1/dns-query",
"https://1.1.1.1/dns-query",
"https://[2606:4700:4700::1001]/dns-query",
"https://[2606:4700:4700::1111]/dns-query",
"https://cloudflare-dns.com/dns-query",
)
params = {
"ct": "application/dns-json",
"name": hostname,
"type": "A",
"do": "false",
"cd": "false",
}

async with aiohttp.ClientSession() as session:
results = await asyncio.gather(
*(asyncio.create_task(self.fetch(session, url, params, ClientTimeout(total=timeout))) for url in URLS),
return_exceptions=True)

for r in results:
if not isinstance(r, Exception):
return r


RESOLVER = ByPassResolver()


def get_bypass_client() -> aiohttp.ClientSession:
ssl_ctx = ssl.SSLContext()
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
connector = aiohttp.TCPConnector(ssl=ssl_ctx, resolver=RESOLVER)
client = aiohttp.ClientSession(connector=connector)
return client


class BypassClient:
def __init__(self):
self.client = get_bypass_client()

async def __aenter__(self) -> aiohttp.ClientSession:
return self.client

async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.client.close()


async def test():
async with BypassClient() as client:
async with client.get(
"https://www.pixiv.net/ajax/search/artworks/%E7%99%BE%E5%90%88?word=%E7%99%BE%E5%90%88&order=date_d&mode=all&p=99999990&s_mode=s_tag&type=all&lang=zh") as rsp:
print(await rsp.json())


if __name__ == '__main__':
asyncio.run(test())
17 changes: 11 additions & 6 deletions pixivpy_async/client.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import asyncio
import aiohttp

from .bypass_sni import get_bypass_client

class PixivClient:
def __init__(self, limit=30, timeout=10, env=False, internal=False, proxy=None):
def __init__(self, limit=30, timeout=10, env=False, internal=False, proxy=None, bypass=False):
"""
When 'env' is True and 'proxy' is None, possible proxies will be
obtained automatically (wrong proxy may be obtained).
Expand Down Expand Up @@ -33,11 +34,15 @@ def __init__(self, limit=30, timeout=10, env=False, internal=False, proxy=None):
self.conn = aiohttp.TCPConnector(limit_per_host=limit)

self.internal = internal
self.client = aiohttp.ClientSession(
connector=self.conn,
timeout=aiohttp.ClientTimeout(total=timeout),
trust_env=env,
)

if bypass:
self.client = get_bypass_client()
else:
self.client = aiohttp.ClientSession(
connector=self.conn,
timeout=aiohttp.ClientTimeout(total=timeout),
trust_env=env,
)

if proxy and _flag:
from functools import partial
Expand Down
17 changes: 9 additions & 8 deletions pixivpy_async/net.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@


class ClientManager:
def __init__(self, s, env, proxy):
def __init__(self, s, env, proxy, bypass):
if s is None:
self.session = PixivClient(internal=True, env=env, proxy=proxy)
self.session = PixivClient(internal=True, env=env, proxy=proxy, bypass=bypass)
else:
self.session = s

Expand All @@ -28,15 +28,16 @@ async def __aexit__(self, exception_type, exception_value, traceback):


class Net(object):
def __init__(self, client=None, env=False, proxy=None, **requests_kwargs):
def __init__(self, client=None, env=False, proxy=None, bypass=False, **requests_kwargs):
self.session = client
self.env = env
self.proxy = proxy
self.bypass = bypass
self.requests_kwargs = requests_kwargs

@retry(*retryable_error, retries=10, cooldown=random.randint(1, 3))
async def down(self, _url, _referer):
async with ClientManager(self.session, self.env, self.proxy) as session:
async with ClientManager(self.session, self.env, self.proxy, self.bypass) as session:
async with session.get(_url, headers={'Referer': _referer}, **self.requests_kwargs) as res:
c = await res.read()
t = res.content_type
Expand All @@ -46,7 +47,7 @@ async def down(self, _url, _referer):

@retry(*retryable_error, retries=10, cooldown=random.randint(1, 3))
async def auth(self, _url, _headers, _data):
async with ClientManager(self.session, self.env, self.proxy) as session:
async with ClientManager(self.session, self.env, self.proxy, self.bypass) as session:
async with session.post(_url, headers=_headers, data=_data, **self.requests_kwargs) as res:
r, b, q = await res.json(), res.status in [200, 301, 302], res.status

Expand All @@ -55,7 +56,7 @@ async def auth(self, _url, _headers, _data):

@retry(*retryable_error, retries=10, cooldown=random.randint(1, 3))
async def fetch(self, _url, _headers, _params):
async with ClientManager(self.session, self.env, self.proxy) as session:
async with ClientManager(self.session, self.env, self.proxy, self.bypass) as session:
async with session.get(_url, headers=_headers, params=_params, **self.requests_kwargs) as res:
q = await res.json()

Expand All @@ -64,7 +65,7 @@ async def fetch(self, _url, _headers, _params):

@retry(*retryable_error, retries=10, cooldown=random.randint(1, 3))
async def post(self, _url, _data, _headers, _params):
async with ClientManager(self.session, self.env, self.proxy) as session:
async with ClientManager(self.session, self.env, self.proxy, self.bypass) as session:
async with session.post(_url, data=_data, headers=_headers, params=_params, **self.requests_kwargs) as res:
r = await res.json()

Expand All @@ -73,7 +74,7 @@ async def post(self, _url, _data, _headers, _params):

@retry(*retryable_error, retries=10, cooldown=random.randint(1, 3))
async def delete(self, _url, _headers, _params):
async with ClientManager(self.session, self.env, self.proxy) as session:
async with ClientManager(self.session, self.env, self.proxy, self.bypass) as session:
async with session.delete(_url, headers=_headers, params=_params, **self.requests_kwargs) as res:
q = await res.json()

Expand Down
4 changes: 2 additions & 2 deletions pixivpy_async/papi.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@


class PixivAPI(BasePixivAPI):
def __init__(self, **requests_kwargs):
def __init__(self, bypass=False,**requests_kwargs):
"""
initialize requests kwargs if need be
"""
super().__init__(**requests_kwargs)
super().__init__(bypass,**requests_kwargs)

async def works(
self,
Expand Down
6 changes: 3 additions & 3 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
_PASSWORD = "UserPay"
_TOKEN = "0zeYA-PllRYp1tfrsq_w3vHGU1rPy237JMf5oDt73c4"

aapi = AppPixivAPI()
papi = PixivAPI()
aapi = AppPixivAPI(bypass=True)
papi = PixivAPI(bypass=True)
papi.login(refresh_token=_TOKEN)
aapi.login(refresh_token=_TOKEN)
t = time.time()

class TestMethods(unittest.TestCase):
def test_login(self):
newaapi = AppPixivAPI()
newaapi = AppPixivAPI(bypass=True)
# newpapi = PixivAPI()
# self.assertIsNotNone(newaapi.login(_USERNAME, _PASSWORD))
credential = newaapi.login(refresh_token=_TOKEN)
Expand Down

0 comments on commit c66f723

Please sign in to comment.