Various convenient features about requests.
Project description
이 설명은 최신 버전이 아닐 수 있습니다. 만약 최신 버전을 확인하고 싶으시다면 이 깃허브 링크를 참고하세요.
requests-utils
requests
라이브러리와 BeatifulSoup를 합쳐 몇 줄의 코드를 하나에 합칠 수 있으며,
간단하게 async, cache를 불러와 사용할 수 있습니다.
또한 no_empty_result
는 BeatifulSoup에는 없는 독자적인 기능으로 typing 경고나 오류 가능성을 줄입니다.
소소하지만 유용하며, 서너 줄의 코드 작성량을 줄여주는 라이브러리입니다.
시작하기
-
파이썬을 설치합니다.
-
터미널에서 다음과 같은 명령어를 실행합니다.
pip install -U requests-utils
requests-util
가 아니니 주의하세요!s
를 꼭 붙여야 합니다!
requests와 bs4는 같이 설치되지만 BeatifulSoup의 추가적인 parser인 lxml와 html5lib는 기본으로 제공하지 않습니다.
따라서 lxml, html5lib 등은 스스로 설치하셔야 오류가 나지 않을 수 있습니다.
만약 설치되지 않은 상태로 해당 parser를 이용한다면 NoParserError
가 납니다.
사용법
requests_utils.requests
모듈
requests_utils.requests
모듈은 다음과 같이 import해 사용할 수 있습니다.
from requests_utils import requests
이 라이브러리는 requests 라이브러리와 99% 호환되며 (심지어 타입 힌트도 requests 라이브러리와 같이 잘 작동합니다!), 그 위에 편리한 기능을 얹은 형태입니다. 즉, 기존 import requests
를 위의 코드로 교체하면 절대 호환성 오류가 나지 않습니다.
이 모듈은 requests
라이브러리를 호환성 있게 대체하는 목적이 가지고 있습니다. 따라서 기존 코드에서 requests
를 import하는 부분을 위의 코드로 변경하여도 깨지는 경우가 거의 발생하지 않습니다. 호환되지 않는 경우를 알고 싶다면 requests 모듈과 호환되지 않는 부분
파트를 참고하세요.
참고: 예시들의 경우 거의 get
요청을 위주로 설명하지만, 다른 모든 메소드(options/head/post/put/patch/delete)에서도 동일하게 작동합니다.
requests의 Session도 비슷하게 사용할 수 있습니다
from requests_utils import requests
with requests.Session() as session:
... # cget, attempts 등 모든 기능 사용 가능
기본값
기본값들은 각각 적당한 값으로 설정되어 있습니다.
기본값들은 다음과 같고 request.get/options/head/post/put/patch/delete에서 적용됩니다.
timeout 기본값: 40
headers 기본값: {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "ko-KR,ko;q=0.9",
"Sec-Ch-Ua": '"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": '"Windows"',
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
}
attempts 기본값: 1
>>> from requests_utils import requests
>>>
>>> from requests_utils import requests
>>> res = requests.get("https://httpbin.org/headers")
>>> res.json()['headers']
{'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'ko-KR,ko;q=0.9',
'Host': 'httpbin.org',
'Sec-Ch-Ua': '"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
'X-Amzn-Trace-Id': ...}
응답
requests_utils.requests
모듈의 get/options/head/post/put/patch/delete 함수는 모두 ResponseProxy를 리턴합니다.
ResponseProxy는 기존 Response와 100% 호환되는 Response의 subclass입니다. 자세한 내용은 ResponseProxy
항목을 참고하세요.
기능을 잘 이해하지 못했다면 기존에 Response를 사용하던 방식대로 사용하시면 문제 없이 작동합니다.
attempts
attempts
는 파라미터로, 모종의 이유로 ConnectionError
가 발생했을 때 같은 requests를 몇 번 더 반복할 것인지 설정하는 파라미터입니다.
만약 10번을 실행하고도 실패했다면 가장 최근에 실패한 연결의 이유를 보여줍니다.
>>> from requests_utils import requests
>>>
>>> requests.get('https://some-not-working-website.com', attempts=10)
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
WARNING:root:Retring...
Traceback (most recent call last):
...
socket.gaierror: [Errno 11001] getaddrinfo failed
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
urllib3.exceptions.NameResolutionError: <urllib3.connection.HTTPSConnection object at ...>: Failed to resolve 'some-not-working-website.com' ([Errno 11001] getaddrinfo failed)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='some-not-working-website.com', port=443): Max retries exceeded with url: / (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at ...>: Failed to resolve 'some-not-working-website.com' ([Errno 11001] getaddrinfo failed)"))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='some-not-working-website.com', port=443): Max retries exceeded with url: / (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at ...>: Failed to resolve 'some-not-working-website.com' ([Errno 11001] getaddrinfo failed)"))
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
ConnectionError: Trying 10 times but failed to get data.
URL: https://some-not-working-website.com
일반 요청 함수
일반 requests.get/options/head/post/put/patch/delete를 requests
에서 사용하던 방식 그대로 사용할 수 있습니다.
다음은 requests.get과 post의 예시입니다. requests
모듈과 똑같이 작동합니다.
>>> from requests_utils import requests
>>>
>>> requests.get('https://jsonplaceholder.typicode.com/todos/1').json() # API that can send request in order to test. Don't execute this command unless you trust this API.
{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}
>>> requests.post('https://jsonplaceholder.typicode.com/todos', json={
... 'title': 'foo',
... 'body': 'bar',
... 'userId': 1,
... }).json()
{'title': 'foo', 'body': 'bar', 'userId': 1, 'id': 201} # Same with original requests library
캐시된 요청 함수
일반 requests.get/../delete 요청과 동일하지만 캐시됩니다. 이때 캐시는 후술할 비동기적이며 캐시된 요청 함수
와 공유됩니다. 하지만 각 메소드들끼리 공유되지는 않습니다. 앞에 c
를 붙여 requests.cget/coptions/chead/cpost/cput/cpatch/cdelete로 함수를 작성해 사용할 수 있습니다.
같은 URL을 보내도 다른 결과를 응답할 수 있는 동적인 서비스를 사용하거나(시간에 따른 응답의 변화를 반영하지 않음) 응답의 크기가 클 경우(메모리가 낭비될 수 있음) 사용하지 않는 것이 좋습니다.
>>> # 기기 사양과 인터넷 연결 품질에 따라 결과는 다를 수 있음
>>> import timeit
>>>
>>> timeit.timeit('requests.get("https://python.org")', number=10, setup='from requests_utils import requests')
1.1833231999917189 # 기기 사양과 인터넷 연결 품질에 따라 다름: 10번의 연결 모두 request를 보냄
>>> timeit.timeit('requests.cget("https://python.org")', number=10, setup='from requests_utils import requests')
0.10267569999268744 # : 처음 한 번만 request를 보내고 그 뒤는 캐시에서 값을 불러옴
비동기적인 요청 함수
비동기적인 요청을 보냅니다. 앞에 a
를 붙여 requests.aget/aoptions/ahead/apost/aput/apatch/adelete로 함수를 작성합니다.
run_in_executer
는 기본적으로 켜져 있습니다. 자세한 내용은 아래의 run_in_executer 사용
을 참고하세요.
>>> import asyncio
>>>
>>> from requests_utils import requests
>>>
>>> res = asyncio.run(requests.aget('https://python.org'))
>>> res
<response [200]>
비동기적이며 캐시된 요청 함수
비동기적이며 캐시되는 요청입니다. 이때 캐시는 같은 메소드라면 캐시된 요청 함수
와 공유됩니다. 앞에 ac
를 붙여 requests.acget/acoptions/achead/acpost/acput/acpatch/acdelete로 함수를 작성합니다.
같은 URL을 보내도 다른 결과를 응답할 수 있는 동적인 서비스를 사용하거나(시간에 따른 응답의 변화를 반영하지 않음) 응답의 크기가 클 경우(메모리가 낭비될 수 있음) 사용하지 않는 것이 좋습니다.
run_in_executer
는 기본적으로 켜져 있습니다. 자세한 내용은 아래의 run_in_executer 사용
을 참고하세요.
>>> import asyncio
>>> import timeit
>>>
>>> timeit.timeit('asyncio.run(requests.aget("https://python.org"))', number=10, setup='from requests_utils import requests; import asyncio')
0.8676127000362612 # 기기 사양과 인터넷 연결 품질에 따라 다름: 10번의 연결 모두 request를 보냄
>>> timeit.timeit('asyncio.run(requests.acget("https://python.org"))', number=10, setup='from requests_utils import requests; import asyncio')
0.11984489997848868 # 처음 한 번만 request를 보내고 그 뒤는 캐시를 불러옴
run_in_executer
사용
비동기적인 요청(aget, acget 등 a가 붙은 메소드)에서는 run_in_executer
parameter를 사용할 수 있습니다. 이 parameter는 함수가 다른 쓰레드에서 돌게 합니다. 순차적으로 프로그램이 동작할 때에는 큰 차이가 없지만 병렬적으로 프로그램을 돌릴 때 큰 속도 향상을 기대할 수 있습니다.
아래와 같이 asyncio.gather
를 이용하면 큰 성능 향상을 보일 수 있습니다.
import asyncio
import time
from requests_utils import requests
async def masure_coroutine_time(coroutine):
start = time.perf_counter()
await coroutine
end = time.perf_counter()
print(end - start)
async def main():
# 단일 request를 보낼 때(큰 차이 없음)
req = requests.aget('https://python.org', run_in_executor=False)
await masure_coroutine_time(req) # 0.07465070000034757
req = requests.aget('https://python.org')
await masure_coroutine_time(req) # 0.05844969999452587
# 여러 request를 보낼 때(큰 속도 향상을 보임)
reqs = (requests.aget(f'https://python.org/{i}', run_in_executor=False) for i in range(10)) # 더미 url을 만듦
await masure_coroutine_time(asyncio.gather(*reqs)) # run_in_executor를 사용하지 않을 때: 느림(3.7874760999984574)
reqs = (requests.aget(f'https://python.org/{i}') for i in range(10)) # 더미 url을 만듦
await masure_coroutine_time(asyncio.gather(*reqs)) # run_in_executor를 사용할 때(기본값): 빠름(0.11582900000212248)
if __name__ == '__main__':
asyncio.run(main())
requests 모듈과 호환되지 않는 부분
이 모듈은 requests
라이브러리와 거의 모든 부분에서 호환되지만 호환되지 않는 부분이 몇 가지 있습니다.
dunder method(__dunder__
)
잠정적 버그의 이유가 될 수 있다는 이유 혹은 기술적인 이유로 일부 dunder method는 불러와지지 않거나 호환되지 않습니다.
사용할 수 없거나 requests 라이브러리와 일치하지 않는 dunder method: __builtins__
, __cached__
, __doc__
, __file__
, __loader__
, __name__
, __package__
, __spec__
사용 가능하고 requests 라이브러리와 일치하는 dunder method: __author__
, __author_email__
, __build__
, __cake__
, __copyright__
, __description__
, __license__
, __title__
, __url__
, __version__
>>> import requests
>>> requests.__name__
'requests'
>>> requests.__path__
['some path']
>>> requests.__cake__
'✨ 🍰 ✨'
>>>
>>> from requests_utils import requests
>>> requests.__name__ # 호환되지 않는 dunder method
'requests_utils.requests_proxy' # requests와 값이 다름
>>> requests.__path__ # 사용할 수 없고 호환되지 않는 dunder method
AttributeError: module 'requests_utils.requests_' has no attribute '__path__'
>>> requests.__cake__ # 호환되는 dunder method
'✨ 🍰 ✨'
import
requests_utils.requests
는 거의 모든 경우에서 import 관련 호환성이 유지됩니다. 하지만 import와 관련해서는 몇 가지 규칙이 존재합니다.
requests_utils.requests
는 from requests_utils import requests
의 형태로만 사용할 수 있습니다.
# 각 라인에서 윗줄과 아랫줄은 각각 requests를 import 할 때와 `requests_utils.requests`를 import할 때를 나타냅니다.
# requests 모듈 import
import requests
from requests_utils import requests # 가능
따라서 다음과 같은 경우는 requests_utils.requests
에서 import가 불가능합니다.
# requests의 하위 모듈 import
import requests.models # 가능
import requests_utils.requests.models # 불가능!
# requests의 하위 모듈 import (w/ from .. import ...)
from request import models # 가능
from requests_utils.requests import models # 불가능!
# requests의 하위 모듈의 하위 구성 요소 import
from request.models import Response # 가능
from requests_utils.requests.models import Response # 불가능!
이런 경우엔 모듈 import를 이용하면 해결됩니다..
예를 들어 다음과 같은 코드가 있다고 해 봅시다.
from request.models import Response # 하위 모듈의 하위 구성 요소 import 사용
def is_response(instance):
return isinstance(instance, Response)
이 코드는 다음과 같이 문제를 해결할 수 있습니다.
# requests.models.Response로 바꾸기.
# 장점: 깔끔하고 error-prone하지 않음.
from requests_utils import requests # requests 모듈 import
def is_response(instance):
return isinstance(instance, requests.models.Response) # requests.models.Response로 변경함
# Response 정의하기.
# 장점: 코드를 수정할 필요가 없음.
from requests_utils import requests
Response = requests.models.Response
def is_response(instance):
return isinstance(instance, Response)
개인의 선호에 따라 원하는 방식으로 사용하시면 됩니다.
ResponseProxy
ResponseProxy
는 이 라이브러리에서 requests.get/options/head/post/put/patch/delete를 사용할 경우의 리턴값입니다. 기존 Response와 100% 호환되면서도 추가적인 함수 6개를 제공합니다.
호환성
이 파트에서는 주석에 내용을 적었습니다.
>>> # 두 모듈을 동시에 사용해야 하니 이름을 변경하겠습니다.
>>> import requests as orginal_requests
>>> from requests_utils import requests as utils_requsts
>>>
>>> # requests 모듈은 Response를 응답합니다.
>>> response1 = orginal_requests.get("https://peps.python.org/pep-0020/") # 정적인 웹사이트
>>> print(response1)
<Response [200]>
>>> print(type(response1)) # Response 객체
<class 'requests.models.Response'>
>>> # requests_utils.requests모듈은 ResponseProxy를 응답합니다.
>>> response2 = utils_requsts.get("https://peps.python.org/pep-0020/")
>>> print(response2)
<Response [200]>
>>> print(type(response2)) # ResponseProxy 객체
<class 'requests_utils.response_proxy.ResponseProxy'>
>>>
>>> # 다음의 모든 검사들을 통과합니다.
>>> assert response1.text == response2.text
>>> assert response1.status_code == response2.status_code
>>> assert response1.url == response2.url
>>> assert response1.content == response2.content
>>>
>>> # 하지만 RequestsProxy에는 이러한 추가적인 기능들이 존재합니다.
>>> print(response2.soup())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
...
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>
>>> print(response2.soup_select('title'))
[<title>PEP 20 – The Zen of Python | peps.python.org</title>, <title>Following system colour scheme</title>, <title>Selected dark colour scheme</title>, <title>Selected light colour scheme</title>]
>>> print(response2.soup_select_one('p', no_empty_result=True).text)
Long time Pythoneer Tim Peters succinctly channels the BDFL’s guiding
principles for Python’s design into 20 aphorisms, only 19 of which
have been written down.
>>>
>>> from requests.models import Response
>>> # RequestsProxy는 Requsests의 subclass입니다.
>>> # 따라서 isinstance 검사를 통과합니다.
>>> isinstance(response2, Response)
True
>>> # 물론 subclass이기 때문에 '==' 검사는 통과하지 않습니다.
>>> type(response1) == type(response2)
False
기본 구조
ResponseProxy
에는 여러 모듈들이 있으며, 크게 세 가지 종류로 분류됩니다.
- soup류:
.soup()
,.soup_select()
,.soup_select_one()
기본적인 함수입니다. - xml류:
.xml()
,.xml_select()
,.xml_select_one()
soup류에서 parser가 'xml'인 경우입니다.
각각의 종류에는 세 가지 함수가 있으며 함수 각각의 기능은 다음과 같습니다.
.soup()
/.xml()
: BeatifulSoup로 해석된 코드가 나옵니다..soup_select()
/.xml_select()
:.soup().select()
와 비슷합니다..soup_select_one()
/.xml_select_one()
:.soup().select_one()
과 비슷합니다.
자세한 내용은 아래를 살펴보세요.
기본값
parser
기본으로 설정된 값: 'html.parser'
.soup()
.soup()
는 텍스트나 response를 받아 BeatifulSoup
로 내보냅니다.
이때 인자는 response와 response.text 모두 가능하지만 response를 사용하는 것을 권합니다. 그러면 더욱 상세한 오류 메시지를 받을 수 있습니다.
>>> from requests_utils import requests
>>>
>>> response = requests.get("https://python.org")
>>> response.soup() # BeatifulSoup에서 사용 가능한 모든 parameter 사용 가능
<!DOCTYPE html>
...
</body>
</html>
이 함수는 사실상 BeatifulSoup
를 통과시키는 것과 같습니다. 아래의 코드는 위의 코드와 거의 같습니다.
>>> import requests
>>> from bs4 import BeautifulSoup
>>>
>>> response = requests.get("https://python.org")
>>> BeautifulSoup(response.text)
<!DOCTYPE html>
<!DOCTYPE html>
...
</body>
</html>
parser가 없을 경우 BeatifulSoup
는 FeatureNotFound
에러가 나오지만 .soup()
는 NoParserError
가 나옵니다.
.soup_select()
.soup_select()
는 텍스트나 response를 받아 BeatifulSoup의 Tag로 내보냅니다. selector
parameter는 CSS 선택자를 받습니다.
>>> from requests_utils import requests
>>>
>>> response = requests.get("https://python.org")
>>> response.soup_select("p")
[<p><strong>Notice:</strong> While JavaScript is not essential for this website
...]
아래의 코드는 위의 코드와 유사하게 동작합니다.
>>> import requests
>>> from bs4 import BeautifulSoup
>>>
>>> response = requests.get('https://python.org')
>>> soup = BeautifulSoup(response.text).select('p')
>>> soup
[<p><strong>Notice:</strong> While JavaScript is not essential for this website
...]
이 함수의 독특한 점은, no_empty_result
라는 parameter의 존재입니다. 이 parameter가 True이면 .select()의 결과가 빈 리스트일때 EmptyResultError
를 냅니다.
>>> from requests_utils import requests
>>>
>>> response = requests.get("https://python.org")
>>> response.soup_select("data-some-complex-and-error-prone-selector")
[]
>>>
>>> response = requests.get("https://python.org")
>>> response.soup_select(
... "data-some-complex-and-error-prone-selector",
... no_empty_result=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "...souptools.py", line 148, in soup_select
raise EmptyResultError(
requests_utils.exceptions.EmptyResultError: Result of select is empty list("[]"). This error happens probably because of invalid selector or URL. Check if both selector and URL are valid. Set to False `no_empty_result` if empty list is intended. It may also because of selector is not matched with URL.
selector: data-some-complex-and-error-prone-selector, URL: https://www.python.org/
.soup_select_one()
.soup_select_one()
는 텍스트나 response를 받아 BeatifulSoup의 Tag로 내보냅니다. selector
parameter는 CSS 선택자를 받습니다.
>>> from requests_utils import requests
>>>
>>> response = requests.get('https://python.org')
>>> response.soup_select_one('p strong', no_empty_result=True)
<strong>Notice:</strong>
아래의 코드는 위의 코드와 유사하게 동작합니다.
>>> import requests
>>> from bs4 import BeautifulSoup
>>>
>>> response = requests.get('https://python.org')
>>> soup = BeautifulSoup(response.text, 'html.parser').select('p strong')
>>> if soup is None: # no_empty_result 관련 확인 코드
... raise Exception
...
>>> soup
<strong>Notice:</strong>
no_empty_result
parameter가 True이면 .select_one()의 결과가 None일때 EmptyResultError
를 냅니다.
이 기능은 타입 힌트에서도 유용하게 쓰일 수 있고, 오류를 더 명확히 하는 데에도 도움을 줍니다.
기존 BeatifulSoup에서는 .select_one()
의 리턴값을 Tag | None
으로 표시했기 때문에 만약 .select_one().text
와 같은 코드를 사용하려고 하면 정적 타입 검사 도구들에서 오류를 발생시켰습니다.
특히 .select_one()
의 결과가 None이 되면 'NoneType' object has no attribute 'text'
라는 어떤 부분에서 오류가 났는지 한눈에 확인하기 힘든 오류 메시지가 나왔습니다.
no_empty_result
를 이용하면 이러한 문제들을 해결할 수 있습니다.
no_empty_result
를 True로 하면 타입 검사 도구들도 조용해지고, 혹시라도 None이 결과값이 될 때 대신 훨씬 더 자세하며 해결책을 포함한 오류 메시지를 만들어 냅니다.
>>> from requests_utils import requests
>>>
>>> response = requests.get("https://python.org")
>>> print(response.soup_select_one("data-some-complex-and-error-prone-selector"))
None # 실제로 None이 결과값으로 나오진 않고 그냥 조용히 종료됨.
>>>
>>> response = requests.get("https://python.org")
>>> response.soup_select_one(
... "data-some-complex-and-error-prone-selector",
... no_empty_result=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "...souptools.py", line 220, in soup_select_one
raise EmptyResultError(
requests_utils.exceptions.EmptyResultError: Result of select_one is None. This error happens probably because of invalid selector or URL. Check if both selector and URL are valid. Set to False `no_empty_result` if empty list is intended. It may also because of selector is not matched with URL.
selector: data-some-complex-and-error-prone-selector, URL: https://www.python.org/
xml 관련 함수
ResponseProxy
의 soup
관련 함수에서 soup
를 xml
로 치환하면 xml 함수가 됩니다.
이 함수들은 parser가 'xml'
이라는 점을 제외하고는 soup와 차이점이 없습니다.
예시 코드는 다음과 같습니다
>>> from requests_utils import requests
>>>
>>> response = requests.get('https://www.w3schools.com/xml/plant_catalog.xml')
>>> selected = response.xml_select('LIGHT', no_empty_result=True)
>>> selected
[<LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Sunny</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sunny</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sunny</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sunny</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sun</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>]
위의 코드는 아래의 코드와 거의 같습니다.
>>> from requests_utils import requests
>>> from functools import partial
>>>
>>> response = requests.get('https://www.w3schools.com/xml/plant_catalog.xml')
>>> # corespond to `.xml_select()`
>>> xml_select_partial = partial(response.soup_select, parser='xml')
>>> selected = xml_select_partial('LIGHT', no_empty_result=True)
>>> selected
[<LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Sunny</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sunny</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sunny</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sunny</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Sun or Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Sun</LIGHT>, <LIGHT>Mostly Shady</LIGHT>, <LIGHT>Shade</LIGHT>, <LIGHT>Shade</LIGHT>]
CustomDefaults
[!WARNING] 대부분의 경우에서는 requests_utils.requests.Session을 사용하는 것이 낫습니다. requests.Session에서도 cget이나 attempts 등의 기능을 사용할 수 있습니다.
CustomDefaults
를 통해 직접 기본값을 설정할 수 있습니다. 이 값으로 일반 get/options/head/post/put/patch/delete 및 c../a../ac.. 함수의 기본값을 효과적으로 설정할 수 있습니다.
>>> from requests_utils import CustomDefaults
>>>
>>> requests = CustomDefaults(headers={'User-Agent': 'Hello, World!'})
>>> requests.get("https://api-bdc.net/data/client-info").json()['userAgentRaw']
'Hello, World!'
라이선스 정보
이 프로그램은 MIT 라이선스로 공유됩니다.
이 프로그램의 일부는 requests(Apache License 2.0) 라이브러리에 있던 코드를 포함합니다. Some part of this program contains code from requests library.
이 프로그램의 일부는 typeshed(Apache License 2.0 or MIT License) 라이브러리에 있던 코드를 포함합니다. Some part of this program contains code from typeshed library.
Relese Note
2.3.0 (2023-10-05): BroadcastList 복원, sessions_with_tools 추가
0.2.3 (2023-09-19): header 기본값 변경, ConnectionError시 에러 한 개만 보이는 것으로 변경, attempts로 재시도할 때 성공했을 때 메시지 추가, retry에서 url 제거, setup.py와 관련 파일 변경
0.2.2 (2023-09-08): attempt parameter를 attempts로 변경, BroadcastList 제거
0.2.1 (2023-08-31): py.typed 추가, freeze_dict_and_list 추가
0.2.0 (2023-08-27): CustomDefaults 추가
0.1.1 (2023-08-27): 첫 릴리즈
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for requests_utils-0.3.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0611e87b331138c5a04060d1eaea3bf1e1f9350be952d1b048ee9211d059ea27 |
|
MD5 | 1f1df860c923a13289ac9766e3cdb2c0 |
|
BLAKE2b-256 | 293b26fa7a6d1807223877863f348580d42f7966bf55017556d9c402e10f945d |