hs-net 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.
Files changed (77) hide show
  1. hs_net-0.1.0/.github/workflows/ci.yml +91 -0
  2. hs_net-0.1.0/.github/workflows/publish.yml +81 -0
  3. hs_net-0.1.0/.gitignore +29 -0
  4. hs_net-0.1.0/.pre-commit-config.yaml +18 -0
  5. hs_net-0.1.0/.python-version +1 -0
  6. hs_net-0.1.0/LICENSE +21 -0
  7. hs_net-0.1.0/PKG-INFO +200 -0
  8. hs_net-0.1.0/README.md +164 -0
  9. hs_net-0.1.0/docs/.gitignore +14 -0
  10. hs_net-0.1.0/docs/README.md +29 -0
  11. hs_net-0.1.0/docs/docs/_nav.json +17 -0
  12. hs_net-0.1.0/docs/docs/api/_meta.json +1 -0
  13. hs_net-0.1.0/docs/docs/api/config.mdx +94 -0
  14. hs_net-0.1.0/docs/docs/api/exceptions.mdx +101 -0
  15. hs_net-0.1.0/docs/docs/api/net.mdx +140 -0
  16. hs_net-0.1.0/docs/docs/api/response.mdx +117 -0
  17. hs_net-0.1.0/docs/docs/api/sync-net.mdx +70 -0
  18. hs_net-0.1.0/docs/docs/guide/_meta.json +1 -0
  19. hs_net-0.1.0/docs/docs/guide/configuration.mdx +142 -0
  20. hs_net-0.1.0/docs/docs/guide/getting-started.mdx +104 -0
  21. hs_net-0.1.0/docs/docs/guide/introduction.mdx +61 -0
  22. hs_net-0.1.0/docs/docs/index.md +37 -0
  23. hs_net-0.1.0/docs/docs/public/rspress-dark-logo.png +0 -0
  24. hs_net-0.1.0/docs/docs/public/rspress-icon.png +0 -0
  25. hs_net-0.1.0/docs/docs/public/rspress-light-logo.png +0 -0
  26. hs_net-0.1.0/docs/docs/usage/_meta.json +1 -0
  27. hs_net-0.1.0/docs/docs/usage/advanced.mdx +138 -0
  28. hs_net-0.1.0/docs/docs/usage/basic-request.mdx +92 -0
  29. hs_net-0.1.0/docs/docs/usage/error-handling.mdx +95 -0
  30. hs_net-0.1.0/docs/docs/usage/http-methods.mdx +104 -0
  31. hs_net-0.1.0/docs/docs/usage/middleware.mdx +170 -0
  32. hs_net-0.1.0/docs/docs/usage/multi-engine.mdx +104 -0
  33. hs_net-0.1.0/docs/docs/usage/selectors.mdx +190 -0
  34. hs_net-0.1.0/docs/package.json +21 -0
  35. hs_net-0.1.0/docs/pnpm-lock.yaml +2426 -0
  36. hs_net-0.1.0/docs/rspress.config.ts +29 -0
  37. hs_net-0.1.0/docs/tsconfig.json +28 -0
  38. hs_net-0.1.0/examples/01_basic_get.py +54 -0
  39. hs_net-0.1.0/examples/02_http_methods.py +56 -0
  40. hs_net-0.1.0/examples/03_selectors.py +134 -0
  41. hs_net-0.1.0/examples/04_middleware.py +128 -0
  42. hs_net-0.1.0/examples/05_multi_engine.py +108 -0
  43. hs_net-0.1.0/examples/06_advanced.py +176 -0
  44. hs_net-0.1.0/examples/07_real_world.py +147 -0
  45. hs_net-0.1.0/pyproject.toml +75 -0
  46. hs_net-0.1.0/src/hs_net/__init__.py +34 -0
  47. hs_net-0.1.0/src/hs_net/_request_builder.py +90 -0
  48. hs_net-0.1.0/src/hs_net/client.py +415 -0
  49. hs_net-0.1.0/src/hs_net/config.py +52 -0
  50. hs_net-0.1.0/src/hs_net/engines/__init__.py +3 -0
  51. hs_net-0.1.0/src/hs_net/engines/aiohttp_engine.py +109 -0
  52. hs_net-0.1.0/src/hs_net/engines/base.py +148 -0
  53. hs_net-0.1.0/src/hs_net/engines/curl_cffi_engine.py +193 -0
  54. hs_net-0.1.0/src/hs_net/engines/httpx_engine.py +193 -0
  55. hs_net-0.1.0/src/hs_net/engines/requests_engine.py +103 -0
  56. hs_net-0.1.0/src/hs_net/engines/requests_go_engine.py +187 -0
  57. hs_net-0.1.0/src/hs_net/exceptions.py +103 -0
  58. hs_net-0.1.0/src/hs_net/models.py +62 -0
  59. hs_net-0.1.0/src/hs_net/py.typed +0 -0
  60. hs_net-0.1.0/src/hs_net/response/__init__.py +4 -0
  61. hs_net-0.1.0/src/hs_net/response/response.py +164 -0
  62. hs_net-0.1.0/src/hs_net/response/selector.py +92 -0
  63. hs_net-0.1.0/src/hs_net/signals.py +108 -0
  64. hs_net-0.1.0/src/hs_net/sync_client.py +407 -0
  65. hs_net-0.1.0/src/hs_net/ua.py +21 -0
  66. hs_net-0.1.0/tests/__init__.py +0 -0
  67. hs_net-0.1.0/tests/conftest.py +69 -0
  68. hs_net-0.1.0/tests/test_client_integration.py +224 -0
  69. hs_net-0.1.0/tests/test_config.py +69 -0
  70. hs_net-0.1.0/tests/test_engines.py +93 -0
  71. hs_net-0.1.0/tests/test_exceptions.py +82 -0
  72. hs_net-0.1.0/tests/test_models.py +80 -0
  73. hs_net-0.1.0/tests/test_request_builder.py +133 -0
  74. hs_net-0.1.0/tests/test_response.py +199 -0
  75. hs_net-0.1.0/tests/test_signals.py +117 -0
  76. hs_net-0.1.0/tests/test_ua.py +51 -0
  77. hs_net-0.1.0/uv.lock +2172 -0
