Skip to content

Commit dd6de2c

Browse files
authored
Merge pull request #6 from mic1on/useCURL
useCURL
2 parents f99365a + 23154f7 commit dd6de2c

File tree

11 files changed

+229
-34
lines changed

11 files changed

+229
-34
lines changed

docs/.vitepress/config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ export default withPwa(defineConfig({
114114
text: 'useLogger',
115115
link: '/api/logger',
116116
},
117+
{
118+
text: 'useParser',
119+
link: '/api/parser',
120+
},
117121
],
118122
},
119123
{
@@ -168,10 +172,6 @@ export default withPwa(defineConfig({
168172
text: 'useTo',
169173
link: '/utils/to',
170174
},
171-
{
172-
text: 'useURL',
173-
link: '/utils/url',
174-
},
175175
{
176176
text: 'utils',
177177
link: '/utils/utils',

docs/api/parser.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
title: useLogger
3+
outline: deep
4+
---
5+
# useParser
6+
7+
解析器,提供了一些常用的方法。
8+
9+
## curl <Badge type="warning" text="useCURL" />
10+
11+
用于解析CURL命令。
12+
```python
13+
from usepy import useParser, useCURL
14+
15+
test_curl_command = """
16+
curl 'http://localhost:3333/api/parser.html' \
17+
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
18+
-H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
19+
-H 'Cache-Control: no-cache' \
20+
-H 'Connection: keep-alive' \
21+
-H 'Cookie: Pycharm-fe1126fc=eb62da0d-7fad-4f5f-9396-37ee80df090c; csrftoken=SIf8rv13bnXYAarsi6aN6mpuHHZBjzKTBADTtouFI5U28Na8l9TFu9IFROY1auqH' \
22+
-H 'Pragma: no-cache' \
23+
-H 'Sec-Fetch-Dest: document' \
24+
-H 'Sec-Fetch-Mode: navigate' \
25+
-H 'Sec-Fetch-Site: same-origin' \
26+
-H 'Sec-Fetch-User: ?1' \
27+
-H 'Upgrade-Insecure-Requests: 1' \
28+
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76' \
29+
-H 'sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"' \
30+
-H 'sec-ch-ua-mobile: ?0' \
31+
-H 'sec-ch-ua-platform: "macOS"' \
32+
--compressed
33+
"""
34+
# curl = useParser.curl(test_curl_command)
35+
curl = useCURL(test_curl_command)
36+
print(curl.url) # http://localhost:3333/api/parser.html
37+
print(curl.method) # GET
38+
print(curl.data) # None
39+
print(curl.headers) # ...
40+
print(curl.cookies) # ...
41+
```
42+
43+
44+
## useURL
45+
46+
用于解析URL。
47+
48+
```python
49+
from usepy import useURL
50+
51+
url = useURL("https://www.google.com/search?q=usepy&ie=utf-8")
52+
```
53+
54+
55+
| url.scheme | url.netloc | url.query | Coourl.query_dictl | url.path |
56+
| ---------- | :------------: | :--------------: | :-----------------------------: | -------: |
57+
| https | www.google.com | q=usepy&ie=utf-8 | `{'q': 'usepy', 'ie': 'utf-8'}` | /search |

docs/utils/url.md

Lines changed: 0 additions & 25 deletions
This file was deleted.

example/parser_curl_demo.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from usepy import useParser, useCURL
2+
3+
if __name__ == '__main__':
4+
test_curl_command = """
5+
your curl command
6+
"""
7+
# curl = useParser.curl(test_curl_command)
8+
curl = useCURL(test_curl_command)
9+
print(curl.url)
10+
print(curl.method)
11+
print(curl.data, type(curl.data))
12+
print(curl.headers)
13+
print(curl.cookies)

src/usepy/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
# logstash_handler,
2727
# JsonFormatter
2828
)
29+
from .parser import (
30+
useParser,
31+
useCURL,
32+
useURL
33+
)
2934
from .utils import (
3035
useTimer,
3136
useTimerManager,
@@ -39,8 +44,7 @@
3944
useCleanHtml,
4045
useBloomFilter,
4146
useTo,
42-
useIs,
43-
useURL,
47+
useIs
4448
)
4549
from .utils.bloom_filter import BloomFilter as useBloomFilter
4650

@@ -64,6 +68,10 @@
6468
'useLogger',
6569
'useLoggerIntercept',
6670
'useLoggerInterceptUvicorn',
71+
# parser
72+
'useParser',
73+
'useCURL',
74+
'useURL',
6775
# utils
6876
'useIs',
6977
'useTo',
@@ -81,5 +89,4 @@
8189
'useDateTime',
8290
'usePath',
8391
'useThread',
84-
'useURL',
8592
]

src/usepy/parser/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from .curl import CURL as useCURL
2+
from .url import URL as useURL
3+
4+
5+
class Parser:
6+
curl = useCURL
7+
url = useURL
8+
9+
10+
useParser = Parser

src/usepy/parser/curl.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""
2+
3+
curl 'http://testing.ceegdev.com:9090/api/user/profile' \
4+
-H 'Accept: application/json, text/plain, */*' \
5+
-H 'Accept-Language: zh-cn' \
6+
-H 'Authorization: 0a96d4f1-375b-4846-85a2-5731a1a2b3ac' \
7+
-H 'Cache-Control: no-cache' \
8+
-H 'Connection: keep-alive' \
9+
-H 'Origin: http://localhost:4000' \
10+
-H 'Pragma: no-cache' \
11+
-H 'Referer: http://localhost:4000/' \
12+
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76' \
13+
--compressed \
14+
--insecure
15+
16+
"""
17+
import argparse
18+
import re
19+
import shlex
20+
from collections import OrderedDict
21+
from typing import Optional, Dict, Generator, Tuple
22+
23+
from usepy.utils import useCookieToDict
24+
25+
26+
def normalize_newlines(multiline_text):
27+
return multiline_text.replace(" \\\n", " ").replace("^", "")
28+
29+
30+
class CURL:
31+
32+
def __init__(self, curl_command):
33+
tokens = shlex.split(normalize_newlines(curl_command))
34+
self.args = self._parse_args(tokens)
35+
36+
@staticmethod
37+
def _parse_args(tokens):
38+
parser = argparse.ArgumentParser()
39+
parser.add_argument('command')
40+
parser.add_argument('url')
41+
parser.add_argument('-d', '--data')
42+
parser.add_argument('-b', '--data-binary', '--data-raw', default=None)
43+
parser.add_argument('-X', default='')
44+
parser.add_argument('-H', '--header', action='append', default=[])
45+
parser.add_argument('--compressed', action='store_true')
46+
parser.add_argument('-k', '--insecure', action='store_true')
47+
parser.add_argument('--user', '-u', default=())
48+
parser.add_argument('-i', '--include', action='store_true')
49+
parser.add_argument('-s', '--silent', action='store_true')
50+
return parser.parse_args(tokens)
51+
52+
@property
53+
def url(self) -> str:
54+
return self.args.url
55+
56+
@property
57+
def method(self) -> str:
58+
_method = self.args.X.lower() if self.args.X else ('post' if self.data else 'get')
59+
return _method.upper()
60+
61+
@property
62+
def data(self) -> Optional[str]:
63+
return self.args.data or self.args.data_binary
64+
65+
@property
66+
def auth(self) -> Optional[tuple]:
67+
if self.args.user:
68+
return tuple(self.args.user.split(':'))
69+
return None
70+
71+
@property
72+
def verify(self) -> bool:
73+
return self.args.insecure
74+
75+
@property
76+
def compressed(self) -> bool:
77+
return self.args.compressed
78+
79+
# 优化这段代码
80+
def _get_headers_kv(self) -> Generator[Tuple[str, str], None, None]:
81+
for header in self.args.header:
82+
if header.startswith(':'):
83+
occurrence = [m.start() for m in re.finditer(':', header)]
84+
key, value = header[:occurrence[1]], header[occurrence[1] + 1:]
85+
else:
86+
key, value = header.split(":", 1)
87+
yield key, value.strip()
88+
89+
@property
90+
def headers(self) -> Optional[Dict]:
91+
_headers = OrderedDict()
92+
for key, value in self._get_headers_kv():
93+
# skip cookie
94+
if key.lower().strip("$") == 'cookie':
95+
continue
96+
_headers[key] = value
97+
return dict(_headers) if _headers else None
98+
99+
@property
100+
def cookies(self) -> Optional[Dict]:
101+
for key, value in self._get_headers_kv():
102+
if key.lower().strip("$") == 'cookie':
103+
return useCookieToDict(value)
104+
return None
File renamed without changes.

src/usepy/utils/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,3 @@
1212
data_to_dict as useDataToDict,
1313
sizeof_fmt as useSizeofFmt,
1414
)
15-
from .url import URL as useURL

src/usepy/utils/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def cookie_to_dict(cookies: str) -> dict:
1313
:param cookies: cookie字符串
1414
:return: dict
1515
"""
16-
return dict(x.split('=') for x in cookies.split('; ')) # noqa
16+
return dict(x.split('=', 1) for x in cookies.split('; ')) # noqa
1717

1818

1919
def headers_to_dict(headers: str) -> dict:

tests/test_parser/test_curl.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from usepy import useCURL
2+
3+
4+
def test_curl():
5+
test_curl_command = """
6+
curl 'https://www.baidu.com/' \
7+
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
8+
-H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
9+
-H 'Cache-Control: no-cache' \
10+
-H 'Connection: keep-alive' \
11+
-H 'Cookie: BIDUPSID=xxxxxxxx' \
12+
-H 'Pragma: no-cache' \
13+
-H 'Referer: https://www.baidu.com/' \
14+
-H 'Sec-Fetch-Dest: document' \
15+
-H 'Sec-Fetch-Mode: navigate' \
16+
-H 'Sec-Fetch-Site: same-origin' \
17+
-H 'Sec-Fetch-User: ?1' \
18+
-H 'Upgrade-Insecure-Requests: 1' \
19+
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76' \
20+
-H 'sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"' \
21+
-H 'sec-ch-ua-mobile: ?0' \
22+
-H 'sec-ch-ua-platform: "macOS"' \
23+
--compressed
24+
"""
25+
curl = useCURL(test_curl_command)
26+
assert curl.url == 'https://www.baidu.com/'
27+
assert curl.method == 'GET'
28+
assert not curl.data
29+
assert len(curl.headers.keys()) == 15
30+
assert len(curl.cookies.keys()) == 1

0 commit comments

Comments
 (0)