一、原理分析:为何选择移动端API?
与Web端相比,移动端(APP)的API通常设计得更加简洁、清晰和稳定。出于性能和省流量的考虑,移动端API通常以结构化数据(如JSON)的形式返回数据,而不是大量的HTML代码。这正好为我们数据提取提供了极大的便利。
我们的核心思路是:
- 抓包分析:使用抓包工具捕获问财网APP(或其官方网站发出的)的HTTP请求。
- 识别接口:从众多请求中筛选出负责核心数据查询的API。
- 参数解析:分析该API请求的URL、参数(Params)、请求头(Headers)的含义,找出哪些是固定值,哪些是可变值。
- 模拟请求:使用Python的
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">requests</font>库,完全模拟一个合法的客户端请求,直接获取JSON数据。
这种方法避免了解析HTML的麻烦,也无需处理复杂的JavaScript逻辑和Cookie会话维持,只要一次分析成功,便可长期稳定使用。
二、实战步骤:逆向分析与接口调用
步骤一:抓包与接口识别
首先,你需要一个抓包工具。Fiddler、Charles 或 Burp Suite 都是优秀的选择。本文以Charles为例。
- 在你的电脑上设置好抓包代理。
- 让你的手机和电脑处于同一局域网,并在手机上设置代理指向你的电脑。
- 打开问财APP,输入一个查询问题,例如“主板上市的科技股”。
- 观察Charles中捕获的HTTP请求流。
你会发现一个非常“显眼”的请求,其URL可能包含<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">search</font>、<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">stock_list</font>等关键字,并且Response是清晰的JSON格式。经过分析,问财的核心查询接口通常类似于:\ <font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">https://www.iwencai.com/unifiedwap/unified-wap/v2/result/get-robot-data</font>
步骤二:关键参数分析
这是最关键的一步。我们需要模拟一个合法的请求,就必须理解每个参数的意义。通过对比多次不同查询的请求,我们可以分析出以下核心参数:
**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">question</font>**: 这是最重要的参数,就是你想要查询的自然语言问题,例如“主板上市的科技股”。**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">perpage</font>**: 每页返回的数据条数,可以设置得大一些以一次性获取所有数据。**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">page</font>**: 页码,通常配合<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">perpage</font>使用进行分页。**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">source</font>**: 数据来源,通常是<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">Ths_iwencai_Xuangu</font>(同花顺问财选股)。**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">secondary_intent</font>**: 二级意图,股票相关查询通常为<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">stock</font>。**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">log_info</font>**: 一个包含查询上下文信息的JSON字符串,通常可以固定不变或微调。**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">rsh</font>**: 一个看起来像MD5哈希值的参数,用于校验。这是难点所在。它很可能由其他参数(如<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">question</font>)和一个固定的密钥(Salt)通过某种算法生成。需要逆向APP的代码才能找到算法。但幸运的是,有时这个参数在Web端是固定的或可预测的。
一个重要发现:在问财网的网页版中,通过浏览器开发者工具(F12-Network)进行分析,有时可以找到无需动态计算<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">rsh</font>参数的请求方式。请求头中的<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">Hexin-V</font>(同花顺的校验Token)可能是另一个难点,但同样存在一些方法可以绕过或生成。
经过社区多次实践,发现问财网的接口校验有时并不严格。直接使用Web端捕获的固定参数,往往也能成功请求。这意味着我们可以“借用”一套有效的参数,只修改<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">question</font>和其他必要字段。
步骤三:Python代码实现
基于以上分析,我们可以构造HTTP请求。核心是构造一个看起来完全来自官方客户端(无论是APP还是浏览器)的请求。
以下是一个成功的Python示例代码。请注意,其中的**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">hexin-v</font>**和**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">rsh</font>**值可能会在一段时间后失效,如果失效,你需要按照步骤一和二的方法,从你自己的浏览器中捕获最新的有效值。
import requests
import json
import pandas as pd
# 代理配置信息
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"
# 构造代理字典
proxies = {
"http": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",
"https": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"
}
def fetch_iwencai_data(question):
"""
直接调用问财网API接口获取数据
:param question: 要查询的问财问题
:return: 包含查询结果的Pandas DataFrame
"""
# 1. 目标API URL
url = "https://www.iwencai.com/unifiedwap/unified-wap/v2/result/get-robot-data"
# 2. 构造请求头 (Headers)
# 注意:这里的Hexin-V值非常重要且会过期,需要你自己从浏览器请求中复制替换!
headers = {
'Host': 'www.iwencai.com',
'Connection': 'keep-alive',
'Content-Length': '0',
'Accept': 'application/json, text/plain, */*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'Hexin-V': 'AwhUqB9fA7uJis6wUTQ9f6KqU9K5wK5k9pK5iE2kCjqJqJp9kC2iE5iE5pJ', # <- 替换为你自己的有效Token!
'Content-Type': 'application/json',
'Origin': 'https://www.iwencai.com',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://www.iwencai.com/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
# 3. 构造查询参数 (Query Parameters)
params = {
'question': question, # 核心:你的问题
'perpage': '500', # 一次获取500条,可根据需要调整
'page': '1',
'secondary_intent': 'stock',
'source': 'Ths_iwencai_Xuangu',
'version': '2.0',
'query_area': '',
'block_list': '',
'add_info': '',
'rsh': 'Ths_iwencai_Xuangu_7cprh7qj700q7qj7', # <- 这个值也可能需要替换!
# 注意:log_info通常是一个JSON字符串,这里为了简洁已简化。
# 在实际复杂场景中,可能需要根据问题动态生成或捕获一个固定的有效值。
'log_info': '{"input_type":"typewrite"}',
}
try:
# 4. 发送GET请求(添加proxies参数)
# 注意:这里使用的是params参数,requests库会将其自动拼接为URL后的查询字符串
response = requests.get(url, headers=headers, params=params, proxies=proxies, timeout=30)
# 5. 检查请求是否成功
if response.status_code == 200:
print("请求成功!")
data_json = response.json() # 解析JSON响应
# 6. 提取核心数据
# JSON结构需要具体分析,通常数据在 data -> answer -> components -> [0] -> data -> meta -> extra -> pick
try:
# 这个路径需要根据实际的JSON返回结构进行调整!
stock_list = data_json['data']['answer']['components'][0]['data']['meta']['extra']['pick']
# 将数据转换为Pandas DataFrame
df = pd.DataFrame(stock_list)
return df
except KeyError as e:
print(f"解析JSON数据时出错,键错误: {e}")
print("完整的响应JSON:", json.dumps(data_json, indent=2, ensure_ascii=False))
return None
else:
print(f"请求失败,状态码: {response.status_code}")
print(response.text)
return None
except requests.exceptions.ProxyError as e:
print(f"代理连接错误: {e}")
return None
except requests.exceptions.ConnectTimeout as e:
print(f"连接超时: {e}")
return None
except requests.exceptions.RequestException as e:
print(f"网络请求异常: {e}")
return None
# 使用示例
if __name__ == '__main__':
search_question = "主板上市的科技股,2023年净利润增长大于20%" # 你的问财问题
result_df = fetch_iwencai_data(search_question)
if result_df is not None:
print(f"共获取到 {len(result_df)} 条数据")
# 打印前几行
print(result_df.head())
# 保存到CSV文件
result_df.to_csv('iwencai_stock_data.csv', index=False, encoding='utf-8-sig')
print("数据已保存到 iwencai_stock_data.csv")
else:
print("未获取到数据")
三、注意事项与最佳实践
- 参数失效:代码中的
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">Hexin-V</font>和<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">rsh</font>值是最可能失效的部分。你需要使用浏览器开发者工具,访问问财官网(www.iwencai.com),进行一次查询,在“网络(Network)”面板中找到<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">get-robot-data</font>请求,从中复制最新的<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">Hexin-V</font>请求头和<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">rsh</font>参数值替换到代码中。 - 频率限制:即使直接调用API,也应保持合理的请求频率,避免过于频繁的请求导致IP被暂时封锁。建议在循环中添加
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">time.sleep()</font>进行延时。 - 错误处理:代码中只做了基本的错误处理。在生产环境中,应增加更完善的异常捕获、重试机制等。
- 数据结构变化:问财可能会调整API返回的JSON数据结构。如果解析失败(出现
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">KeyError</font>),应重新分析JSON结构,并调整数据提取的代码路径。 - 合法合规:请将获取的数据用于个人学习和研究,遵守问财网的用户协议,尊重数据的版权和知识产权。