nyapy 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nyapy-0.1.0/PKG-INFO +11 -0
- nyapy-0.1.0/README.md +0 -0
- nyapy-0.1.0/pyproject.toml +16 -0
- nyapy-0.1.0/src/nyapy/__init__.py +5 -0
- nyapy-0.1.0/src/nyapy/client.py +12 -0
- nyapy-0.1.0/src/nyapy/enums.py +49 -0
- nyapy-0.1.0/src/nyapy/nyaa.py +86 -0
- nyapy-0.1.0/src/nyapy/nyaafun.py +27 -0
- nyapy-0.1.0/src/nyapy/sukebei.py +27 -0
- nyapy-0.1.0/src/nyapy/types/nyaa.py +29 -0
nyapy-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nyapy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A nyaa scrapper
|
|
5
|
+
Author: mikel39
|
|
6
|
+
Author-email: mikel39 <202028875+mikel39@users.noreply.github.com>
|
|
7
|
+
Requires-Dist: bs4>=0.0.2
|
|
8
|
+
Requires-Dist: httpx>=0.28.1
|
|
9
|
+
Requires-Python: >=3.14
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
nyapy-0.1.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "nyapy"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
authors = [
|
|
5
|
+
{ name = "mikel39", email = "202028875+mikel39@users.noreply.github.com" }
|
|
6
|
+
]
|
|
7
|
+
description = "A nyaa scrapper"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.14"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"bs4>=0.0.2",
|
|
12
|
+
"httpx>=0.28.1",
|
|
13
|
+
]
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["uv_build>=0.9.28,<0.10.0"]
|
|
16
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from httpx import AsyncClient, AsyncHTTPTransport
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_client(base_url: str) -> AsyncClient:
|
|
5
|
+
return AsyncClient(
|
|
6
|
+
base_url=base_url,
|
|
7
|
+
headers={
|
|
8
|
+
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36'
|
|
9
|
+
},
|
|
10
|
+
timeout=10.0,
|
|
11
|
+
transport=AsyncHTTPTransport(retries=6),
|
|
12
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from enum import IntEnum, StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SukebeiCategories(StrEnum):
|
|
5
|
+
art = '1_0'
|
|
6
|
+
art_anime = '1_1'
|
|
7
|
+
art_doujinshi = '1_2'
|
|
8
|
+
art_games = '1_3'
|
|
9
|
+
art_manga = '1_4'
|
|
10
|
+
art_pictures = '1_5'
|
|
11
|
+
real_life = '2_0'
|
|
12
|
+
real_life_pictures = '2_1'
|
|
13
|
+
real_life_videos = '2_2'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NyaaFunCategories(StrEnum):
|
|
17
|
+
anime = '1_0'
|
|
18
|
+
anime_amv = '1_1'
|
|
19
|
+
anime_english = '1_2'
|
|
20
|
+
anime_non_english = '1_3'
|
|
21
|
+
anime_raw = '1_4'
|
|
22
|
+
audio = '2_0'
|
|
23
|
+
audio_lossless = '2_1'
|
|
24
|
+
audio_lossy = '2_2'
|
|
25
|
+
literature = '3_0'
|
|
26
|
+
literature_english = '3_1'
|
|
27
|
+
literature_non_english = '3_2'
|
|
28
|
+
literature_raw = '3_3'
|
|
29
|
+
live_action = '4_0'
|
|
30
|
+
live_action_english = '4_1'
|
|
31
|
+
live_action_idol_pv = '4_2'
|
|
32
|
+
live_action_non_english = '4_3'
|
|
33
|
+
live_action_raw = '4_4'
|
|
34
|
+
pictures = '5_0'
|
|
35
|
+
pictures_graphics = '5_1'
|
|
36
|
+
pictures_photos = '5_2'
|
|
37
|
+
software = '6_0'
|
|
38
|
+
software_apps = '6_1'
|
|
39
|
+
software_games = '6_2'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Filter(IntEnum):
|
|
43
|
+
NO_REMAKES = 1
|
|
44
|
+
TRUSTED_ONLY = 2
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class NyaaSite(StrEnum):
|
|
48
|
+
FUN = 'https://nyaa.si/'
|
|
49
|
+
FAP = 'https://sukebei.nyaa.si/'
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from bs4 import BeautifulSoup
|
|
4
|
+
|
|
5
|
+
from .client import get_client
|
|
6
|
+
from .enums import NyaaSite
|
|
7
|
+
from .types.nyaa import ContentData, ResponseData
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BadResponse(Exception):
|
|
11
|
+
def __init__(self, message: str) -> None:
|
|
12
|
+
super().__init__(message)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Nyaa:
|
|
16
|
+
def __init__(self, site: NyaaSite) -> None:
|
|
17
|
+
self.client = get_client(site)
|
|
18
|
+
|
|
19
|
+
async def get_content(
|
|
20
|
+
self, *, filter, category, query, sort, order, page
|
|
21
|
+
) -> ResponseData:
|
|
22
|
+
if sort == 'date':
|
|
23
|
+
sort = 'id'
|
|
24
|
+
|
|
25
|
+
res = await self.client.get(
|
|
26
|
+
'',
|
|
27
|
+
params={
|
|
28
|
+
'f': filter,
|
|
29
|
+
'c': category,
|
|
30
|
+
'q': query,
|
|
31
|
+
'p': page,
|
|
32
|
+
's': sort,
|
|
33
|
+
'o': order,
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if res.status_code != 200:
|
|
38
|
+
raise BadResponse('Nyaa error')
|
|
39
|
+
|
|
40
|
+
html = BeautifulSoup(res.text, 'html.parser')
|
|
41
|
+
rows = html.select('tbody tr')
|
|
42
|
+
data: list[ContentData] = []
|
|
43
|
+
|
|
44
|
+
for row in rows:
|
|
45
|
+
properties = row.find_all('td')
|
|
46
|
+
category_row = properties[0].find('a')
|
|
47
|
+
category_row = (category_row or {}).get('title', '')
|
|
48
|
+
comments = properties[1].find('a', attrs={'class': 'commentss'})
|
|
49
|
+
comments = int(comments.text) if comments else 0
|
|
50
|
+
name = properties[1].find('a', {'class': False})
|
|
51
|
+
(name, id) = (name.text, name.get('href')) if name else ('', '')
|
|
52
|
+
id = re.search(r'\d*', (str(id) or ''))
|
|
53
|
+
id = id.group(1) if id else 0
|
|
54
|
+
torrent = properties[2].find('i', {'class': 'fa-download'})
|
|
55
|
+
torrent = torrent.parent if torrent else None
|
|
56
|
+
torrent = torrent.get('href') if torrent else None
|
|
57
|
+
torrent = str(self.client.base_url)[:-1] + str(torrent) if torrent else None
|
|
58
|
+
magnet = properties[2].find('i', {'class': 'fa-magnet'})
|
|
59
|
+
magnet = magnet.parent if magnet else None
|
|
60
|
+
magnet = str(magnet.get('href')) if magnet else None
|
|
61
|
+
size = properties[3].text
|
|
62
|
+
date = properties[4].get('data-timestamp')
|
|
63
|
+
seeders = int(properties[5].text)
|
|
64
|
+
leechers = int(properties[6].text)
|
|
65
|
+
downloads = int(properties[7].text)
|
|
66
|
+
|
|
67
|
+
content: ContentData = {
|
|
68
|
+
'id': int(id),
|
|
69
|
+
'category': str(category_row),
|
|
70
|
+
'name': str(name),
|
|
71
|
+
'comments': comments,
|
|
72
|
+
'links': {'torrent': torrent, 'magnet': magnet},
|
|
73
|
+
'size': str(size),
|
|
74
|
+
'date': str(date),
|
|
75
|
+
'seeders': seeders,
|
|
76
|
+
'leechers': leechers,
|
|
77
|
+
'downloads': downloads,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
data.append(content)
|
|
81
|
+
|
|
82
|
+
return {'page': page, 'results': len(data), 'data': data}
|
|
83
|
+
|
|
84
|
+
async def close(self):
|
|
85
|
+
"""Closes connection to nyaa"""
|
|
86
|
+
await self.client.aclose()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .enums import Filter, NyaaFunCategories, NyaaSite
|
|
2
|
+
from .nyaa import Nyaa
|
|
3
|
+
from .types.nyaa import Order, ResponseData, Sort
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NyaaFun(Nyaa):
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
super().__init__(NyaaSite.FUN)
|
|
9
|
+
|
|
10
|
+
async def get_content(
|
|
11
|
+
self,
|
|
12
|
+
*,
|
|
13
|
+
filter: Filter | None = None,
|
|
14
|
+
category: NyaaFunCategories | None = None,
|
|
15
|
+
query: str | None = None,
|
|
16
|
+
page: int = 1,
|
|
17
|
+
sort: Sort | None = None,
|
|
18
|
+
order: Order = 'desc',
|
|
19
|
+
) -> ResponseData:
|
|
20
|
+
return await super().get_content(
|
|
21
|
+
filter=filter,
|
|
22
|
+
category=category,
|
|
23
|
+
query=query,
|
|
24
|
+
sort=sort,
|
|
25
|
+
page=page,
|
|
26
|
+
order=order,
|
|
27
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .enums import Filter, NyaaSite, SukebeiCategories
|
|
2
|
+
from .nyaa import Nyaa
|
|
3
|
+
from .types.nyaa import Order, ResponseData, Sort
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Sukebei(Nyaa):
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
super().__init__(NyaaSite.FAP)
|
|
9
|
+
|
|
10
|
+
async def get_content(
|
|
11
|
+
self,
|
|
12
|
+
*,
|
|
13
|
+
filter: Filter | None = None,
|
|
14
|
+
category: SukebeiCategories | None = None,
|
|
15
|
+
query: str | None = None,
|
|
16
|
+
page: int = 1,
|
|
17
|
+
sort: Sort | None = None,
|
|
18
|
+
order: Order = 'desc',
|
|
19
|
+
) -> ResponseData:
|
|
20
|
+
return await super().get_content(
|
|
21
|
+
filter=filter,
|
|
22
|
+
category=category,
|
|
23
|
+
query=query,
|
|
24
|
+
sort=sort,
|
|
25
|
+
page=page,
|
|
26
|
+
order=order,
|
|
27
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Literal, TypedDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _Links(TypedDict):
|
|
5
|
+
torrent: str | None
|
|
6
|
+
magnet: str | None
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ContentData(TypedDict):
|
|
10
|
+
id: int
|
|
11
|
+
category: str
|
|
12
|
+
name: str
|
|
13
|
+
comments: int
|
|
14
|
+
size: str
|
|
15
|
+
date: str
|
|
16
|
+
seeders: int
|
|
17
|
+
leechers: int
|
|
18
|
+
downloads: int
|
|
19
|
+
links: _Links
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ResponseData(TypedDict):
|
|
23
|
+
page: int
|
|
24
|
+
results: int
|
|
25
|
+
data: list[ContentData]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
type Sort = Literal['comments', 'size', 'date', 'seeders', 'leechers', 'downloads']
|
|
29
|
+
type Order = Literal['asc', 'desc']
|