Shopee商品数据API解析与Java实现
1. Shopee API接口概述与合规调用指南Shopee作为东南亚领先的电商平台其公开API为开发者提供了获取商品数据的合法途径。与直接爬取网页数据不同API调用具有明确的权限控制和访问频率限制更符合平台规则。在开始技术实现前有几个关键点需要注意robots协议限制Shopee明确禁止爬虫抓取数据其robots.txt文件显示User-agent: * Disallow: /。这意味着任何绕过API直接抓取页面数据的行为都违反平台规则API调用频率未认证的公开API通常有每分钟60次的请求限制超过可能触发IP封禁数据使用范围获取的数据仅可用于技术研究禁止用于商业爬虫或自动化下单等场景实测发现Shopee的API接口设计相对规范返回数据为结构化JSON格式。以台湾站点为例主要API端点包括分类树获取https://xiapi.xiapibuy.com/api/v4/pages/get_category_tree商品列表获取https://xiapi.xiapibuy.com/api/v4/search/search_items重要提示所有API调用都应当设置合理的间隔时间建议至少1秒/次避免对服务器造成过大压力。我在实际项目中测试发现连续高频请求会导致HTTP 429错误。2. 商品分类数据结构解析Shopee采用两级分类体系通过get_category_tree接口返回的JSON数据结构如下{ data: { category_list: [ { catid: 11040766, display_name: 女生衣著, children: [ { catid: 11042304, display_name: T恤 } ] } ] } }关键字段说明catid分类唯一标识商品列表查询需使用二级分类IDparent_catid父分类ID一级分类该值为0level分类层级1或2display_name分类显示名称通过Java解析时推荐使用FastJSON处理嵌套结构。这里有个实际开发中的坑点部分分类的children字段可能为null而非空数组需要特别处理NPE异常。3. 商品列表接口参数详解获取商品列表的核心接口是search_items其请求参数需要特别注意String url https://xiapi.xiapibuy.com/api/v4/search/search_items ?byrelevancy fe_categoryids categoryId limit60 // 每页记录数 newest (pageIndex * 60) // 偏移量 orderdesc page_typesearch scenarioPAGE_OTHERS version2;参数说明表参数名必填示例值说明fe_categoryids是11042304二级分类IDlimit是60固定每页60条newest是0偏移量计算规则(页码-1)*60scenario是PAGE_OTHERS固定值勿修改version是2API版本号返回数据中最有用的item_basic对象包含itemid商品唯一IDname商品标题price价格需除以100000historical_sold历史销量image主图URL4. 完整Java实现代码以下是增强版的Java实现增加了异常处理和速率控制import java.io.IOException; import java.util.concurrent.TimeUnit; import org.jsoup.Connection.Method; import org.jsoup.Jsoup; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; public class ShopeeApiClient { private static final String CATEGORY_API https://xiapi.xiapibuy.com/api/v4/pages/get_category_tree; private static final String ITEMS_API https://xiapi.xiapibuy.com/api/v4/search/search_items; private static final int MAX_PAGES 100; private static final int DELAY_MS 1500; // 1.5秒间隔 public static void main(String[] args) { try { JSONArray categories fetchCategories(); processCategories(categories); } catch (Exception e) { System.err.println(程序异常终止: e.getMessage()); } } private static JSONArray fetchCategories() throws IOException { String result Jsoup.connect(CATEGORY_API) .ignoreContentType(true) .method(Method.GET) .timeout(30000) .execute() .body(); return JSON.parseObject(result) .getJSONObject(data) .getJSONArray(category_list); } private static void processCategories(JSONArray categories) { for (int i 0; i categories.size(); i) { JSONObject parentCat categories.getJSONObject(i); System.out.printf(处理分类: %s (%s)%n, parentCat.getString(display_name), parentCat.getString(catid)); JSONArray children parentCat.getJSONArray(children); if (children null || children.isEmpty()) continue; for (int j 0; j children.size(); j) { processSubCategory(children.getJSONObject(j)); } } } private static void processSubCategory(JSONObject subCat) { String catId subCat.getString(catid); System.out.printf( 处理子分类: %s (%s)%n, subCat.getString(display_name), catId); for (int page 0; page MAX_PAGES; page) { try { TimeUnit.MILLISECONDS.sleep(DELAY_MS); JSONArray items fetchItems(catId, page); if (items null || items.isEmpty()) break; System.out.printf( 第%d页获取到%d个商品%n, page 1, items.size()); processItems(items); } catch (Exception e) { System.err.printf(处理页面%d出错: %s%n, page, e.getMessage()); break; } } } private static JSONArray fetchItems(String catId, int page) throws IOException { String url String.format(%s?byrelevancyfe_categoryids%slimit60newest%dorderdescpage_typesearchscenarioPAGE_OTHERSversion2, ITEMS_API, catId, page * 60); String response Jsoup.connect(url) .ignoreContentType(true) .timeout(30000) .execute() .body(); return JSON.parseObject(response).getJSONArray(items); } private static void processItems(JSONArray items) { for (int i 0; i items.size(); i) { JSONObject item items.getJSONObject(i) .getJSONObject(item_basic); System.out.printf( [%d] %s - %.2f元%n, item.getLong(itemid), item.getString(name), item.getLong(price) / 100000.0); } } }代码优化点说明增加了1.5秒的请求间隔符合API限制使用更清晰的方法拆分各处理逻辑添加了空数据检查和异常处理价格显示格式化为两位小数输出日志更友好直观5. 常见问题与调试技巧在实际对接过程中可能会遇到以下典型问题问题1返回数据为空检查分类ID是否正确必须使用二级分类ID确认页码计算正确newest参数(页码-1)*60某些分类可能确实没有商品问题2连接超时适当增加timeout值示例代码设置为30秒添加重试机制建议最多3次问题3HTTP 429错误降低请求频率增加DELAY_MS值使用代理IP轮询需谨慎避免滥用调试建议先用Postman测试API接口确认参数组合有效打印完整响应数据检查错误信息字段对大数据量处理时添加进度日志输出// 示例调试代码打印原始响应 System.out.println(原始响应: response.substring(0, Math.min(200, response.length())));6. 性能优化与扩展建议对于需要大规模采集的场景可以考虑以下优化方案多线程优化ExecutorService executor Executors.newFixedThreadPool(5); executor.submit(() - processSubCategory(subCat));数据存储优化使用数据库批量插入代替逐条处理考虑使用Spring Batch处理海量数据断点续采将已采集的categoryId和pageIndex持久化程序重启时从上次中断处继续代理池管理使用Luminati等专业代理服务实现自动IP切换机制数据更新策略记录商品最后更新时间增量更新而非全量采集7. 法律合规与最佳实践在开发过程中务必注意严格遵守Shopee的API使用条款禁止绕过限制进行高频访问数据不得用于商业爬虫或恶意竞争建议添加用户代理标识Jsoup.connect(url) .userAgent(MyResearchApp/1.0 (contactexample.com)) ...我在实际项目中的经验是保持透明合规的使用态度必要时可以联系Shopee官方申请正式的API权限。曾经因为测试时忘记加延迟导致IP被临时封禁后来通过控制请求频率就再未出现问题。