mcp-query-table 0.3.8__tar.gz → 0.3.10__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 (21) hide show
  1. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/PKG-INFO +2 -3
  2. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/__main__.py +1 -1
  3. mcp_query_table-0.3.10/mcp_query_table/_version.py +1 -0
  4. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/server.py +2 -1
  5. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/sites/eastmoney.py +8 -4
  6. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/sites/iwencai.py +8 -8
  7. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/sites/tdx.py +4 -3
  8. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/tool.py +13 -5
  9. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/utils.py +0 -17
  10. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/pyproject.toml +1 -2
  11. mcp_query_table-0.3.8/mcp_query_table/_version.py +0 -1
  12. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/.gitignore +0 -0
  13. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/LICENSE +0 -0
  14. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/README.md +0 -0
  15. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/__init__.py +0 -0
  16. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/enums.py +0 -0
  17. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/providers/__init__.py +0 -0
  18. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/providers/baidu.py +0 -0
  19. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/providers/n.py +0 -0
  20. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/providers/yuanbao.py +0 -0
  21. {mcp_query_table-0.3.8 → mcp_query_table-0.3.10}/mcp_query_table/sites/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp_query_table
3
- Version: 0.3.8
3
+ Version: 0.3.10
4
4
  Summary: query table from website, support MCP
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: MIT License
@@ -33,8 +33,7 @@ Requires-Dist: loguru
33
33
  Requires-Dist: mcp
34
34
  Requires-Dist: pandas
35
35
  Requires-Dist: playwright
36
- Requires-Dist: playwright-stealth
37
- Requires-Dist: setuptools
36
+ Requires-Dist: playwright-stealth>=2.0.0
38
37
  Requires-Dist: tabulate
39
38
  Description-Content-Type: text/markdown
40
39
 
@@ -17,7 +17,7 @@ def main():
17
17
  parser.add_argument("--executable_path", type=str, help="浏览器路径",
18
18
  nargs="?", default=r'C:\Program Files\Google\Chrome\Application\chrome.exe')
19
19
  parser.add_argument("--user_data_dir", type=str, help="浏览器用户数据目录",
20
- nargs="?", default=rf'C:\Users\{getpass.getuser()}\AppData\Local\Google\Chrome\User Data')
20
+ nargs="?", default=rf'C:\Users\{getpass.getuser()}\AppData\Local\Google\Chrome\User Data\Default')
21
21
  parser.add_argument("--transport", type=str, help="传输类型",
22
22
  default='stdio', choices=['stdio', 'sse'])
23
23
  parser.add_argument("--host", type=str, help="MCP服务端绑定地址",
@@ -0,0 +1 @@
1
+ __version__ = "0.3.10"
@@ -53,9 +53,10 @@ async def query(
53
53
  query_type: Annotated[QueryType, Field(default=QueryType.CNStock,
54
54
  description="查询类型。支持`A股`、`指数`、`基金`、`港股`、`美股`等")],
55
55
  max_page: Annotated[int, Field(default=1, ge=1, le=10, description="最大页数。只查第一页即可")],
56
+ rename: Annotated[bool, Field(default=False, description="是否重命名列名")],
56
57
  site: Annotated[Site, Field(default=Site.THS, description="站点。支持`东方财富`、`通达信`、`同花顺`")]
57
58
  ) -> str:
58
- return await qsv.query(query_input, query_type, max_page, site)
59
+ return await qsv.query(query_input, query_type, max_page, rename, site)
59
60
 
60
61
 
61
62
  # chat功能不通过mcp暴露,因为在Cline等客户端中本就有LLM功能,反而导致返回的数据没有正确提交
@@ -81,7 +81,7 @@ class Pagination:
81
81
  datas.extend(v)
82
82
  return datas
83
83
 
84
- def get_dataframe(self):
84
+ def get_dataframe(self, rename: bool):
85
85
  columns = {x['key']: x['title'] for x in self.columns}
86
86
  dtypes = {x['key']: convert_type(x['dataType']) for x in self.columns}
87
87
 
@@ -98,7 +98,10 @@ class Pagination:
98
98
  except ValueError:
99
99
  logger.info("转换失败 {}:{}", k, v)
100
100
 
101
- return df.rename(columns=columns)
101
+ if rename:
102
+ return df.rename(columns=columns)
103
+ else:
104
+ return df
102
105
 
103
106
 
104
107
  P = Pagination()
@@ -121,7 +124,8 @@ async def on_response(response):
121
124
  async def query(page: Page,
122
125
  q: str = "收盘价>100元",
123
126
  type_: QueryType = 'stock',
124
- max_page: int = 5) -> pd.DataFrame:
127
+ max_page: int = 5,
128
+ rename: bool = True) -> pd.DataFrame:
125
129
  type = _type_.get(type_, None)