@@ -0,0 +1,91 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [dev]
6
+ pull_request:
7
+ branches: [main]
8
+ workflow_call:
9
+
10
+ jobs:
11
+ lint:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v4
18
+
19
+ - name: Set up Python
20
+ run: uv python install 3.13
21
+
22
+ - name: Install dependencies
23
+ run: uv sync --group dev
24
+
25
+ - name: Ruff lint
26
+ run: uv run ruff check src/
27
+
28
+ - name: Ruff format check
29
+ run: uv run ruff format --check src/
30
+
31
+ - name: Bandit security check
32
+ run: uv run bandit -c pyproject.toml -r src/
33
+
34
+ test:
35
+ runs-on: ubuntu-latest
36
+ strategy:
37
+ matrix:
38
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+
42
+ - name: Install uv
43
+ uses: astral-sh/setup-uv@v4
44
+
45
+ - name: Set up Python ${{ matrix.python-version }}
46
+ run: uv python install ${{ matrix.python-version }}
47
+
48
+ - name: Install dependencies
49
+ run: uv sync --group dev
50
+
51
+ - name: Run tests
52
+ run: uv run pytest tests/ -v --ignore=tests/test_client_integration.py
53
+
54
+ integration-test:
55
+ runs-on: ubuntu-latest
56
+ needs: [lint, test]
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+
60
+ - name: Install uv
61
+ uses: astral-sh/setup-uv@v4
62
+
63
+ - name: Set up Python
64
+ run: uv python install 3.13
65
+
66
+ - name: Install dependencies
67
+ run: uv sync --group dev
68
+
69
+ - name: Run integration tests
70
+ run: uv run pytest tests/test_client_integration.py -v
71
+
72
+ build:
73
+ runs-on: ubuntu-latest
74
+ needs: [lint, test]
75
+ steps:
76
+ - uses: actions/checkout@v4
77
+
78
+ - name: Install uv
79
+ uses: astral-sh/setup-uv@v4
80
+
81
+ - name: Set up Python
82
+ run: uv python install 3.13
83
+
84
+ - name: Build package
85
+ run: uv build
86
+
87
+ - name: Upload artifacts
88
+ uses: actions/upload-artifact@v4
89
+ with:
90
+ name: dist
91
+ path: dist/
@@ -0,0 +1,81 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ check:
10
+ runs-on: ubuntu-latest
11
+ outputs:
12
+ should_publish: ${{ steps.check_version.outputs.should_publish }}
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Check if version changed
19
+ id: check_version
20
+ run: |
21
+ # 获取当前版本
22
+ CURRENT_VERSION=$(grep -oP 'version = "\K[^"]+' pyproject.toml | head -1)
23
+ echo "Current version: $CURRENT_VERSION"
24
+
25
+ # 检查该版本 tag 是否已存在
26
+ if git tag -l "v$CURRENT_VERSION" | grep -q .; then
27
+ echo "Version v$CURRENT_VERSION already published, skipping."
28
+ echo "should_publish=false" >> $GITHUB_OUTPUT
29
+ else
30
+ echo "New version v$CURRENT_VERSION detected."
31
+ echo "should_publish=true" >> $GITHUB_OUTPUT
32
+ fi
33
+
34
+ ci:
35
+ needs: check
36
+ if: needs.check.outputs.should_publish == 'true'
37
+ uses: ./.github/workflows/ci.yml
38
+
39
+ publish:
40
+ runs-on: ubuntu-latest
41
+ needs: [check, ci]
42
+ if: needs.check.outputs.should_publish == 'true'
43
+ permissions:
44
+ id-token: write
45
+ contents: write
46
+ environment: pypi
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+
50
+ - name: Install uv
51
+ uses: astral-sh/setup-uv@v4
52
+
53
+ - name: Set up Python
54
+ run: uv python install 3.13
55
+
56
+ - name: Build package
57
+ run: uv build
58
+
59
+ - name: Publish to PyPI
60
+ run: uv publish
61
+ env:
62
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
63
+
64
+ - name: Get version
65
+ id: version
66
+ run: |
67
+ VERSION=$(grep -oP 'version = "\K[^"]+' pyproject.toml | head -1)
68
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
69
+
70
+ - name: Create Git tag
71
+ run: |
72
+ git tag "v${{ steps.version.outputs.version }}"
73
+ git push origin "v${{ steps.version.outputs.version }}"
74
+
75
+ - name: Create GitHub Release
76
+ uses: softprops/action-gh-release@v2
77
+ with:
78
+ tag_name: "v${{ steps.version.outputs.version }}"
79
+ name: "v${{ steps.version.outputs.version }}"
80
+ files: dist/*
81
+ generate_release_notes: true
@@ -0,0 +1,29 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # IDE
13
+ .idea/
14
+ .vscode/
15
+ *.swp
16
+ *.swo
17
+
18
+ # Testing
19
+ .pytest_cache/
20
+ .coverage
21
+ htmlcov/
22
+
23
+ # Docs
24
+ docs/node_modules/
25
+ docs/doc_build/
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
@@ -0,0 +1,18 @@
1
+ repos:
2
+ # bandit - 安全检查
3
+ - repo: https://github.com/PyCQA/bandit
4
+ rev: 1.7.10
5
+ hooks:
6
+ - id: bandit
7
+ args: ["-c", "pyproject.toml"]
8
+ exclude: ^tests/
9
+ additional_dependencies: ["bandit[toml]"]
10
+
11
+ # ruff - lint + format
12
+ - repo: https://github.com/astral-sh/ruff-pre-commit
13
+ rev: 'v0.6.9'
14
+ hooks:
15
+ - id: ruff
16
+ args: [--fix, --exit-non-zero-on-fix, --show-fixes]
17
+ - id: ruff-format
18
+
@@ -0,0 +1 @@
1
+ 3.13
hs_net-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 昊色居士
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.
hs_net-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,200 @@
1
+ Metadata-Version: 2.4
2
+ Name: hs-net
3
+ Version: 0.1.0
4
+ Summary: 统一多引擎的增强型 HTTP 客户端,内置重试、选择器、信号中间件
5
+ Project-URL: Homepage, https://github.com/x-haose/hs-net
6
+ Project-URL: Repository, https://github.com/x-haose/hs-net
7
+ Project-URL: Issues, https://github.com/x-haose/hs-net/issues
8
+ Author-email: 昊色居士 <x-haose@users.noreply.github.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: aiohttp,async,http,httpx,network,scraping
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Internet :: WWW/HTTP
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: aiohttp>=3.9
24
+ Requires-Dist: blinker>=1.8
25
+ Requires-Dist: curl-cffi>=0.7
26
+ Requires-Dist: fake-useragent>=1.5
27
+ Requires-Dist: furl>=2.1
28
+ Requires-Dist: httpx[http2,socks]>=0.28
29
+ Requires-Dist: jmespath>=1.1.0
30
+ Requires-Dist: parsel>=1.9
31
+ Requires-Dist: pydantic>=2.9
32
+ Requires-Dist: requests-go>=1.0
33
+ Requires-Dist: requests>=2.31
34
+ Requires-Dist: tenacity>=8.3
35
+ Description-Content-Type: text/markdown
36
+
37
+ # hs-net
38
+
39
+ 统一多引擎的增强型 HTTP 客户端,内置重试、选择器、信号中间件,同步异步全支持。
40
+
41
+ ## 特性
42
+
43
+ - **多引擎切换** — httpx、aiohttp、curl-cffi、requests、requests-go,统一 API,一行切换
44
+ - **同步 & 异步** — `Net`(异步)和 `SyncNet`(同步),接口完全一致
45
+ - **智能选择器** — 内置 CSS、XPath、正则、JMESPath 四种数据提取
46
+ - **自动重试** — 基于 tenacity,可配置次数、间隔、随机抖动
47
+ - **信号中间件** — 请求前、响应后、重试时三个钩子
48
+ - **反爬支持** — curl-cffi 浏览器 TLS 指纹模拟 + 随机 User-Agent
49
+
50
+ ## 安装
51
+
52
+ ```bash
53
+ pip install hs-net
54
+ ```
55
+
56
+ > Python >= 3.10,所有引擎开箱即用,无需额外安装。
57
+
58
+ ## 快速开始
59
+
60
+ ### 异步
61
+
62
+ ```python
63
+ import asyncio
64
+ from hs_net import Net
65
+
66
+ async def main():
67
+ async with Net() as net:
68
+ resp = await net.get("https://example.com")
69
+ print(resp.css("title::text").get()) # Example Domain
70
+
71
+ asyncio.run(main())
72
+ ```
73
+
74
+ ### 同步
75
+
76
+ ```python
77
+ from hs_net import SyncNet
78
+
79
+ with SyncNet() as net:
80
+ resp = net.get("https://example.com")
81
+ print(resp.css("title::text").get()) # Example Domain
82
+ ```
83
+
84
+ ## 引擎切换
85
+
86
+ ```python
87
+ # httpx(默认)
88
+ Net(engine="httpx")
89
+
90
+ # aiohttp
91
+ Net(engine="aiohttp")
92
+
93
+ # curl-cffi(支持浏览器指纹模拟)
94
+ Net(engine="curl_cffi", engine_options={"impersonate": "chrome120"})
95
+
96
+ # requests(仅同步)
97
+ SyncNet(engine="requests")
98
+
99
+ # requests-go
100
+ Net(engine="requests_go")
101
+ ```
102
+
103
+ ## 数据提取
104
+
105
+ ```python
106
+ resp = await net.get("https://example.com")
107
+
108
+ # CSS 选择器
109
+ resp.css("title::text").get()
110
+
111
+ # XPath
112
+ resp.xpath("//h1/text()").get()
113
+
114
+ # 正则
115
+ resp.re_first(r"价格: (\d+)元")
116
+
117
+ # JMESPath(JSON 响应)
118
+ resp = await net.get("https://api.example.com/users")
119
+ resp.jmespath("data[?age > `18`].name")
120
+ ```
121
+
122
+ ## 配置
123
+
124
+ ```python
125
+ from hs_net import Net, NetConfig
126
+
127
+ # 方式 1:构造函数参数
128
+ net = Net(
129
+ engine="httpx",
130
+ base_url="https://api.example.com/v1",
131
+ timeout=30.0,
132
+ retries=5,
133
+ retry_delay=1.0,
134
+ user_agent="chrome",
135
+ verify=False,
136
+ concurrency=10,
137
+ )
138
+
139
+ # 方式 2:NetConfig 对象
140
+ config = NetConfig(
141
+ engine="curl_cffi",
142
+ retries=3,
143
+ user_agent="random",
144
+ engine_options={"impersonate": "chrome120"},
145
+ )
146
+ net = Net(config=config)
147
+ ```
148
+
149
+ 参数优先级:`请求方法参数 > 构造函数参数 > NetConfig 默认值`
150
+
151
+ ## 信号中间件
152
+
153
+ ```python
154
+ async with Net() as net:
155
+
156
+ @net.on_request_before
157
+ async def add_auth(req_data):
158
+ req_data.headers["Authorization"] = "Bearer token"
159
+ return req_data
160
+
161
+ @net.on_response_after
162
+ async def log_response(resp):
163
+ print(f"{resp.status_code} {resp.url}")
164
+
165
+ @net.on_request_retry
166
+ async def on_retry(exc):
167
+ print(f"重试: {exc}")
168
+
169
+ resp = await net.get("https://example.com")
170
+ ```
171
+
172
+ ## 错误处理
173
+
174
+ ```python
175
+ from hs_net import Net, StatusException, RetryExhausted, RequestException
176
+
177
+ async with Net() as net:
178
+ try:
179
+ resp = await net.get("https://httpbin.org/status/404")
180
+ except RetryExhausted as e:
181
+ print(f"{e.attempts} 次重试失败: {e.last_exception}")
182
+ except StatusException as e:
183
+ print(f"HTTP {e.code}")
184
+ except RequestException as e:
185
+ print(f"请求异常: {e}")
186
+ ```
187
+
188
+ ## 文档
189
+
190
+ 完整文档见 `docs/` 目录,本地预览:
191
+
192
+ ```bash
193
+ cd docs
194
+ pnpm install
195
+ pnpm run dev
196
+ ```
197
+
198
+ ## License
199
+
200
+ MIT
hs_net-0.1.0/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # hs-net
2
+
3
+ 统一多引擎的增强型 HTTP 客户端,内置重试、选择器、信号中间件,同步异步全支持。
4
+
5
+ ## 特性
6
+
7
+ - **多引擎切换** — httpx、aiohttp、curl-cffi、requests、requests-go,统一 API,一行切换
8
+ - **同步 & 异步** — `Net`(异步)和 `SyncNet`(同步),接口完全一致
9
+ - **智能选择器** — 内置 CSS、XPath、正则、JMESPath 四种数据提取
10
+ - **自动重试** — 基于 tenacity,可配置次数、间隔、随机抖动
11
+ - **信号中间件** — 请求前、响应后、重试时三个钩子
12
+ - **反爬支持** — curl-cffi 浏览器 TLS 指纹模拟 + 随机 User-Agent
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ pip install hs-net
18
+ ```
19
+
20
+ > Python >= 3.10,所有引擎开箱即用,无需额外安装。
21
+
22
+ ## 快速开始
23
+
24
+ ### 异步
25
+
26
+ ```python
27
+ import asyncio
28
+ from hs_net import Net
29
+
30
+ async def main():
31
+ async with Net() as net:
32
+ resp = await net.get("https://example.com")
33
+ print(resp.css("title::text").get()) # Example Domain
34
+
35
+ asyncio.run(main())
36
+ ```
37
+
38
+ ### 同步
39
+
40
+ ```python
41
+ from hs_net import SyncNet
42
+
43
+ with SyncNet() as net:
44
+ resp = net.get("https://example.com")
45
+ print(resp.css("title::text").get()) # Example Domain
46
+ ```
47
+
48
+ ## 引擎切换
49
+
50
+ ```python
51
+ # httpx(默认)
52
+ Net(engine="httpx")
53
+
54
+ # aiohttp
55
+ Net(engine="aiohttp")
56
+
57
+ # curl-cffi(支持浏览器指纹模拟)
58
+ Net(engine="curl_cffi", engine_options={"impersonate": "chrome120"})
59
+
60
+ # requests(仅同步)
61
+ SyncNet(engine="requests")
62
+
63
+ # requests-go
64
+ Net(engine="requests_go")
65
+ ```
66
+
67
+ ## 数据提取
68
+
69
+ ```python
70
+ resp = await net.get("https://example.com")
71
+
72
+ # CSS 选择器
73
+ resp.css("title::text").get()
74
+
75
+ # XPath
76
+ resp.xpath("//h1/text()").get()
77
+
78
+ # 正则
79
+ resp.re_first(r"价格: (\d+)元")
80
+
81
+ # JMESPath(JSON 响应)
82
+ resp = await net.get("https://api.example.com/users")
83
+ resp.jmespath("data[?age > `18`].name")
84
+ ```
85
+
86
+ ## 配置
87
+
88
+ ```python
89
+ from hs_net import Net, NetConfig
90
+
91
+ # 方式 1:构造函数参数
92
+ net = Net(
93
+ engine="httpx",
94
+ base_url="https://api.example.com/v1",
95
+ timeout=30.0,
96
+ retries=5,
97
+ retry_delay=1.0,
98
+ user_agent="chrome",
99
+ verify=False,
100
+ concurrency=10,
101
+ )
102
+
103
+ # 方式 2:NetConfig 对象
104
+ config = NetConfig(
105
+ engine="curl_cffi",
106
+ retries=3,
107
+ user_agent="random",
108
+ engine_options={"impersonate": "chrome120"},
109
+ )
110
+ net = Net(config=config)
111
+ ```
112
+
113
+ 参数优先级:`请求方法参数 > 构造函数参数 > NetConfig 默认值`
114
+
115
+ ## 信号中间件
116
+
117
+ ```python
118
+ async with Net() as net:
119
+
120
+ @net.on_request_before
121
+ async def add_auth(req_data):
122
+ req_data.headers["Authorization"] = "Bearer token"
123
+ return req_data
124
+
125
+ @net.on_response_after
126
+ async def log_response(resp):
127
+ print(f"{resp.status_code} {resp.url}")
128
+
129
+ @net.on_request_retry
130
+ async def on_retry(exc):
131
+ print(f"重试: {exc}")
132
+
133
+ resp = await net.get("https://example.com")
134
+ ```
135
+
136
+ ## 错误处理
137
+
138
+ ```python
139
+ from hs_net import Net, StatusException, RetryExhausted, RequestException
140
+
141
+ async with Net() as net:
142
+ try:
143
+ resp = await net.get("https://httpbin.org/status/404")
144
+ except RetryExhausted as e:
145
+ print(f"{e.attempts} 次重试失败: {e.last_exception}")
146
+ except StatusException as e:
147
+ print(f"HTTP {e.code}")
148
+ except RequestException as e:
149
+ print(f"请求异常: {e}")
150
+ ```
151
+
152
+ ## 文档
153
+
154
+ 完整文档见 `docs/` 目录,本地预览:
155
+
156
+ ```bash
157
+ cd docs
158
+ pnpm install
159
+ pnpm run dev
160
+ ```
161
+
162
+ ## License
163
+
164
+ MIT
@@ -0,0 +1,14 @@
1
+ # Local
2
+ .DS_Store
3
+ *.local
4
+ *.log*
5
+
6
+ # Dist
7
+ node_modules
8
+ dist/
9
+ doc_build/
10
+
11
+ # IDE
12
+ .vscode/*
13
+ !.vscode/extensions.json
14
+ .idea
@@ -0,0 +1,29 @@
1
+ # Rspress website
2
+
3
+ ## Setup
4
+
5
+ Install the dependencies:
6
+
7
+ ```bash
8
+ npm install
9
+ ```
10
+
11
+ ## Get started
12
+
13
+ Start the dev server:
14
+
15
+ ```bash
16
+ npm run dev
17
+ ```
18
+
19
+ Build the website for production:
20
+
21
+ ```bash
22
+ npm run build
23
+ ```
24
+
25
+ Preview the production build locally:
26
+
27
+ ```bash
28
+ npm run preview
29
+ ```
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "text": "指南",
4
+ "link": "/guide/introduction",
5
+ "activeMatch": "/guide/"
6
+ },
7
+ {
8
+ "text": "使用教程",
9
+ "link": "/usage/basic-request",
10
+ "activeMatch": "/usage/"
11
+ },
12
+ {
13
+ "text": "API 参考",
14
+ "link": "/api/net",
15
+ "activeMatch": "/api/"
16
+ }
17
+ ]
@@ -0,0 +1 @@
1
+ ["net", "sync-net", "response", "config", "exceptions"]