mcp-query-table 0.2.6__tar.gz → 0.3.2__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 (27) hide show
  1. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/PKG-INFO +38 -13
  2. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/README.md +37 -12
  3. mcp_query_table-0.3.2/mcp_query_table/__init__.py +6 -0
  4. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table/__main__.py +7 -6
  5. mcp_query_table-0.3.2/mcp_query_table/_version.py +1 -0
  6. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table/enums.py +7 -0
  7. mcp_query_table-0.3.2/mcp_query_table/providers/baidu.py +82 -0
  8. mcp_query_table-0.3.2/mcp_query_table/providers/n.py +77 -0
  9. mcp_query_table-0.3.2/mcp_query_table/providers/yuanbao.py +82 -0
  10. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table/server.py +32 -11
  11. mcp_query_table-0.3.2/mcp_query_table/sites/__init__.py +0 -0
  12. mcp_query_table-0.3.2/mcp_query_table/tool.py +263 -0
  13. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table.egg-info/PKG-INFO +38 -13
  14. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table.egg-info/SOURCES.txt +4 -0
  15. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/pyproject.toml +1 -0
  16. mcp_query_table-0.2.6/mcp_query_table/__init__.py +0 -4
  17. mcp_query_table-0.2.6/mcp_query_table/_version.py +0 -1
  18. mcp_query_table-0.2.6/mcp_query_table/tool.py +0 -165
  19. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/LICENSE +0 -0
  20. {mcp_query_table-0.2.6/mcp_query_table/sites → mcp_query_table-0.3.2/mcp_query_table/providers}/__init__.py +0 -0
  21. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table/sites/eastmoney.py +0 -0
  22. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table/sites/iwencai.py +0 -0
  23. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table/sites/tdx.py +0 -0
  24. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table.egg-info/dependency_links.txt +0 -0
  25. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table.egg-info/requires.txt +0 -0
  26. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/mcp_query_table.egg-info/top_level.txt +0 -0
  27. {mcp_query_table-0.2.6 → mcp_query_table-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp_query_table
3
- Version: 0.2.6
3
+ Version: 0.3.2
4
4
  Summary: query table from website, support MCP
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: MIT License
@@ -39,13 +39,20 @@ Dynamic: license-file
39
39
 
40
40
  # mcp_query_table
41
41
 
42
- 基于`playwright`实现的财经网页表格爬虫,支持`Model Context Protocol (MCP) `。目前可查询来源为
42
+ 1. 基于`playwright`实现的财经网页表格爬虫,支持`Model Context Protocol (MCP) `。目前可查询来源为
43
43
 
44
- - [同花顺i问财](http://iwencai.com/)
45
- - [通达信问小达](https://wenda.tdx.com.cn/)
46
- - [东方财富条件选股](https://xuangu.eastmoney.com/)
44
+ - [同花顺i问财](http://iwencai.com/)
45
+ - [通达信问小达](https://wenda.tdx.com.cn/)
46
+ - [东方财富条件选股](https://xuangu.eastmoney.com/)
47
47
 
48
- 实盘时,如果某网站宕机或改版,可以立即切换到其他网站。(注意:不同网站的表格结构不同,需要提前做适配)
48
+ 实盘时,如果某网站宕机或改版,可以立即切换到其他网站。(注意:不同网站的表格结构不同,需要提前做适配)
49
+
50
+ 2. 基于`playwright`实现的大语言模型调用爬虫。目前可用来源为
51
+ - [纳米搜索](https://www.n.cn/)
52
+ - [腾讯元宝](https://yuanbao.tencent.com/)
53
+ - [百度AI搜索](https://chat.baidu.com/)
54
+
55
+ `RooCode`提供了`Human Reply`功能。但发现`纳米搜索`网页版复制时格式破坏,所以研发了此功能
49
56
 
50
57
  ## 安装
51
58
 
@@ -63,7 +70,7 @@ from mcp_query_table import *
63
70
 
64
71
 
65
72
  async def main() -> None:
66
- async with BrowserManager(port=9222, browser_path=None, debug=True) as bm:
73
+ async with BrowserManager(cdp_endpoint="http://127.0.0.1:9222", executable_path=None, debug=True) as bm:
67
74
  # 问财需要保证浏览器宽度>768,防止界面变成适应手机
68
75
  page = await bm.get_page()
69
76
  df = await query(page, '收益最好的200只ETF', query_type=QueryType.ETF, max_page=1, site=Site.THS)
@@ -75,8 +82,14 @@ async def main() -> None:
75
82
  # TODO 东财翻页要提前登录
76
83
  df = await query(page, '今日涨幅前5的概念板块;', query_type=QueryType.Board, max_page=3, site=Site.EastMoney)
77
84
  print(df)
78
- bm.release_page(page)
85
+
86
+ output = await chat(page, "1+2等于多少?", provider=Provider.YuanBao)
87
+ print(output)
88
+ output = await chat(page, "3+4等于多少?", provider=Provider.YuanBao, create=True)
89
+ print(output)
90
+
79
91
  print('done')
92
+ bm.release_page(page)
80
93
  await page.wait_for_timeout(2000)
81
94
 
82
95
 
@@ -119,7 +132,7 @@ if __name__ == '__main__':
119
132
 
120
133
  确保可以在控制台中执行`python -m mcp_query_table -h`。如果不能,可能要先`pip install mcp_query_table`
121
134
 
122
- 在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`browser_path`是`Chrome`的绝对路径。
135
+ 在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`executable_path`是`Chrome`的绝对路径。
123
136
 
124
137
  ### STDIO方式
125
138
 
@@ -133,7 +146,9 @@ if __name__ == '__main__':
133
146
  "mcp_query_table",
134
147
  "--format",
135
148
  "markdown",
136
- "--browser_path",
149
+ "--cdp_endpoint",
150
+ "http://127.0.0.1:9222",
151
+ "--executable_path",
137
152
  "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
138
153
  ]
139
154
  }
@@ -146,11 +161,20 @@ if __name__ == '__main__':
146
161
  先在控制台中执行如下命令,启动`MCP`服务
147
162
 
148
163
  ```commandline
149
- python -m mcp_query_table --format markdown --browser_path "C:\Program Files\Google\Chrome\Application\chrome.exe" --transport sse --mcp_port 8000
164
+ python -m mcp_query_table --format markdown --transport sse --port 8000
150
165
  ```
151
166
 
152
167
  然后就可以连接到`MCP`服务了
153
- http://localhost:8000/sse
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "mcp_query_table": {
173
+ "url": "http://127.0.0.1:8000/sse"
174
+ }
175
+ }
176
+ }
177
+ ```
154
178
 
155
179
  ## 使用`MCP Inspector`进行调试
156
180
 
@@ -158,7 +182,8 @@ http://localhost:8000/sse
158
182
  npx @modelcontextprotocol/inspector python -m mcp_query_table --format markdown
159
183
  ```
160
184
 
161
- 打开浏览器并翻页是一个比较耗时的操作,会导致`MCP Inspector`页面超时,可以`http://localhost:5173/?timeout=60000` 表示超时时间为60秒
185
+ 打开浏览器并翻页是一个比较耗时的操作,会导致`MCP Inspector`页面超时,可以`http://localhost:5173/?timeout=600000`
186
+ 表示超时时间为600秒
162
187
 
163
188
  第一次尝试编写`MCP`项目,可能会有各种问题,欢迎大家交流。
164
189
 
@@ -1,12 +1,19 @@
1
1
  # mcp_query_table
2
2
 
3
- 基于`playwright`实现的财经网页表格爬虫,支持`Model Context Protocol (MCP) `。目前可查询来源为
3
+ 1. 基于`playwright`实现的财经网页表格爬虫,支持`Model Context Protocol (MCP) `。目前可查询来源为
4
4
 
5
- - [同花顺i问财](http://iwencai.com/)
6
- - [通达信问小达](https://wenda.tdx.com.cn/)
7
- - [东方财富条件选股](https://xuangu.eastmoney.com/)
5
+ - [同花顺i问财](http://iwencai.com/)
6
+ - [通达信问小达](https://wenda.tdx.com.cn/)
7
+ - [东方财富条件选股](https://xuangu.eastmoney.com/)
8
8
 
9
- 实盘时,如果某网站宕机或改版,可以立即切换到其他网站。(注意:不同网站的表格结构不同,需要提前做适配)
9
+ 实盘时,如果某网站宕机或改版,可以立即切换到其他网站。(注意:不同网站的表格结构不同,需要提前做适配)
10
+
11
+ 2. 基于`playwright`实现的大语言模型调用爬虫。目前可用来源为
12
+ - [纳米搜索](https://www.n.cn/)
13
+ - [腾讯元宝](https://yuanbao.tencent.com/)
14
+ - [百度AI搜索](https://chat.baidu.com/)
15
+
16
+ `RooCode`提供了`Human Reply`功能。但发现`纳米搜索`网页版复制时格式破坏,所以研发了此功能
10
17
 
11
18
  ## 安装
12
19
 
@@ -24,7 +31,7 @@ from mcp_query_table import *
24
31
 
25
32
 
26
33
  async def main() -> None:
27
- async with BrowserManager(port=9222, browser_path=None, debug=True) as bm:
34
+ async with BrowserManager(cdp_endpoint="http://127.0.0.1:9222", executable_path=None, debug=True) as bm:
28
35
  # 问财需要保证浏览器宽度>768,防止界面变成适应手机
29
36
  page = await bm.get_page()
30
37
  df = await query(page, '收益最好的200只ETF', query_type=QueryType.ETF, max_page=1, site=Site.THS)
@@ -36,8 +43,14 @@ async def main() -> None:
36
43
  # TODO 东财翻页要提前登录
37
44
  df = await query(page, '今日涨幅前5的概念板块;', query_type=QueryType.Board, max_page=3, site=Site.EastMoney)
38
45
  print(df)
39
- bm.release_page(page)
46
+
47
+ output = await chat(page, "1+2等于多少?", provider=Provider.YuanBao)
48
+ print(output)
49
+ output = await chat(page, "3+4等于多少?", provider=Provider.YuanBao, create=True)
50
+ print(output)
51
+
40
52
  print('done')
53
+ bm.release_page(page)
41
54
  await page.wait_for_timeout(2000)
42
55
 
43
56
 
@@ -80,7 +93,7 @@ if __name__ == '__main__':
80
93
 
81
94
  确保可以在控制台中执行`python -m mcp_query_table -h`。如果不能,可能要先`pip install mcp_query_table`
82
95
 
83
- 在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`browser_path`是`Chrome`的绝对路径。
96
+ 在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`executable_path`是`Chrome`的绝对路径。
84
97
 
85
98
  ### STDIO方式
86
99
 
@@ -94,7 +107,9 @@ if __name__ == '__main__':
94
107
  "mcp_query_table",
95
108
  "--format",
96
109
  "markdown",
97
- "--browser_path",
110
+ "--cdp_endpoint",
111
+ "http://127.0.0.1:9222",
112
+ "--executable_path",
98
113
  "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
99
114
  ]
100
115
  }
@@ -107,11 +122,20 @@ if __name__ == '__main__':
107
122
  先在控制台中执行如下命令,启动`MCP`服务
108
123
 
109
124
  ```commandline
110
- python -m mcp_query_table --format markdown --browser_path "C:\Program Files\Google\Chrome\Application\chrome.exe" --transport sse --mcp_port 8000
125
+ python -m mcp_query_table --format markdown --transport sse --port 8000
111
126
  ```
112
127
 
113
128
  然后就可以连接到`MCP`服务了
114
- http://localhost:8000/sse
129
+
130
+ ```json
131
+ {
132
+ "mcpServers": {
133
+ "mcp_query_table": {
134
+ "url": "http://127.0.0.1:8000/sse"
135
+ }
136
+ }
137
+ }
138
+ ```
115
139
 
116
140
  ## 使用`MCP Inspector`进行调试
117
141
 
@@ -119,7 +143,8 @@ http://localhost:8000/sse
119
143
  npx @modelcontextprotocol/inspector python -m mcp_query_table --format markdown
120
144
  ```
121
145
 
122
- 打开浏览器并翻页是一个比较耗时的操作,会导致`MCP Inspector`页面超时,可以`http://localhost:5173/?timeout=60000` 表示超时时间为60秒
146
+ 打开浏览器并翻页是一个比较耗时的操作,会导致`MCP Inspector`页面超时,可以`http://localhost:5173/?timeout=600000`
147
+ 表示超时时间为600秒
123
148
 
124
149
  第一次尝试编写`MCP`项目,可能会有各种问题,欢迎大家交流。
125
150
 
@@ -0,0 +1,6 @@
1
+ from ._version import __version__
2
+
3
+ from .enums import QueryType, Site, Provider
4
+ from .tool import BrowserManager, query, chat
5
+
6
+ TIMEOUT = 1000 * 60 * 2 # 2分钟,在抓取EventStream数据时等待数据返回,防止外层30秒超时
@@ -10,19 +10,20 @@ def main():
10
10
 
11
11
  parser.add_argument("--format", type=str, help="输出格式",
12
12
  default='markdown', choices=['markdown', 'csv', 'json'])
13
- parser.add_argument("--cdp_port", type=int, help="浏览器远程调试端口",
14
- default=9222)
15
- parser.add_argument("--browser_path", type=str, help="浏览器类型",
13
+ parser.add_argument("--cdp_endpoint", type=str, help="浏览器CDP调试地址",
14
+ default="http://127.0.0.1:9222")
15
+ parser.add_argument("--executable_path", type=str, help="浏览器类型",
16
16
  default=r'C:\Program Files\Google\Chrome\Application\chrome.exe')
17
17
 
18
18
  parser.add_argument("--transport", type=str, help="传输类型",
19
19
  default='stdio', choices=['stdio', 'sse'])
20
- parser.add_argument("--mcp_host", type=str, help="MCP服务端地址",
20
+ parser.add_argument("--host", type=str, help="MCP服务端绑定地址",
21
21
  default='0.0.0.0')
22
- parser.add_argument("--mcp_port", type=int, help="MCP服务端端口",
22
+ parser.add_argument("--port", type=int, help="MCP服务端绑定端口",
23
23
  default='8000')
24
24
  args = parser.parse_args()
25
- serve(args.format, args.cdp_port, args.browser_path, args.transport, args.mcp_host, args.mcp_port)
25
+ serve(args.format, args.cdp_endpoint, args.executable_path,
26
+ args.transport, args.host, args.port)
26
27
 
27
28
 
28
29
  if __name__ == "__main__":
@@ -0,0 +1 @@
1
+ __version__ = "0.3.2"
@@ -19,3 +19,10 @@ class Site(Enum):
19
19
  EastMoney = '东方财富' # 东方财富 条件选股
20
20
  TDX = '通达信' # 通达信 问小达
21
21
  THS = '同花顺' # 同花顺 i问财
22
+
23
+
24
+ class Provider(Enum):
25
+ """提供商"""
26
+ Nami = '纳米搜索' # 360 纳米搜索
27
+ YuanBao = '腾讯元宝' # 腾讯元宝
28
+ BaiDu = '百度AI搜索' # 百度AI搜索
@@ -0,0 +1,82 @@
1
+ """
2
+ 百度AI搜索
3
+
4
+ 限制了输入长度为5000,很多时候会被截断,导致MCP无法正常工作
5
+ """
6
+ import json
7
+
8
+ from playwright.async_api import Page
9
+
10
+ import mcp_query_table
11
+ from mcp_query_table.tool import GlobalVars
12
+
13
+ _PAGE0_ = "https://chat.baidu.com/search"
14
+ _PAGE1_ = "https://chat.baidu.com/aichat/api/conversation"
15
+
16
+ G = GlobalVars()
17
+
18
+
19
+ def read_event_stream(text):
20
+ text1 = []
21
+ text2 = []
22
+ for event in text.split('\n\n'):
23
+ if '"component":"thinkingSteps"' in event:
24
+ if '"reasoningContent":' not in event:
25
+ continue
26
+ lines = event.split('\n')
27
+ for line in lines:
28
+ if line.startswith('data:'):
29
+ t = line[5:]
30
+ t = json.loads(t)['data']['message']['content']['generator']['data']['reasoningContent']
31
+ text1.append(t)
32
+ if '"component":"markdown-yiyan"' in event:
33
+ lines = event.split('\n')
34
+ for line in lines:
35
+ if line.startswith('data:'):
36
+ t = line[5:]
37
+ t = json.loads(t)['data']['message']['content']['generator']['data']['value']
38
+ text2.append(t)
39
+
40
+ text2 = ''.join(text2)
41
+ if len(text1) == 0:
42
+ return text2
43
+ else:
44
+ text1 = ''.join(text1)
45
+ return f"<thinking>{text1}</thinking>\n\n{text2}"
46
+
47
+
48
+ async def on_response(response):
49
+ if response.url.startswith(_PAGE1_):
50
+ # print("on_response", response.url)
51
+ text = await response.text()
52
+ G.set_text(read_event_stream(text))
53
+
54
+
55
+ async def on_route(route):
56
+ # 避免出现 Protocol error (Network.getResponseBody): No data found for resource with given identifier
57
+ # print("on_route", route.request.url)
58
+ if route.request.url == _PAGE1_:
59
+ # TODO 为何只要转发一下就没事了?
60
+ response = await route.fetch(timeout=mcp_query_table.TIMEOUT)
61
+ await route.fulfill(response=response)
62
+ else:
63
+ await route.continue_()
64
+
65
+
66
+ async def chat(page: Page,
67
+ prompt: str,
68
+ create: bool,
69
+ ) -> str:
70
+ if not page.url.startswith(_PAGE0_):
71
+ create = True
72
+
73
+ if create:
74
+ await page.goto(_PAGE0_)
75
+
76
+ await page.route(_PAGE1_, on_route)
77
+ async with page.expect_response(_PAGE1_, timeout=mcp_query_table.TIMEOUT) as response_info:
78
+ await page.locator("#chat-input-box").fill(prompt)
79
+ await page.locator("#chat-input-box").press("Enter")
80
+ await on_response(await response_info.value)
81
+
82
+ return G.get_text()
@@ -0,0 +1,77 @@
1
+ """
2
+ 360 纳米搜索
3
+ """
4
+ import json
5
+
6
+ from playwright.async_api import Page
7
+
8
+ import mcp_query_table
9
+ from mcp_query_table.tool import GlobalVars
10
+
11
+ _PAGE0_ = "https://www.n.cn"
12
+ _PAGE1_ = "https://www.n.cn/search"
13
+ _PAGE2_ = "https://www.n.cn/api/common/chat/v2"
14
+
15
+ G = GlobalVars()
16
+
17
+
18
+ def read_event_stream(text):
19
+ text1 = []
20
+ text2 = []
21
+ for event in text.split('\n\n'):
22
+ if "event: 102" in event:
23
+ if 'data: {"type":"reasoning_text"' in event:
24
+ lines = event.split('\n')
25
+ for line in lines:
26
+ if line.startswith('data: '):
27
+ t = line[6:]
28
+ t = json.loads(t)['message']
29
+ text1.append(t)
30
+ if "event: 200" in event:
31
+ lines = event.split('\n')
32
+ for line in lines:
33
+ if line.startswith('data: '):
34
+ t = line[6:]
35
+ if t == '':
36
+ text2.append('\n')
37
+ elif t == ' ':
38
+ text2.append('\n')
39
+ else:
40
+ text2.append(t)
41
+
42
+ text2 = ''.join(text2)
43
+ if len(text1) == 0:
44
+ return text2
45
+ else:
46
+ text1 = ''.join(text1)
47
+ return f"<thinking>{text1}</thinking>\n\n{text2}"
48
+
49
+
50
+ async def on_response(response):
51
+ if response == _PAGE2_:
52
+ # print("on_response", response.url)
53
+ text = await response.text()
54
+ G.set_text(read_event_stream(text))
55
+
56
+
57
+ async def chat(page: Page,
58
+ prompt: str,
59
+ create: bool,
60
+ ) -> str:
61
+ if not create:
62
+ if not page.url.startswith(_PAGE1_):
63
+ create = True
64
+
65
+ if create:
66
+ await page.goto(_PAGE0_)
67
+ name = "输入任何问题"
68
+ else:
69
+ name = "提出后续问题,Enter发送,Shift+Enter 换行"
70
+
71
+ async with page.expect_response(_PAGE2_, timeout=mcp_query_table.TIMEOUT) as response_info:
72
+ textbox = page.get_by_role("textbox", name=name)
73
+ await textbox.fill(prompt)
74
+ await textbox.press("Enter")
75
+ await on_response(await response_info.value)
76
+
77
+ return G.get_text()
@@ -0,0 +1,82 @@
1
+ """
2
+ 腾讯元宝
3
+ """
4
+ import json
5
+
6
+ from playwright.async_api import Page
7
+
8
+ import mcp_query_table
9
+ from mcp_query_table.tool import GlobalVars
10
+
11
+ _PAGE0_ = "https://yuanbao.tencent.com/"
12
+ _PAGE1_ = "https://yuanbao.tencent.com/api/chat"
13
+
14
+ G = GlobalVars()
15
+
16
+
17
+ def read_event_stream(text):
18
+ text1 = []
19
+ text2 = []
20
+ for event in text.split('\n\n'):
21
+ if 'data: {"type":"think"' in event:
22
+ lines = event.split('\n')
23
+ for line in lines:
24
+ if line.startswith('data: '):
25
+ t = line[6:]
26
+ t = json.loads(t)['content']
27
+ text1.append(t)
28
+ if 'data: {"type":"text"' in event:
29
+ lines = event.split('\n')
30
+ for line in lines:
31
+ if line.startswith('data: '):
32
+ t = line[6:]
33
+ t = json.loads(t).get('msg', "")
34
+ text2.append(t)
35
+
36
+ text2 = ''.join(text2)
37
+ if len(text1) == 0:
38
+ return text2
39
+ else:
40
+ text1 = ''.join(text1)
41
+ return f"<thinking>{text1}</thinking>\n\n{text2}"
42
+
43
+
44
+ async def on_response(response):
45
+ if response.url.startswith(_PAGE1_):
46
+ # print("on_response", response.url)
47
+ text = await response.text()
48
+ G.set_text(read_event_stream(text))
49
+
50
+
51
+ async def on_route(route):
52
+ # print("on_route", route.request.url)
53
+ if route.request.url.startswith(_PAGE1_):
54
+ # TODO 这里会导致数据全部加载,逻辑变了,所以界面可能混乱
55
+ response = await route.fetch(timeout=mcp_query_table.TIMEOUT)
56
+ await route.fulfill(
57
+ # 强行加utf-8,否则编码搞不定
58
+ content_type="text/event-stream; charset=utf-8",
59
+ response=response,
60
+ )
61
+ else:
62
+ await route.continue_()
63
+
64
+
65
+ async def chat(page: Page,
66
+ prompt: str,
67
+ create: bool,
68
+ ) -> str:
69
+ if not page.url.startswith(_PAGE0_):
70
+ create = True
71
+
72
+ if create:
73
+ await page.goto(_PAGE0_)
74
+
75
+ await page.route(f"{_PAGE1_}/*", on_route)
76
+ async with page.expect_response(f"{_PAGE1_}/*", timeout=mcp_query_table.TIMEOUT) as response_info:
77
+ textbox = page.locator(".ql-editor")
78
+ await textbox.fill(prompt)
79
+ await textbox.press("Enter")
80
+ await on_response(await response_info.value)
81
+
82
+ return G.get_text()
@@ -4,18 +4,22 @@ from loguru import logger
4
4
  from mcp.server.fastmcp import FastMCP
5
5
  from pydantic import Field
6
6
 
7
- from mcp_query_table import QueryType, Site, query as query_table_query
7
+ from mcp_query_table import QueryType, Site, query as qt_query, chat as qt_chat
8
+ from mcp_query_table.enums import Provider
8
9
  from mcp_query_table.tool import BrowserManager
9
10
 
10
11
 
11
12
  class QueryServer:
12
- def __init__(self, format: str = 'markdown', port: int = 9222, browser_path: Optional[str] = None) -> None:
13
+ def __init__(self,
14
+ format: str = 'markdown',
15
+ cdp_endpoint: Optional[str] = 'http://127.0.0.1:9222',
16
+ executable_path: Optional[str] = None) -> None:
13
17
  self.format: str = format
14
- self.browser = BrowserManager(port=port, browser_path=browser_path, debug=False)
18
+ self.browser = BrowserManager(cdp_endpoint=cdp_endpoint, executable_path=executable_path, debug=False)
15
19
 
16
20
  async def query(self, query_input: str, query_type: QueryType, max_page: int, site: Site):
17
21
  page = await self.browser.get_page()
18
- df = await query_table_query(page, query_input, query_type, max_page, site)
22
+ df = await qt_query(page, query_input, query_type, max_page, site)
19
23
  self.browser.release_page(page)
20
24
 
21
25
  if self.format == 'csv':
@@ -25,6 +29,12 @@ class QueryServer:
25
29
  if self.format == 'json':
26
30
  return df.to_json(force_ascii=False, indent=2)
27
31
 
32
+ async def chat(self, prompt: str, create: bool, provider: Provider):
33
+ page = await self.browser.get_page()
34
+ txt = await qt_chat(page, prompt, create, provider)
35
+ self.browser.release_page(page)
36
+ return txt
37
+
28
38
 
29
39
  # !!!log_level这一句非常重要,否则Cline/MCP Server/Tools工作不正常
30
40
  mcp = FastMCP("query_table_mcp", log_level="ERROR")
@@ -43,14 +53,25 @@ async def query(
43
53
  return await qsv.query(query_input, query_type, max_page, site)
44
54
 
45
55
 
46
- def serve(format, cdp_port, browser_path, transport, mcp_host, mcp_port):
56
+ @mcp.tool(description="大语言模型对话")
57
+ async def chat(
58
+ prompt: Annotated[str, Field(description="提示词。如:`9.9大还是9.11大?`")],
59
+ create: Annotated[bool, Field(default=False, description="是否创建新对话")],
60
+ provider: Annotated[
61
+ Provider, Field(default=Provider.Nami, description="提供商。支持`纳米搜索`、`腾讯元宝`、`百度AI搜索`")]
62
+ ) -> str:
63
+ return await qsv.chat(prompt, create, provider)
64
+
65
+
66
+ def serve(format, cdp_endpoint, executable_path, transport, host, port):
47
67
  qsv.format = format
48
- qsv.port = cdp_port
49
- qsv.browser_path = browser_path
50
- logger.info("serve:{},{},{},{}", qsv.format, qsv.port, qsv.browser_path, transport)
68
+ qsv.cdp_endpoint = cdp_endpoint
69
+ qsv.executable_path = executable_path
70
+ logger.info(f"{format=},{transport=}")
71
+ logger.info(f"{cdp_endpoint=},{executable_path=}")
51
72
  if transport == 'sse':
52
- logger.info("mcp:{},{}:{}", transport, mcp_host, mcp_port)
73
+ logger.info(f"{host=},{port=}", transport, host, port)
53
74
 
54
- mcp.settings.host = mcp_host
55
- mcp.settings.port = mcp_port
75
+ mcp.settings.host = host
76
+ mcp.settings.port = port
56
77
  mcp.run(transport=transport)
@@ -0,0 +1,263 @@
1
+ import subprocess
2
+ import sys
3
+ import time
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ import pandas as pd
8
+ from loguru import logger
9
+ from playwright.async_api import async_playwright, Playwright, Page
10
+
11
+ from mcp_query_table.enums import QueryType, Site, Provider
12
+
13
+
14
+ def create_detached_process(command):
15
+ # 设置通用参数
16
+ kwargs = {}
17
+
18
+ if sys.platform == 'win32':
19
+ kwargs.update({
20
+ # 在PyCharm中运行还是会出现新建进程被关闭
21
+ 'creationflags': subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
22
+ })
23
+ else:
24
+ # Unix-like 系统(Linux, macOS)特定设置
25
+ kwargs.update({
26
+ 'start_new_session': True # 创建新的会话
27
+ })
28
+ return subprocess.Popen(command, **kwargs)
29
+
30
+
31
+ def is_local_url(url: str) -> bool:
32
+ """判断url是否是本地地址"""
33
+ for local in ('localhost', '127.0.0.1'):
34
+ if local in url.lower():
35
+ return True
36
+ return False
37
+
38
+
39
+ def get_executable_path(executable_path) -> Optional[str]:
40
+ """获取浏览器可执行文件路径"""
41
+ browsers = {
42
+ "default": executable_path,
43
+ "chrome.exe": r"C:\Program Files\Google\Chrome\Application\chrome.exe",
44
+ "msedge.exe": r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
45
+ }
46
+ for k, v in browsers.items():
47
+ if v is None:
48
+ continue
49
+ if Path(v).exists():
50
+ return v
51
+ return None
52
+
53
+
54
+ class BrowserManager:
55
+ async def __aenter__(self):
56
+ return self
57
+
58
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
59
+ await self.cleanup()
60
+
61
+ def __init__(self,
62
+ cdp_endpoint: Optional[str] = None,
63
+ executable_path: Optional[str] = None,
64
+ debug: bool = False):
65
+ """
66
+
67
+ Parameters
68
+ ----------
69
+ cdp_endpoint:str
70
+ 浏览器CDP地址
71
+ executable_path:str
72
+ 浏览器可执行文件路径。推荐使用chrome,因为Microsoft Edge必须在任务管理器中完全退出才能启动调试端口
73
+ debug:bool
74
+ 是否显示开发者工具
75
+
76
+ """
77
+ self.cdp_endpoint = cdp_endpoint or 'http://127.0.0.1:9222'
78
+ self.executable_path = executable_path
79
+ self.debug = debug
80
+
81
+ self.playwright: Optional[Playwright] = None
82
+ self.browser = None
83
+ self.context = None
84
+ # 空闲page池
85
+ self.pages = []
86
+
87
+ async def cleanup(self):
88
+ if self.browser:
89
+ await self.browser.close()
90
+ if self.playwright:
91
+ await self.playwright.stop()
92
+
93
+ async def _connect_to_local(self) -> None:
94
+ """连接本地浏览器"""
95
+ port = self.cdp_endpoint.split(':')[-1]
96
+ executable_path = get_executable_path(self.executable_path)
97
+ command = [executable_path, f'--remote-debugging-port={port}', '--start-maximized']
98
+ if self.debug:
99
+ command.append('--auto-open-devtools-for-tabs')
100
+
101
+ for i in range(2):
102
+ try:
103
+ self.browser = await self.playwright.chromium.connect_over_cdp(self.cdp_endpoint,
104
+ timeout=10000, slow_mo=1000)
105
+ break
106
+ except:
107
+ if i == 0:
108
+ logger.info(f"start browser:{command}")
109
+ create_detached_process(command)
110
+ time.sleep(3)
111
+ continue
112
+ if i == 1:
113
+ raise ConnectionError(
114
+ f"已提前打开了浏览器,但未开启远程调试端口?请关闭浏览器全部进程后重试 `taskkill /f /im {Path(executable_path).name}`")
115
+
116
+ async def _connect_to_remote(self) -> None:
117
+ """连接远程浏览器"""
118
+ try:
119
+ self.browser = await self.playwright.chromium.connect_over_cdp(self.cdp_endpoint,
120
+ timeout=10000, slow_mo=1000)
121
+ except:
122
+ raise ConnectionError(f"连接远程浏览器失败,请检查CDP地址和端口是否正确。{self.cdp_endpoint}")
123
+
124
+ async def _launch(self) -> None:
125
+ """启动浏览器,并连接CDP协议
126
+
127
+ References
128
+ ----------
129
+ https://blog.csdn.net/qq_30576521/article/details/142370538
130
+
131
+ """
132
+ self.playwright = await async_playwright().start()
133
+
134
+ if is_local_url(self.cdp_endpoint):
135
+ await self._connect_to_local()
136
+ else:
137
+ await self._connect_to_remote()
138
+
139
+ self.context = self.browser.contexts[0]
140
+ # 复用打开的page
141
+ for page in self.context.pages:
142
+ # 防止开发者工具被使用
143
+ if page.url.startswith("devtools://"):
144
+ continue
145
+ self.pages.append(page)
146
+
147
+ async def _try_launch(self) -> None:
148
+ if self.browser is None:
149
+ await self._launch()
150
+ if not self.browser.is_connected():
151
+ await self._launch()
152
+
153
+ async def get_page(self) -> Page:
154
+ """获取可用Page。无空闲标签时会打开新标签"""
155
+ await self._try_launch()
156
+
157
+ # 反复取第一个tab
158
+ while len(self.pages) > 0:
159
+ page = self.pages.pop()
160
+ if page.is_closed():
161
+ continue
162
+ return page
163
+
164
+ # 不够,新建一个
165
+ return await self.context.new_page()
166
+
167
+ def release_page(self, page) -> None:
168
+ """用完的Page释放到池中。如果用完不放回,get_page会一直打开新标签"""
169
+ if page.is_closed():
170
+ return
171
+ # 放回
172
+ self.pages.append(page)
173
+
174
+
175
+ class GlobalVars:
176
+ """全局变量"""
177
+
178
+ def __init__(self):
179
+ self.text = ""
180
+
181
+ def set_text(self, text):
182
+ self.text = text
183
+
184
+ def get_text(self):
185
+ return self.text
186
+
187
+
188
+ async def query(
189
+ page: Page,
190
+ query_input: str = "收盘价>100元",
191
+ query_type: QueryType = QueryType.CNStock,
192
+ max_page: int = 5,
193
+ site: Site = Site.THS) -> pd.DataFrame:
194
+ """查询表格
195
+
196
+ Parameters
197
+ ----------
198
+ page : playwright.sync_api.Page
199
+ 页面
200
+ query_input : str, optional
201
+ 查询条件, by default "收盘价>100元"
202
+ query_type : QueryType, optional
203
+ 查询类型, by default QueryType.astock
204
+ max_page : int, optional
205
+ 最大页数, by default 5
206
+ site : Site, optional
207
+ 站点, by default Site.iwencai
208
+
209
+ Returns
210
+ -------
211
+ pd.DataFrame
212
+ 查询结果
213
+
214
+ """
215
+
216
+ if site == Site.EastMoney:
217
+ from mcp_query_table.sites.eastmoney import query
218
+ return await query(page, query_input, query_type, max_page)
219
+ if site == Site.THS:
220
+ from mcp_query_table.sites.iwencai import query
221
+ return await query(page, query_input, query_type, max_page)
222
+ if site == Site.TDX:
223
+ from mcp_query_table.sites.tdx import query
224
+ return await query(page, query_input, query_type, max_page)
225
+
226
+ raise ValueError(f"未支持的站点:{site}")
227
+
228
+
229
+ async def chat(
230
+ page: Page,
231
+ prompt: str = "9.9大还是9.11大?",
232
+ create: bool = False,
233
+ provider: Provider = Provider.Nami) -> str:
234
+ """大语言对话
235
+
236
+ Parameters
237
+ ----------
238
+ page : playwright.sync_api.Page
239
+ 页面
240
+ prompt : str, optional
241
+ 对话内容, by default "9.9大还是9.11大?"
242
+ create : bool, optional
243
+ 是否创建新对话, by default False
244
+ provider : Provider, optional
245
+ 提供商, by default Provider.N
246
+
247
+ Returns
248
+ -------
249
+ str
250
+ 对话结果
251
+
252
+ """
253
+ if provider == Provider.Nami:
254
+ from mcp_query_table.providers.n import chat
255
+ return await chat(page, prompt, create)
256
+ if provider == Provider.YuanBao:
257
+ from mcp_query_table.providers.yuanbao import chat
258
+ return await chat(page, prompt, create)
259
+ if provider == Provider.BaiDu:
260
+ from mcp_query_table.providers.baidu import chat
261
+ return await chat(page, prompt, create)
262
+
263
+ raise ValueError(f"未支持的提供商:{provider}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp_query_table
3
- Version: 0.2.6
3
+ Version: 0.3.2
4
4
  Summary: query table from website, support MCP
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: MIT License
@@ -39,13 +39,20 @@ Dynamic: license-file
39
39
 
40
40
  # mcp_query_table
41
41
 
42
- 基于`playwright`实现的财经网页表格爬虫,支持`Model Context Protocol (MCP) `。目前可查询来源为
42
+ 1. 基于`playwright`实现的财经网页表格爬虫,支持`Model Context Protocol (MCP) `。目前可查询来源为
43
43
 
44
- - [同花顺i问财](http://iwencai.com/)
45
- - [通达信问小达](https://wenda.tdx.com.cn/)
46
- - [东方财富条件选股](https://xuangu.eastmoney.com/)
44
+ - [同花顺i问财](http://iwencai.com/)
45
+ - [通达信问小达](https://wenda.tdx.com.cn/)
46
+ - [东方财富条件选股](https://xuangu.eastmoney.com/)
47
47
 
48
- 实盘时,如果某网站宕机或改版,可以立即切换到其他网站。(注意:不同网站的表格结构不同,需要提前做适配)
48
+ 实盘时,如果某网站宕机或改版,可以立即切换到其他网站。(注意:不同网站的表格结构不同,需要提前做适配)
49
+
50
+ 2. 基于`playwright`实现的大语言模型调用爬虫。目前可用来源为
51
+ - [纳米搜索](https://www.n.cn/)
52
+ - [腾讯元宝](https://yuanbao.tencent.com/)
53
+ - [百度AI搜索](https://chat.baidu.com/)
54
+
55
+ `RooCode`提供了`Human Reply`功能。但发现`纳米搜索`网页版复制时格式破坏,所以研发了此功能
49
56
 
50
57
  ## 安装
51
58
 
@@ -63,7 +70,7 @@ from mcp_query_table import *
63
70
 
64
71
 
65
72
  async def main() -> None:
66
- async with BrowserManager(port=9222, browser_path=None, debug=True) as bm:
73
+ async with BrowserManager(cdp_endpoint="http://127.0.0.1:9222", executable_path=None, debug=True) as bm:
67
74
  # 问财需要保证浏览器宽度>768,防止界面变成适应手机
68
75
  page = await bm.get_page()
69
76
  df = await query(page, '收益最好的200只ETF', query_type=QueryType.ETF, max_page=1, site=Site.THS)
@@ -75,8 +82,14 @@ async def main() -> None:
75
82
  # TODO 东财翻页要提前登录
76
83
  df = await query(page, '今日涨幅前5的概念板块;', query_type=QueryType.Board, max_page=3, site=Site.EastMoney)
77
84
  print(df)
78
- bm.release_page(page)
85
+
86
+ output = await chat(page, "1+2等于多少?", provider=Provider.YuanBao)
87
+ print(output)
88
+ output = await chat(page, "3+4等于多少?", provider=Provider.YuanBao, create=True)
89
+ print(output)
90
+
79
91
  print('done')
92
+ bm.release_page(page)
80
93
  await page.wait_for_timeout(2000)
81
94
 
82
95
 
@@ -119,7 +132,7 @@ if __name__ == '__main__':
119
132
 
120
133
  确保可以在控制台中执行`python -m mcp_query_table -h`。如果不能,可能要先`pip install mcp_query_table`
121
134
 
122
- 在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`browser_path`是`Chrome`的绝对路径。
135
+ 在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`executable_path`是`Chrome`的绝对路径。
123
136
 
124
137
  ### STDIO方式
125
138
 
@@ -133,7 +146,9 @@ if __name__ == '__main__':
133
146
  "mcp_query_table",
134
147
  "--format",
135
148
  "markdown",
136
- "--browser_path",
149
+ "--cdp_endpoint",
150
+ "http://127.0.0.1:9222",
151
+ "--executable_path",
137
152
  "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
138
153
  ]
139
154
  }
@@ -146,11 +161,20 @@ if __name__ == '__main__':
146
161
  先在控制台中执行如下命令,启动`MCP`服务
147
162
 
148
163
  ```commandline
149
- python -m mcp_query_table --format markdown --browser_path "C:\Program Files\Google\Chrome\Application\chrome.exe" --transport sse --mcp_port 8000
164
+ python -m mcp_query_table --format markdown --transport sse --port 8000
150
165
  ```
151
166
 
152
167
  然后就可以连接到`MCP`服务了
153
- http://localhost:8000/sse
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "mcp_query_table": {
173
+ "url": "http://127.0.0.1:8000/sse"
174
+ }
175
+ }
176
+ }
177
+ ```
154
178
 
155
179
  ## 使用`MCP Inspector`进行调试
156
180
 
@@ -158,7 +182,8 @@ http://localhost:8000/sse
158
182
  npx @modelcontextprotocol/inspector python -m mcp_query_table --format markdown
159
183
  ```
160
184
 
161
- 打开浏览器并翻页是一个比较耗时的操作,会导致`MCP Inspector`页面超时,可以`http://localhost:5173/?timeout=60000` 表示超时时间为60秒
185
+ 打开浏览器并翻页是一个比较耗时的操作,会导致`MCP Inspector`页面超时,可以`http://localhost:5173/?timeout=600000`
186
+ 表示超时时间为600秒
162
187
 
163
188
  第一次尝试编写`MCP`项目,可能会有各种问题,欢迎大家交流。
164
189
 
@@ -12,6 +12,10 @@ mcp_query_table.egg-info/SOURCES.txt
12
12
  mcp_query_table.egg-info/dependency_links.txt
13
13
  mcp_query_table.egg-info/requires.txt
14
14
  mcp_query_table.egg-info/top_level.txt
15
+ mcp_query_table/providers/__init__.py
16
+ mcp_query_table/providers/baidu.py
17
+ mcp_query_table/providers/n.py
18
+ mcp_query_table/providers/yuanbao.py
15
19
  mcp_query_table/sites/__init__.py
16
20
  mcp_query_table/sites/eastmoney.py
17
21
  mcp_query_table/sites/iwencai.py
@@ -29,6 +29,7 @@ dynamic = ["version"]
29
29
  packages = [
30
30
  "mcp_query_table",
31
31
  "mcp_query_table.sites",
32
+ "mcp_query_table.providers",
32
33
  ]
33
34
 
34
35
  [tool.setuptools.dynamic]
@@ -1,4 +0,0 @@
1
- from ._version import __version__
2
-
3
- from .enums import QueryType, Site
4
- from .tool import BrowserManager, query
@@ -1 +0,0 @@
1
- __version__ = "0.2.6"
@@ -1,165 +0,0 @@
1
- import subprocess
2
- import time
3
- from pathlib import Path
4
- from typing import Optional
5
-
6
- import pandas as pd
7
- from loguru import logger
8
- from playwright.async_api import async_playwright, Playwright, Page
9
-
10
- from mcp_query_table.enums import QueryType, Site
11
-
12
-
13
- class BrowserManager:
14
- async def __aenter__(self):
15
- return self
16
-
17
- async def __aexit__(self, exc_type, exc_val, exc_tb):
18
- await self.cleanup()
19
-
20
- def __init__(self, port: int = 9222, browser_path: Optional[str] = None, debug: bool = False):
21
- """
22
-
23
- Parameters
24
- ----------
25
- port:int
26
- 浏览器调试端口
27
- browser_path
28
- 浏览器可执行路径。推荐使用chrome,因为Microsoft Edge必须在任务管理器中完全退出才能启动调试端口
29
- debug:bool
30
- 是否显示开发者工具
31
-
32
- """
33
- if browser_path is None:
34
- browser_path = r'C:\Program Files\Google\Chrome\Application\chrome.exe'
35
- if not Path(browser_path).exists():
36
- # Microsoft Edge必须在任务管理器中完全退出才能启动调试端口
37
- browser_path = r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
38
- if not Path(browser_path).exists():
39
- raise ValueError("未找到浏览器可执行文件")
40
-
41
- self.port = port
42
- self.browser_path = browser_path
43
- self.debug = debug
44
-
45
- self.playwright: Optional[Playwright] = None
46
- self.browser = None
47
- self.context = None
48
- # 空闲page池
49
- self.pages = []
50
-
51
- async def cleanup(self):
52
- if self.browser:
53
- await self.browser.close()
54
- if self.playwright:
55
- await self.playwright.stop()
56
-
57
- async def _launch(self) -> None:
58
- """启动浏览器,并连接CDP协议
59
-
60
- References
61
- ----------
62
- https://blog.csdn.net/qq_30576521/article/details/142370538
63
-
64
- """
65
- self.playwright = await async_playwright().start()
66
-
67
- try:
68
- # 尝试连接已打开的浏览器
69
- self.browser = await self.playwright.chromium.connect_over_cdp(f"http://127.0.0.1:{self.port}",
70
- slow_mo=1000,
71
- timeout=5000)
72
- except:
73
-
74
- # 执行完成后不会关闭浏览器
75
- if self.debug:
76
- command = f'"{self.browser_path}" --remote-debugging-port={self.port} --start-maximized --auto-open-devtools-for-tabs'
77
- else:
78
- command = f'"{self.browser_path}" --remote-debugging-port={self.port} --start-maximized'
79
- logger.info(f"start browser:{command}")
80
- subprocess.Popen(command, shell=True)
81
- time.sleep(3)
82
-
83
- try:
84
- self.browser = await self.playwright.chromium.connect_over_cdp(f"http://127.0.0.1:{self.port}",
85
- slow_mo=1000,
86
- timeout=5000)
87
- except:
88
- logger.warning("是否提前打开了浏览器,但未开启远程调试端口?请关闭浏览器全部进程后重试")
89
- raise
90
-
91
- self.context = self.browser.contexts[0]
92
- # 复用打开的page
93
- for page in self.context.pages:
94
- # 防止开发者工具被使用
95
- if page.url.startswith("devtools://"):
96
- continue
97
- self.pages.append(page)
98
-
99
- async def _try_launch(self) -> None:
100
- if self.browser is None:
101
- await self._launch()
102
- if not self.browser.is_connected():
103
- await self._launch()
104
-
105
- async def get_page(self) -> Page:
106
- """获取可用Page。无空闲标签时会打开新标签"""
107
- await self._try_launch()
108
-
109
- # 反复取第一个tab
110
- while len(self.pages) > 0:
111
- page = self.pages.pop()
112
- if page.is_closed():
113
- continue
114
- return page
115
-
116
- # 不够,新建一个
117
- return await self.context.new_page()
118
-
119
- def release_page(self, page) -> None:
120
- """用完的Page释放到池中。如果用完不放回,get_page会一直打开新标签"""
121
- if page.is_closed():
122
- return
123
- # 放回
124
- self.pages.append(page)
125
-
126
-
127
- async def query(
128
- page: Page,
129
- query_input: str = "收盘价>100元",
130
- query_type: QueryType = QueryType.CNStock,
131
- max_page: int = 5,
132
- site: Site = Site.THS) -> pd.DataFrame:
133
- """查询表格
134
-
135
- Parameters
136
- ----------
137
- page : playwright.sync_api.Page
138
- 页面
139
- query_input : str, optional
140
- 查询条件, by default "收盘价>100元"
141
- query_type : QueryType, optional
142
- 查询类型, by default QueryType.astock
143
- max_page : int, optional
144
- 最大页数, by default 5
145
- site : Site, optional
146
- 站点, by default Site.iwencai
147
-
148
- Returns
149
- -------
150
- pd.DataFrame
151
- 查询结果
152
-
153
- """
154
-
155
- if site == Site.EastMoney:
156
- from mcp_query_table.sites.eastmoney import query
157
- return await query(page, query_input, query_type, max_page)
158
- if site == Site.THS:
159
- from mcp_query_table.sites.iwencai import query
160
- return await query(page, query_input, query_type, max_page)
161
- if site == Site.TDX:
162
- from mcp_query_table.sites.tdx import query
163
- return await query(page, query_input, query_type, max_page)
164
-
165
- raise ValueError(f"未支持的站点:{site}")
File without changes