126
130
  assert type is not None, f"不支持的类型:{type_}"
127
131
 
@@ -141,4 +145,4 @@ async def query(page: Page,
141
145
  await page.get_by_role("button", name="下一页").click()
142
146
  await on_response(await response_info.value)
143
147
 
144
- return P.get_dataframe()
148
+ return P.get_dataframe(rename)
@@ -10,10 +10,8 @@ import re
10
10
  import pandas as pd
11
11
  from loguru import logger
12
12
  from playwright.async_api import Page
13
- from playwright_stealth import stealth_async
14
13
 
15
14
  from mcp_query_table.enums import QueryType
16
- from mcp_query_table.utils import FixedConfig
17
15
 
18
16
  # 初次查询页面
19
17
  _PAGE1_ = 'https://www.iwencai.com/customized/chart/get-robot-data'
@@ -82,7 +80,7 @@ class Pagination:
82
80
  datas.extend(v)
83
81
  return datas
84
82
 
85
- def get_dataframe(self):
83
+ def get_dataframe(self, rename: bool):
86
84
  columns = {x['key']: x['index_name'] for x in self.columns}
87
85
  dtypes = {x['key']: convert_type(x['type']) for x in self.columns}
88
86
 
@@ -96,7 +94,10 @@ class Pagination:
96
94
  except ValueError:
97
95
  logger.info("转换失败 {}:{}", k, v)
98
96
 
99
- return df.rename(columns=columns)
97
+ if rename:
98
+ return df.rename(columns=columns)
99
+ else:
100
+ return df
100
101
 
101
102
 
102
103
  P = Pagination()
@@ -150,12 +151,11 @@ async def on_response(response):
150
151
  async def query(page: Page,
151
152
  w: str = "收盘价>1000元",
152
153
  type_: QueryType = 'stock',
153
- max_page: int = 5) -> pd.DataFrame:
154
+ max_page: int = 5,
155
+ rename: bool = False) -> pd.DataFrame:
154
156
  querytype = _querytype_.get(type_, None)
155
157
  assert querytype is not None, f"不支持的类型:{type_}"
156
158
 
157
- await stealth_async(page, FixedConfig())
158
-
159
159
  await page.route(re.compile(r'.*\.(?:jpg|jpeg|png|gif|webp)(?:$|\?)'), lambda route: route.abort())
160
160
 
161
161
  P.reset()
@@ -172,4 +172,4 @@ async def query(page: Page,
172
172
  await page.get_by_text("下页").click()
173
173
  await on_response(await response_info.value)
174
174
 
175
- return P.get_dataframe()
175
+ return P.get_dataframe(rename)
@@ -78,7 +78,7 @@ class Pagination:
78
78
  datas.extend(v)
79
79
  return datas
80
80
 
81
- def get_dataframe(self):
81
+ def get_dataframe(self, rename: bool):
82
82
  dtypes = [convert_type(x) for x in self.dtypes]
83
83
  df = pd.DataFrame(self.get_list(), columns=self.columns)
84
84
  for i, v in enumerate(dtypes):
@@ -128,7 +128,8 @@ async def on_response2(response):
128
128
  async def query(page: Page,
129
129
  message: str = "收盘价>100元",
130
130
  type_: QueryType = 'AG',
131
- max_page: int = 5) -> pd.DataFrame:
131
+ max_page: int = 5,
132
+ rename: bool = False) -> pd.DataFrame:
132
133
  queryType = _queryType_.get(type_, None)
133
134
  assert queryType is not None, f"不支持的类型:{type_}"
134
135
 
