import httpx import json from typing import Optional, Dict, Any from app.config import settings from app.crawler.token_manager import TokenManager class HuaweiAPI: def __init__(self): self.base_url = "https://web-drcn.hispace.dbankcloud.com/edge" self.locale = "zh_CN" self.token_manager = TokenManager() self.client = httpx.AsyncClient(timeout=30.0) async def get_app_info(self, pkg_name: Optional[str] = None, app_id: Optional[str] = None) -> Dict[str, Any]: """获取应用基本信息""" if not pkg_name and not app_id: raise ValueError("必须提供 pkg_name 或 app_id") # 获取token tokens = await self.token_manager.get_token() # 构建请求 url = f"{self.base_url}/webedge/appinfo" headers = { "Content-Type": "application/json", "User-Agent": "HuaweiMarketCrawler/1.0", "interface-code": tokens["interface_code"], "identity-id": tokens["identity_id"] } body = {"locale": self.locale} if pkg_name: body["pkgName"] = pkg_name else: body["appId"] = app_id # 发送请求 response = await self.client.post(url, headers=headers, json=body) response.raise_for_status() data = response.json() # 数据清洗 return self._clean_data(data) async def get_app_rating(self, app_id: str) -> Optional[Dict[str, Any]]: """获取应用评分详情""" # 跳过元服务 if app_id.startswith("com.atomicservice"): return None tokens = await self.token_manager.get_token() url = f"{self.base_url}/harmony/page-detail" headers = { "Content-Type": "application/json", "User-Agent": "HuaweiMarketCrawler/1.0", "interface-code": tokens["interface_code"], "identity-id": tokens["identity_id"] } body = { "pageId": f"webAgAppDetail|{app_id}", "pageNum": 1, "pageSize": 100, "zone": "" } try: response = await self.client.post(url, headers=headers, json=body) response.raise_for_status() data = response.json() # 解析评分数据 layouts = data["pages"][0]["data"]["cardlist"]["layoutData"] comment_cards = [l for l in layouts if l.get("type") == "fl.card.comment"] if not comment_cards: return None star_info_str = comment_cards[0]["data"][0]["starInfo"] return json.loads(star_info_str) except Exception as e: print(f"获取评分失败: {e}") return None def _clean_data(self, data: Dict[str, Any]) -> Dict[str, Any]: """清洗数据""" # 移除 \0 字符 for key, value in data.items(): if isinstance(value, str): data[key] = value.replace('\x00', '') # 移除 AG-TraceId data.pop('AG-TraceId', None) # 验证 appId 长度 if len(data.get('appId', '')) < 15: raise ValueError("appId长度小于15,可能是安卓应用") return data async def close(self): """关闭客户端""" await self.client.aclose()