├── tests
├── __init__.py
├── conftest.py
├── test_sync_translation.py
├── test_translation.py
├── test_sync_search.py
└── test_search.py
├── naipy
├── __init__.py
├── error.py
├── http.py
├── client.py
├── sync.py
└── model.py
├── requirements
├── deps.txt
├── style.txt
└── test.txt
├── setup.py
├── License
├── README.md
└── .gitignore
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/naipy/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = "2.6.0"
2 |
--------------------------------------------------------------------------------
/requirements/deps.txt:
--------------------------------------------------------------------------------
1 | aiohttp==3.8.3
2 | requests==2.28.1
--------------------------------------------------------------------------------
/requirements/style.txt:
--------------------------------------------------------------------------------
1 | black==22.12.0
2 | isort==5.11.4
--------------------------------------------------------------------------------
/naipy/error.py:
--------------------------------------------------------------------------------
1 | class HTTPException(Exception):
2 | pass
3 |
--------------------------------------------------------------------------------
/requirements/test.txt:
--------------------------------------------------------------------------------
1 | pytest==7.2.0
2 | pytest-cov==4.0.0
3 | pytest-asyncio==0.20.3
4 | pytest-rerunfailures==10.3
5 | pytest-timeout==2.1.0
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from naipy import client, sync
4 |
5 |
6 | @pytest.fixture
7 | def search():
8 | return client.Search()
9 |
10 |
11 | @pytest.fixture
12 | def trans():
13 | return client.Translation()
14 |
15 |
16 | @pytest.fixture
17 | def sync_search():
18 | return sync.Search()
19 |
20 |
21 | @pytest.fixture
22 | def sync_trans():
23 | return sync.Translation()
24 |
--------------------------------------------------------------------------------
/tests/test_sync_translation.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | import pytest
4 |
5 | from naipy.sync import Translation
6 |
7 | text = "안녕하세요"
8 |
9 |
10 | # 언어인식
11 | def test_detect(sync_trans: Translation):
12 | r = sync_trans.detect(text)
13 | assert r.data
14 |
15 |
16 | # 번역
17 | def test_sync_translation(sync_trans: Translation):
18 | r = sync_trans.translation(text, target="en")
19 | assert r.data
20 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | from naipy import __version__
4 |
5 | setuptools.setup(
6 | name="naipy",
7 | version=__version__,
8 | license="MIT",
9 | author="VoidAsMad",
10 | author_email="voidasmad@gmail.com",
11 | description="네이버 API를 사용한 검색/번역 라이브러리",
12 | long_description=open("README.md", "rt", encoding="UTF8").read(),
13 | long_description_content_type="text/markdown",
14 | url="https://github.com/VoidAsMad/Naipy",
15 | packages=setuptools.find_packages(),
16 | python_requires=">=3.6",
17 | install_requires=["requests", "aiohttp"],
18 | )
19 |
--------------------------------------------------------------------------------
/tests/test_translation.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | import pytest
4 |
5 | from naipy.client import Translation
6 |
7 | text = "안녕하세요"
8 |
9 |
10 | # 언어인식
11 | @pytest.mark.asyncio
12 | async def test_detect(trans: Translation):
13 | r = await trans.detect(text)
14 | assert r.data
15 |
16 |
17 | # 번역
18 | @pytest.mark.asyncio
19 | async def test_translation(trans: Translation):
20 | r = await trans.translation(text, target="en")
21 | assert r.data
22 |
23 |
24 | # 동시번역
25 | @pytest.mark.asyncio
26 | async def test_dual_translation(trans: Translation):
27 | r = await trans.dual_translation(text, targets=["en", "ja"])
28 | assert r
29 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Wolpis Carter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/tests/test_sync_search.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from naipy.sync import Search
4 |
5 | # 검색어
6 | text = "너구리"
7 |
8 | # 이미지 검색
9 | def test_image(sync_search: Search):
10 | r = sync_search.image(text)
11 | assert r.data
12 |
13 |
14 | # 블로그 검색
15 | def test_blog(sync_search: Search):
16 | r = sync_search.blog(text)
17 | assert r.data
18 |
19 |
20 | # 도서 검색
21 | def test_book(sync_search: Search):
22 | r = sync_search.book(text)
23 | assert r.data
24 |
25 |
26 | # 백과사전 검색
27 | def test_encyc(sync_search: Search):
28 | r = sync_search.encyc(text)
29 | assert r.data
30 |
31 |
32 | # 카페글 검색
33 | def test_cafearticle(sync_search: Search):
34 | r = sync_search.cafearticle(text)
35 | assert r.data
36 |
37 |
38 | # 지식인 검색
39 | def test_kin(sync_search: Search):
40 | r = sync_search.kin(text)
41 | assert r.data
42 |
43 |
44 | # 웹사이트 검색
45 | def test_webkr(sync_search: Search):
46 | r = sync_search.webkr(text)
47 | assert r.data
48 |
49 |
50 | # 삼품 검색
51 | def test_shop(sync_search: Search):
52 | r = sync_search.shop(text)
53 | assert r.data
54 |
55 |
56 | # 전문자료 검색
57 | def test_doc(sync_search: Search):
58 | r = sync_search.doc(text)
59 | assert r.data
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
Naipy(네이피)
3 |
4 |
5 | 
6 |
7 |
8 |
누구나 쉽게 네이버 API를
9 |
10 |
11 | [
](https://pypi.python.org/pypi/naipy)
12 | 누구나 손쉽게 네이버API를 사용할 수 있어요!
13 | > [2022 공개SW 개발자 대회 이노그리드 대표상 수상 작품](https://www.oss.kr/dev_competition_activities/show/8df8c099-9b18-454f-a3e1-29baa00116ad?page=2)
14 | > **동기 비동기 모두 지원합니다!**
15 | > 현재 지원API : 검색API, 번역API, 언어감지API
16 | > [자세한 사용법은 여기를 참조해주세요!](https://naipy.notion.site/Naipy-3c332c562b5f42059c48b783b5b59528)
17 |
18 | ## Installation
19 | ```
20 | $ pip install naipy
21 | ```
22 | ## Example
23 | ### 검색(이미지)[동기]
24 | ```py
25 | from naipy import sync
26 |
27 | naipy = sync.Search()
28 | # 필수 인자(API)가 들어가는 곳입니다. (샘플키지원)
29 |
30 | print(naipy.image("너구리").link)
31 | # 너구리라는 검색어 데이터의 link를 반환합니다.
32 | ```
33 | ### 검색(이미지)[비동기]
34 | ```py
35 | from naipy import client
36 | import asyncio
37 |
38 | async def main():
39 | c = client.Search()
40 | # 필수 인자(API)가 들어가는 곳입니다. (샘플키지원)
41 | r = await c.image("너구리")
42 | # 너구리라는 검색어 데이터의 link를 반환합니다.
43 | print(r.link)
44 |
45 | asyncio.run(main())
46 | ```
47 |
--------------------------------------------------------------------------------
/tests/test_search.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from naipy.client import Search
4 |
5 | # 검색어
6 | text = "너구리"
7 |
8 | # 이미지 검색
9 | @pytest.mark.asyncio
10 | async def test_image(search: Search):
11 | r = await search.image(text)
12 | assert r.data
13 |
14 |
15 | # 블로그 검색
16 | @pytest.mark.asyncio
17 | async def test_blog(search: Search):
18 | r = await search.blog(text)
19 | assert r.data
20 |
21 |
22 | # 도서 검색
23 | @pytest.mark.asyncio
24 | async def test_book(search: Search):
25 | r = await search.book(text)
26 | assert r.data
27 |
28 |
29 | # 백과사전 검색
30 | @pytest.mark.asyncio
31 | async def test_encyc(search: Search):
32 | r = await search.encyc(text)
33 | assert r.data
34 |
35 |
36 | # 카페글 검색
37 | @pytest.mark.asyncio
38 | async def test_cafearticle(search: Search):
39 | r = await search.cafearticle(text)
40 | assert r.data
41 |
42 |
43 | # 지식인 검색
44 | @pytest.mark.asyncio
45 | async def test_kin(search: Search):
46 | r = await search.kin(text)
47 | assert r.data
48 |
49 |
50 | # 웹사이트 검색
51 | @pytest.mark.asyncio
52 | async def test_webkr(search: Search):
53 | r = await search.webkr(text)
54 | assert r.data
55 |
56 |
57 | # 삼품 검색
58 | @pytest.mark.asyncio
59 | async def test_shop(search: Search):
60 | r = await search.shop(text)
61 | assert r.data
62 |
63 |
64 | # 전문자료 검색
65 | @pytest.mark.asyncio
66 | async def test_doc(search: Search):
67 | r = await search.doc(text)
68 | assert r.data
69 |
--------------------------------------------------------------------------------
/naipy/http.py:
--------------------------------------------------------------------------------
1 | import warnings
2 | from typing import Dict, Union
3 |
4 | import aiohttp
5 |
6 | from naipy.error import HTTPException
7 |
8 |
9 | class NaipyRequest:
10 | base_url = "https://openapi.naver.com/v1/"
11 |
12 | def __init__(self, client_id: str = None, client_secret: str = None) -> None:
13 | if not client_id or not client_secret:
14 | self.client_id = "p7aYK48ehm6TqdhTt_yv"
15 | self.client_secret = "UJAqwrxSfQ"
16 | warnings.warn("샘플키로 요청합니다.", UserWarning)
17 | else:
18 | self.client_id = client_id
19 | self.client_secret = client_secret
20 |
21 | async def request(
22 | self, method: str, endpoint: str, params: Dict[str, Union[str, int]]
23 | ) -> Dict[str, Union[str, int]]:
24 | headers = {
25 | "X-Naver-Client-Id": self.client_id,
26 | "X-Naver-Client-Secret": self.client_secret,
27 | "Content-Type": "application/x-www-form-urlencoded",
28 | }
29 | url = self.base_url + endpoint
30 | async with aiohttp.ClientSession(headers=headers) as session:
31 | async with session.request(method, url=url, params=params) as response:
32 | rescode = response.status
33 | if rescode == 200:
34 | return await response.json()
35 | else:
36 | raise HTTPException(f"Error Code : {rescode}")
37 |
38 | async def get_search(self, tag: str, params: Dict[str, Union[str, int]]):
39 | return await self.request("GET", f"search/{tag}", params)
40 |
41 | async def get_translation(self, tag: str, params: Dict[str, Union[str, int]]):
42 | return await self.request("POST", f"papago/{tag}", params)
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
--------------------------------------------------------------------------------
/naipy/client.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import random
3 | from typing import List
4 |
5 | from naipy.http import NaipyRequest
6 | from naipy.model import (
7 | BlogNaipy,
8 | BookNaipy,
9 | CafearticleNaipy,
10 | DetectNaipy,
11 | DocNaipy,
12 | EncycNaipy,
13 | ImageNaipy,
14 | KinNaipy,
15 | N2mtNaipy,
16 | ShopNaipy,
17 | WebkrNaipy,
18 | )
19 |
20 |
21 | class Search(NaipyRequest):
22 | """
23 | 네이버 비동기 검색 라이브러리입니다.
(Parameters가 빈칸일시 샘플키로 요청합니다.)
24 | #### client_id
25 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client ID`를 입력합니다.
26 | #### client_secret
27 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client Secret`를 입력합니다.
28 | """
29 |
30 | def __init__(self, client_id: str = None, client_secret: str = None) -> None:
31 | super().__init__(client_id=client_id, client_secret=client_secret)
32 |
33 | async def image(self, text: str) -> ImageNaipy:
34 | """
35 | 이미지를 검색합니다.
36 | """
37 | params = {"query": text}
38 | data = await self.get_search("image", params=params)
39 | data["item"] = random.choice(data["items"])
40 | return ImageNaipy(data=data, **data)
41 |
42 | async def blog(self, text: str) -> BlogNaipy:
43 | """
44 | 블로그를 검색합니다.
45 | """
46 | params = {"query": text}
47 | data = await self.get_search("blog", params=params)
48 | data["item"] = random.choice(data["items"])
49 | return BlogNaipy(data=data, **data)
50 |
51 | async def book(self, text: str) -> BookNaipy:
52 | """
53 | 도서를 검색합니다.
54 | """
55 | params = {"query": text}
56 | data = await self.get_search("book", params=params)
57 | data["item"] = random.choice(data["items"])
58 | return BookNaipy(data=data, **data)
59 |
60 | async def encyc(self, text: str) -> EncycNaipy:
61 | """
62 | 백과사전를 검색합니다.
63 | """
64 | params = {"query": text}
65 | data = await self.get_search("encyc", params=params)
66 | data["item"] = random.choice(data["items"])
67 | return EncycNaipy(data=data, **data)
68 |
69 | async def cafearticle(self, text: str) -> CafearticleNaipy:
70 | """
71 | 카페글를 검색합니다.
72 | """
73 | params = {"query": text}
74 | data = await self.get_search("cafearticle", params=params)
75 | data["item"] = random.choice(data["items"])
76 | return CafearticleNaipy(data=data, **data)
77 |
78 | async def kin(self, text: str) -> KinNaipy:
79 | """
80 | 네이버 지식인을 검색합니다.
81 | """
82 | params = {"query": text}
83 | data = await self.get_search("kin", params=params)
84 | data["item"] = random.choice(data["items"])
85 | return KinNaipy(data=data, **data)
86 |
87 | async def webkr(self, text: str) -> WebkrNaipy:
88 | """
89 | 웹사이트를 검색합니다.
90 | """
91 | params = {"query": text}
92 | data = await self.get_search("webkr", params=params)
93 | data["item"] = random.choice(data["items"])
94 | return WebkrNaipy(data=data, **data)
95 |
96 | async def shop(self, text: str) -> ShopNaipy:
97 | """
98 | 쇼핑을 검색합니다.
99 | """
100 | params = {"query": text}
101 | data = await self.get_search("shop", params=params)
102 | data["item"] = random.choice(data["items"])
103 | return ShopNaipy(data=data, **data)
104 |
105 | async def doc(self, text: str) -> DocNaipy:
106 | """
107 | 전문자료를 검색합니다.
108 | """
109 | params = {"query": text}
110 | data = await self.get_search("doc", params=params)
111 | data["item"] = random.choice(data["items"])
112 | return DocNaipy(data=data, **data)
113 |
114 |
115 | class Translation(NaipyRequest):
116 | """
117 | 네이버 번역 라이브러리입니다.
(Parameters가 빈칸일시 샘플키로 요청합니다.)
118 | #### client_id
119 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client ID`를 입력합니다.
120 | #### client_secret
121 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client Secret`를 입력합니다.
122 | """
123 |
124 | def __init__(self, client_id: str = None, client_secret: str = None) -> None:
125 | super().__init__(client_id=client_id, client_secret=client_secret)
126 |
127 | async def detect(self, text: str) -> DetectNaipy:
128 | """
129 | 언어를 인식합니다. [언어코드표](https://developers.naver.com/docs/papago/papago-nmt-api-reference.md#%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0)
130 | """
131 | params = {"query": text}
132 | data = await self.get_translation("detectLangs", params=params)
133 | return DetectNaipy(data=data, **data)
134 |
135 | async def translation(self, text: str, target: str) -> N2mtNaipy:
136 | """
137 | 문장또는 단어를 번역합니다. `target`파라미터는 [언어코드표](https://developers.naver.com/docs/papago/papago-nmt-api-reference.md#%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0)를 참고해주세요!
138 | """
139 | source = await self.detect(text)
140 | source = source.langCode
141 | params = {"text": text, "source": source, "target": target}
142 | data = await self.get_translation("n2mt", params=params)
143 | data = data["message"]["result"]
144 | return N2mtNaipy(data, **data)
145 |
146 | async def dual_translation(self, text: str, targets: List[str]) -> List[N2mtNaipy]:
147 | """
148 | 문장또는 단어를 여러 언어로 번역합니다. `target`파라미터는 [언어코드표](https://developers.naver.com/docs/papago/papago-nmt-api-reference.md#%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0)를 참고해주세요!
149 | """
150 | source = await self.detect(text)
151 | source = source.langCode
152 | futures = [
153 | asyncio.ensure_future(
154 | self.get_translation(
155 | "n2mt", params={"text": text, "source": source, "target": target}
156 | )
157 | )
158 | for target in targets
159 | ]
160 | result = await asyncio.gather(*futures)
161 | return [
162 | N2mtNaipy(data["message"]["result"], **data["message"]["result"])
163 | for data in result
164 | ]
165 |
--------------------------------------------------------------------------------
/naipy/sync.py:
--------------------------------------------------------------------------------
1 | import random
2 | import warnings
3 | from typing import Dict, Union
4 |
5 | import requests
6 |
7 | from naipy.error import HTTPException
8 | from naipy.model import (
9 | BlogNaipy,
10 | BookNaipy,
11 | CafearticleNaipy,
12 | DetectNaipy,
13 | DocNaipy,
14 | EncycNaipy,
15 | ImageNaipy,
16 | KinNaipy,
17 | N2mtNaipy,
18 | ShopNaipy,
19 | WebkrNaipy,
20 | )
21 |
22 |
23 | class SyncNaipyRequest:
24 | base_url = "https://openapi.naver.com/v1/"
25 |
26 | def __init__(self, client_id: str = None, client_secret: str = None) -> None:
27 | if not client_id or not client_secret:
28 | self.client_id = "p7aYK48ehm6TqdhTt_yv"
29 | self.client_secret = "UJAqwrxSfQ"
30 | warnings.warn("샘플키로 요청합니다.", UserWarning)
31 | else:
32 | self.client_id = client_id
33 | self.client_secret = client_secret
34 |
35 | def request(
36 | self, method: str, endpoint: str, params: Dict[str, Union[str, int]]
37 | ) -> Dict[str, Union[str, int]]:
38 | headers = {
39 | "X-Naver-Client-Id": self.client_id,
40 | "X-Naver-Client-Secret": self.client_secret,
41 | "Content-Type": "application/x-www-form-urlencoded",
42 | }
43 | url = self.base_url + endpoint
44 | response = requests.request(method, url=url, headers=headers, params=params)
45 | rescode = response.status_code
46 | if rescode == 200:
47 | return response.json()
48 | else:
49 | raise HTTPException(f"Error Code : {rescode}")
50 |
51 | def get_search(self, tag: str, params: Dict[str, Union[str, int]]):
52 | return self.request("GET", f"search/{tag}", params)
53 |
54 | def get_translation(self, tag: str, params: Dict[str, Union[str, int]]):
55 | return self.request("POST", f"papago/{tag}", params)
56 |
57 |
58 | class Search(SyncNaipyRequest):
59 | """
60 | 네이버 검색 라이브러리입니다.
(Parameters가 빈칸일시 샘플키로 요청합니다.)
61 | #### client_id
62 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client ID`를 입력합니다.
63 | #### client_secret
64 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client Secret`를 입력합니다.
65 | """
66 |
67 | def __init__(self, client_id: str = None, client_secret: str = None) -> None:
68 | super().__init__(client_id=client_id, client_secret=client_secret)
69 |
70 | def image(self, text: str) -> ImageNaipy:
71 | """
72 | 이미지를 검색합니다.
73 | """
74 | params = {"query": text}
75 | data = self.get_search("image", params=params)
76 | data["item"] = random.choice(data["items"])
77 | return ImageNaipy(data=data, **data)
78 |
79 | def blog(self, text: str) -> BlogNaipy:
80 | """
81 | 블로그를 검색합니다.
82 | """
83 | params = {"query": text}
84 | data = self.get_search("blog", params=params)
85 | data["item"] = random.choice(data["items"])
86 | return BlogNaipy(data=data, **data)
87 |
88 | def book(self, text: str) -> BookNaipy:
89 | """
90 | 도서를 검색합니다.
91 | """
92 | params = {"query": text}
93 | data = self.get_search("book", params=params)
94 | data["item"] = random.choice(data["items"])
95 | return BookNaipy(data=data, **data)
96 |
97 | def encyc(self, text: str) -> EncycNaipy:
98 | """
99 | 백과사전를 검색합니다.
100 | """
101 | params = {"query": text}
102 | data = self.get_search("encyc", params=params)
103 | data["item"] = random.choice(data["items"])
104 | return EncycNaipy(data=data, **data)
105 |
106 | def cafearticle(self, text: str) -> CafearticleNaipy:
107 | """
108 | 카페글를 검색합니다.
109 | """
110 | params = {"query": text}
111 | data = self.get_search("cafearticle", params=params)
112 | data["item"] = random.choice(data["items"])
113 | return CafearticleNaipy(data=data, **data)
114 |
115 | def kin(self, text: str) -> KinNaipy:
116 | """
117 | 네이버 지식인을 검색합니다.
118 | """
119 | params = {"query": text}
120 | data = self.get_search("kin", params=params)
121 | data["item"] = random.choice(data["items"])
122 | return KinNaipy(data=data, **data)
123 |
124 | def webkr(self, text: str) -> WebkrNaipy:
125 | """
126 | 웹사이트를 검색합니다.
127 | """
128 | params = {"query": text}
129 | data = self.get_search("webkr", params=params)
130 | data["item"] = random.choice(data["items"])
131 | return WebkrNaipy(data=data, **data)
132 |
133 | def shop(self, text: str) -> ShopNaipy:
134 | """
135 | 쇼핑을 검색합니다.
136 | """
137 | params = {"query": text}
138 | data = self.get_search("shop", params=params)
139 | data["item"] = random.choice(data["items"])
140 | return ShopNaipy(data=data, **data)
141 |
142 | def doc(self, text: str) -> DocNaipy:
143 | """
144 | 전문자료를 검색합니다.
145 | """
146 | params = {"query": text}
147 | data = self.get_search("doc", params=params)
148 | data["item"] = random.choice(data["items"])
149 | return DocNaipy(data=data, **data)
150 |
151 |
152 | class Translation(SyncNaipyRequest):
153 | """
154 | 네이버 번역 라이브러리입니다.
(Parameters가 빈칸일시 샘플키로 요청합니다.)
155 | #### client_id
156 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client ID`를 입력합니다.
157 | #### client_secret
158 | [네이버개발자센터](https://developers.naver.com/main/)에서 발급받은 `Client Secret`를 입력합니다.
159 | """
160 |
161 | def __init__(self, client_id: str = None, client_secret: str = None) -> None:
162 | super().__init__(client_id=client_id, client_secret=client_secret)
163 |
164 | def detect(self, text: str) -> DetectNaipy:
165 | """
166 | 언어를 인식합니다. [언어코드표](https://developers.naver.com/docs/papago/papago-nmt-api-reference.md#%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0)
167 | """
168 | params = {"query": text}
169 | data = self.get_translation("detectLangs", params=params)
170 | return DetectNaipy(data=data, **data)
171 |
172 | def translation(self, text: str, target: str) -> N2mtNaipy:
173 | """
174 | 문장또는 단어를 번역합니다. `target`파라미터는 [언어코드표](https://developers.naver.com/docs/papago/papago-nmt-api-reference.md#%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0)를 참고해주세요!
175 | """
176 | source = self.detect(text).langCode
177 | params = {"text": text, "source": source, "target": target}
178 | data = self.get_translation("n2mt", params=params)
179 | data = data["message"]["result"]
180 | return N2mtNaipy(data, **data)
181 |
--------------------------------------------------------------------------------
/naipy/model.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from dataclasses import dataclass, field
3 | from typing import Any, Dict, List, Optional
4 |
5 |
6 | @dataclass(frozen=True)
7 | class BaseNaipy:
8 | data: Dict[str, Any] = field(repr=False)
9 | """데이터 Dict"""
10 |
11 |
12 | @dataclass(frozen=True)
13 | class DetectNaipy(BaseNaipy):
14 | langCode: Optional[str] = field(repr=True, compare=True, default=None)
15 | """언어코드"""
16 |
17 |
18 | @dataclass(frozen=True)
19 | class N2mtNaipy(BaseNaipy):
20 | srcLangType: Optional[str] = field(repr=True, compare=True, default=None)
21 | """시작언어"""
22 | tarLangType: Optional[str] = field(repr=True, compare=True, default=None)
23 | """도착언어"""
24 | translatedText: Optional[str] = field(repr=True, compare=True, default=None)
25 | """번역된 글자"""
26 | engineType: Optional[str] = field(repr=True, compare=True, default=None)
27 | """엔진 유형"""
28 | pivot: Optional[str] = field(repr=True, compare=True, default=None)
29 | """"""
30 | dict: Optional[str] = field(repr=True, compare=True, default=None)
31 | """"""
32 | tarDict: Optional[str] = field(repr=True, compare=True, default=None)
33 | """"""
34 |
35 | @property
36 | def json(self):
37 | """return JSON"""
38 | result = {
39 | "srcLangType": self.srcLangType,
40 | "tarLangType": self.tarLangType,
41 | "text": self.translatedText,
42 | }
43 | return result
44 |
45 |
46 | @dataclass(frozen=True)
47 | class SearchNaipy(BaseNaipy):
48 | lastBuildDate: Optional[str] = field(repr=True, compare=True, default=None)
49 | """검색 결과를 생성한 시간"""
50 | total: Optional[int] = field(repr=True, compare=True, default=None)
51 | """검색 결과 문서의 총 개수"""
52 | start: Optional[int] = field(repr=True, compare=True, default=None)
53 | """검색 결과 문서 중, 문서의 시작점"""
54 | display: Optional[int] = field(repr=True, compare=True, default=None)
55 | """검색된 검색 결과의 개수"""
56 | items: List[str] = field(repr=True, compare=True, default=None)
57 | """데이터 List"""
58 | item: List[str] = field(repr=True, compare=True, default=None)
59 | """추출할 리스트"""
60 |
61 |
62 | @dataclass(frozen=True)
63 | class ImageNaipy(SearchNaipy):
64 | @property
65 | def title(self) -> str:
66 | """검색 결과 이미지의 제목"""
67 | return self.item[f"{sys._getframe().f_code.co_name}"]
68 |
69 | @property
70 | def link(self) -> str:
71 | """검색 결과 이미지의 하이퍼텍스트 link"""
72 | return self.item[f"{sys._getframe().f_code.co_name}"]
73 |
74 | @property
75 | def thumbnail(self) -> str:
76 | """검색 결과 이미지의 썸네일 link"""
77 | return self.item[f"{sys._getframe().f_code.co_name}"]
78 |
79 | @property
80 | def sizeheight(self) -> str:
81 | """검색 결과 이미지의 썸네일 높이"""
82 | return self.item[f"{sys._getframe().f_code.co_name}"]
83 |
84 | @property
85 | def sizewidth(self) -> str:
86 | """검색 결과 이미지의 너비"""
87 | return self.item[f"{sys._getframe().f_code.co_name}"]
88 |
89 |
90 | @dataclass(frozen=True)
91 | class BlogNaipy(SearchNaipy):
92 | @property
93 | def title(self) -> str:
94 | """검색 결과 문서의 제목"""
95 | return self.item[f"{sys._getframe().f_code.co_name}"]
96 |
97 | @property
98 | def link(self) -> str:
99 | """검색 결과 문서의 하이퍼텍스트 link"""
100 | return self.item[f"{sys._getframe().f_code.co_name}"]
101 |
102 | @property
103 | def description(self) -> str:
104 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
105 | return self.item[f"{sys._getframe().f_code.co_name}"]
106 |
107 | @property
108 | def bloggername(self) -> str:
109 | """검색 결과 블로그 포스트를 작성한 블로거의 이름"""
110 | return self.item[f"{sys._getframe().f_code.co_name}"]
111 |
112 | @property
113 | def bloggerlink(self) -> str:
114 | """검색 결과 블로그 포스트를 작성한 블로거의 하이퍼텍스트 link"""
115 | return self.item[f"{sys._getframe().f_code.co_name}"]
116 |
117 | @property
118 | def postdate(self) -> str:
119 | """포스트 날짜"""
120 | return self.item[f"{sys._getframe().f_code.co_name}"]
121 |
122 |
123 | @dataclass(frozen=True)
124 | class BookNaipy(SearchNaipy):
125 | @property
126 | def title(self) -> str:
127 | """검색 결과 문서의 제목"""
128 | return self.item[f"{sys._getframe().f_code.co_name}"]
129 |
130 | @property
131 | def link(self) -> str:
132 | """검색 결과 문서의 하이퍼텍스트 link"""
133 | return self.item[f"{sys._getframe().f_code.co_name}"]
134 |
135 | @property
136 | def image(self) -> str:
137 | """썸네일 이미지의 URL"""
138 | return self.item[f"{sys._getframe().f_code.co_name}"]
139 |
140 | @property
141 | def author(self) -> str:
142 | """저자 정보"""
143 | return self.item[f"{sys._getframe().f_code.co_name}"]
144 |
145 | @property
146 | def discount(self) -> str:
147 | """할인 가격 정보"""
148 | return self.item[f"{sys._getframe().f_code.co_name}"]
149 |
150 | @property
151 | def publisher(self) -> str:
152 | """출판사 정보"""
153 | return self.item[f"{sys._getframe().f_code.co_name}"]
154 |
155 | @property
156 | def pubdate(self) -> str:
157 | """출간일 정보"""
158 | return self.item[f"{sys._getframe().f_code.co_name}"]
159 |
160 | @property
161 | def isbn(self) -> str:
162 | """국제표준도서번호"""
163 | return self.item[f"{sys._getframe().f_code.co_name}"]
164 |
165 | @property
166 | def description(self) -> str:
167 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
168 | return self.item[f"{sys._getframe().f_code.co_name}"]
169 |
170 |
171 | @dataclass(frozen=True)
172 | class EncycNaipy(SearchNaipy):
173 | @property
174 | def title(self) -> str:
175 | """검색 결과 사전 정의의 제목"""
176 | return self.item[f"{sys._getframe().f_code.co_name}"]
177 |
178 | @property
179 | def link(self) -> str:
180 | """검색 결과 사전의"""
181 | return self.item[f"{sys._getframe().f_code.co_name}"]
182 |
183 | @property
184 | def thumbnail(self) -> str:
185 | """해당 이미지의 썸네일 link url"""
186 | return self.item[f"{sys._getframe().f_code.co_name}"]
187 |
188 | @property
189 | def description(self) -> str:
190 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
191 | return self.item[f"{sys._getframe().f_code.co_name}"]
192 |
193 |
194 | @dataclass(frozen=True)
195 | class CafearticleNaipy(SearchNaipy):
196 | @property
197 | def title(self) -> str:
198 | """검색 결과 문서의 제목"""
199 | return self.item[f"{sys._getframe().f_code.co_name}"]
200 |
201 | @property
202 | def link(self) -> str:
203 | """검색 결과 문서의 하이퍼텍스트 link"""
204 | return self.item[f"{sys._getframe().f_code.co_name}"]
205 |
206 | @property
207 | def description(self) -> str:
208 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
209 | return self.item[f"{sys._getframe().f_code.co_name}"]
210 |
211 | @property
212 | def cafename(self) -> str:
213 | """검색 결과 문서가 작성된 카페 이름"""
214 | return self.item[f"{sys._getframe().f_code.co_name}"]
215 |
216 | @property
217 | def cafeurl(self) -> str:
218 | """검색 결과 문서가 적성된 카페의 하이퍼텍스트 link"""
219 | return self.item[f"{sys._getframe().f_code.co_name}"]
220 |
221 |
222 | @dataclass(frozen=True)
223 | class KinNaipy(SearchNaipy):
224 | @property
225 | def title(self) -> str:
226 | """검색 결과 문서의 제목"""
227 | return self.item[f"{sys._getframe().f_code.co_name}"]
228 |
229 | @property
230 | def link(self) -> str:
231 | """검색 결과 문서의 하이퍼텍스트 link"""
232 | return self.item[f"{sys._getframe().f_code.co_name}"]
233 |
234 | @property
235 | def description(self) -> str:
236 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
237 | return self.item[f"{sys._getframe().f_code.co_name}"]
238 |
239 |
240 | @dataclass(frozen=True)
241 | class WebkrNaipy(SearchNaipy):
242 | @property
243 | def title(self) -> str:
244 | """검색 결과 문서의 제목"""
245 | return self.item[f"{sys._getframe().f_code.co_name}"]
246 |
247 | @property
248 | def link(self) -> str:
249 | """검색 결과 문서의 하이퍼텍스트 link"""
250 | return self.item[f"{sys._getframe().f_code.co_name}"]
251 |
252 | @property
253 | def description(self) -> str:
254 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
255 | return self.item[f"{sys._getframe().f_code.co_name}"]
256 |
257 |
258 | @dataclass(frozen=True)
259 | class ShopNaipy(SearchNaipy):
260 | @property
261 | def title(self) -> str:
262 | """검색 결과 문서의 제목"""
263 | return self.item[f"{sys._getframe().f_code.co_name}"]
264 |
265 | @property
266 | def link(self) -> str:
267 | """검색 결과 문서의 하이퍼텍스트 link"""
268 | return self.item[f"{sys._getframe().f_code.co_name}"]
269 |
270 | @property
271 | def image(self) -> str:
272 | """썸네일 이미지의 URL"""
273 | return self.item[f"{sys._getframe().f_code.co_name}"]
274 |
275 | @property
276 | def lprice(self) -> str:
277 | """최저가"""
278 | return self.item[f"{sys._getframe().f_code.co_name}"]
279 |
280 | @property
281 | def hprice(self) -> str:
282 | """최고가"""
283 | if self.item == "":
284 | return None
285 | return self.item[f"{sys._getframe().f_code.co_name}"]
286 |
287 | @property
288 | def mallName(self) -> str:
289 | """쇼핑몰의 상호"""
290 | return self.item[f"{sys._getframe().f_code.co_name}"]
291 |
292 | @property
293 | def productId(self) -> int:
294 | """해당 상품에 대한 ID"""
295 | return int(self.item[f"{sys._getframe().f_code.co_name}"])
296 |
297 | @property
298 | def productType(self) -> int:
299 | """상품군 정보"""
300 | return int(self.item[f"{sys._getframe().f_code.co_name}"])
301 |
302 | @property
303 | def brand(self) -> str:
304 | """해당 상품의 브랜드 명"""
305 | return self.item[f"{sys._getframe().f_code.co_name}"]
306 |
307 | @property
308 | def maker(self) -> str:
309 | """해당 상품의 제조사 명"""
310 | return self.item[f"{sys._getframe().f_code.co_name}"]
311 |
312 | @property
313 | def category(self) -> List[str]:
314 | """해당 상품의 제조사 명"""
315 | result = []
316 | for i in range(4):
317 | result += self.item[f"{sys._getframe().f_code.co_name}{i+1}"]
318 | return result
319 |
320 |
321 | @dataclass(frozen=True)
322 | class DocNaipy(SearchNaipy):
323 | @property
324 | def title(self) -> str:
325 | """문서의 제목"""
326 | return self.item[f"{sys._getframe().f_code.co_name}"]
327 |
328 | @property
329 | def link(self) -> str:
330 | """검색 결과 문서의 하이퍼텍스트"""
331 | return self.item[f"{sys._getframe().f_code.co_name}"]
332 |
333 | @property
334 | def description(self) -> str:
335 | """검색 결과 문서의 내용을 요약한 패시지 정보"""
336 | return self.item[f"{sys._getframe().f_code.co_name}"]
337 |
--------------------------------------------------------------------------------