@@ -147,4 +148,4 @@ async def query(page: Page,
147
148
  await page.get_by_role("button", name="下一页").click()
148
149
  await on_response1(await response_info.value)
149
150
 
150
- return P.get_dataframe()
151
+ return P.get_dataframe(rename)
@@ -4,11 +4,12 @@ import sys
4
4
  import time
5
5
  from pathlib import Path
6
6
  from typing import Optional
7
- from urllib.parse import urlparse
7
+ from urllib.parse import urlparse, quote
8
8
 
9
9
  import pandas as pd
10
10
  from loguru import logger
11
11
  from playwright.async_api import async_playwright, Playwright, Page
12
+ from playwright_stealth import Stealth
12
13
 
13
14
  from mcp_query_table.enums import QueryType, Site, Provider
14
15
 
@@ -204,6 +205,8 @@ class BrowserManager:
204
205
  self.context = await self.browser.new_context()
205
206
  else:
206
207
  self.context = self.browser.contexts[0]
208
+ # 爱问财,无头模式,需要使用 stealth 插件
209
+ await Stealth().apply_stealth_async(self.context)
207
210
 
208
211
  # 复用打开的page
209
212
  for page in self.context.pages:
@@ -246,7 +249,9 @@ async def query(
246
249
  query_input: str = "收盘价>100元",
247
250
  query_type: QueryType = QueryType.CNStock,
248
251
  max_page: int = 5,
249
- site: Site = Site.THS) -> pd.DataFrame:
252
+ rename: bool = False,
253
+ site: Site = Site.THS,
254
+ ) -> pd.DataFrame:
250
255
  """查询表格
251
256
 
252
257
  Parameters
@@ -259,6 +264,8 @@ async def query(
259
264
  查询类型, by default QueryType.astock
260
265
  max_page : int, optional
261
266
  最大页数, by default 5
267
+ rename: bool
268
+ 是否重命名列名, by default False
262
269
  site : Site, optional
263
270
  站点, by default Site.iwencai
264
271
 
@@ -268,16 +275,17 @@ async def query(
268
275
  查询结果
269
276
 
270
277
  """
278
+ query_input = quote(query_input.strip(), safe='')
271
279
 
272
280
  if site == Site.EastMoney:
273
281
  from mcp_query_table.sites.eastmoney import query
274
- return await query(page, query_input, query_type, max_page)
282
+ return await query(page, query_input, query_type, max_page, rename)
275
283
  if site == Site.THS:
276
284
  from mcp_query_table.sites.iwencai import query
277
- return await query(page, query_input, query_type, max_page)
285
+ return await query(page, query_input, query_type, max_page, rename)
278
286
  if site == Site.TDX:
279
287
  from mcp_query_table.sites.tdx import query
280
- return await query(page, query_input, query_type, max_page)
288
+ return await query(page, query_input, query_type, max_page, rename)
281
289
 
282
290
  raise ValueError(f"未支持的站点:{site}")
283
291
 
@@ -1,10 +1,6 @@
1
- import random
2
- import string
3
1
  from pathlib import Path
4
2
  from typing import List, Tuple
5
3
 
6
- from playwright_stealth import StealthConfig
7
-
8
4
 
9
5
  def is_image(path: str) -> bool:
10
6
  """判断是否是图片文件"""
@@ -36,16 +32,3 @@ class GlobalVars:
36
32
 
37
33
  def get_text(self):
38
34
  return self.text
39
-
40
-
41
- # https://github.com/AtuboDad/playwright_stealth/issues/31#issuecomment-2342541305
42
- class FixedConfig(StealthConfig):
43
-
44
- @property
45
- def enabled_scripts(self):
46
- key = "".join(random.choices(string.ascii_letters, k=10))
47
- for script in super().enabled_scripts:
48
- if "const opts" in script:
49
- yield script.replace("const opts", f"window.{key}")
50
- continue
51
- yield script.replace("opts", f"window.{key}")
@@ -16,9 +16,8 @@ dependencies = [
16
16
  "pandas",
17
17
  "loguru",
18
18
  "playwright",
19
- "playwright-stealth",
19
+ "playwright-stealth>=2.0.0", # https://github.com/Mattwmaster58/playwright_stealth
20
20
  "mcp",
21
- "setuptools", # playwright-stealth中要使用pkg_resources,而pkg_resources在setuptools中
22
21
  "tabulate"
23
22
  ]
24
23
  dynamic = ["version"]
@@ -1 +0,0 @@
1
- __version__ = "0.3.8"