Java 获取1688关键词搜索接口完整指南

admin8小时前1688api8

1688(阿里巴巴批发网)作为国内最大的B2B电商平台,为开发者提供了丰富的API接口用于商品搜索、数据获取等操作。本文将详细介绍如何使用Java调用1688关键词搜索接口,涵盖从账号注册到完整代码实现的完整流程。


一、接口概述

1688开放平台提供了多种商品搜索相关接口,核心接口包括:

表格

接口名称 功能描述 适用场景
alibaba.product.search 按关键词搜索商品列表 通用商品搜索
item_search 关键词搜索商品列表(第三方封装) 快速集成
alibaba.product.list.get 获取店铺商品列表 店铺商品管理

本文以 item_search 接口为例,介绍通过关键词搜索1688商品列表的完整实现。该接口支持分页查询、价格过滤、排序等功能,返回商品ID、标题、价格、库存、图片等核心字段。


二、准备工作

1. 注册开发者账号

  1. 访问 1688开放平台 注册开发者账号

  2. 完成企业认证(个人开发者功能受限)

  3. 创建应用,获取以下凭证:

表格

凭证 说明
AppKey 应用唯一标识
AppSecret 应用密钥,用于签名
AccessToken 用户授权令牌(OAuth2.0)

2. Maven 依赖配置

xml

<dependencies>
    <!-- HTTP 请求 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.14</version>
    </dependency>
    
    <!-- JSON 解析 -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
    
    <!-- 日志 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.9</version>
    </dependency>
</dependencies>

三、核心代码实现

1. 签名工具类

1688 API 采用 HMAC-SHA1 签名算法,需将参数按字典序排序后拼接生成签名:

java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.stream.Collectors;

public class SignUtil {
    
    private static final String HMAC_SHA1 = "HmacSHA1";
    
    /**
     * 生成 HMAC-SHA1 签名
     * 签名规则:将参数按 key 字典序排序,拼接为 key1=value1&key2=value2,使用 appSecret 作为密钥进行 HMAC-SHA1 加密
     */
    public static String generateHmacSha1Sign(Map<String, String> params, String appSecret) {
        // 1. 过滤空值,按 key 字典序排序
        String sortedParams = params.entrySet().stream()
            .filter(entry -> entry.getValue() != null && !entry.getValue().isEmpty())
            .sorted(Map.Entry.comparingByKey())
            .map(entry -> entry.getKey() + "=" + entry.getValue())
            .collect(Collectors.joining("&"));
        
        // 2. HMAC-SHA1 加密
        try {
            Mac mac = Mac.getInstance(HMAC_SHA1);
            SecretKeySpec secretKey = new SecretKeySpec(
                appSecret.getBytes(StandardCharsets.UTF_8), 
                HMAC_SHA1
            );
            mac.init(secretKey);
            byte[] bytes = mac.doFinal(sortedParams.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(bytes).toUpperCase();
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException("签名生成失败", e);
        }
    }
    
    /**
     * 字节数组转十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}

2. Access Token 获取(OAuth2.0)点击注册账号测试接口


import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.util.ArrayList;
import java.util.List;

public class AuthService {
    
    private static final String TOKEN_URL = "https://gw.open.1688.com/openapi/http/1/system.oauth2/getToken";
    
    /**
     * 使用 client_credentials 模式获取 Access Token
     */
    public static String getAccessToken(String clientId, String clientSecret) {
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            HttpPost post = new HttpPost(TOKEN_URL);
            
            List<BasicNameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("grant_type", "client_credentials"));
            params.add(new BasicNameValuePair("client_id", clientId));
            params.add(new BasicNameValuePair("client_secret", clientSecret));
            params.add(new BasicNameValuePair("need_refresh_token", "true"));
            
            post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
            
            String response = EntityUtils.toString(client.execute(post).getEntity(), "UTF-8");
            JsonObject json = JsonParser.parseString(response).getAsJsonObject();
            
            if (json.has("access_token")) {
                return json.get("access_token").getAsString();
            } else {
                throw new RuntimeException("获取 Token 失败: " + response);
            }
        } catch (Exception e) {
            throw new RuntimeException("Token 获取异常", e);
        }
    }
}

3. 关键词搜索服务类


import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ItemSearchService {
    
    // 1688 开放平台网关地址
    private static final String API_URL = "https://gw.open.1688.com/openapi/param2/2/portals.open/api.listOfferDetail/";
    // 或第三方封装接口
    private static final String API_URL_THIRD = "https://api-gw.onebound.cn/1688/item_search/";
    
    private final String appKey;
    private final String appSecret;
    private final Gson gson = new Gson();
    
    public ItemSearchService(String appKey, String appSecret) {
        this.appKey = appKey;
        this.appSecret = appSecret;
    }
    
    /**
     * 按关键词搜索商品
     * 
     * @param keyword   搜索关键词
     * @param page      页码(从1开始)
     * @param pageSize  每页数量(最大50)
     * @param sort      排序方式:price(价格)、sale(销量)、credit(信用)
     * @param priceStart 起始价格(可选)
     * @param priceEnd  结束价格(可选)
     * @return 商品列表
     */
    public SearchResult searchByKeyword(String keyword, int page, int pageSize, 
                                        String sort, Double priceStart, Double priceEnd) {
        
        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("key", appKey);
        params.put("secret", appSecret);
        params.put("q", keyword);
        params.put("page", String.valueOf(page));
        params.put("page_size", String.valueOf(pageSize));
        
        if (sort != null) {
            params.put("sort", sort);
        }
        if (priceStart != null) {
            params.put("start_price", String.valueOf(priceStart));
        }
        if (priceEnd != null) {
            params.put("end_price", String.valueOf(priceEnd));
        }
        
        // 生成签名
        String sign = SignUtil.generateHmacSha1Sign(params, appSecret);
        params.put("sign", sign);
        
        // 构建请求 URL
        String url = buildUrl(API_URL_THIRD, params);
        
        // 发送请求
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            httpGet.setHeader("Accept", "application/json");
            
            String response = EntityUtils.toString(client.execute(httpGet).getEntity(), "UTF-8");
            JsonObject jsonResponse = JsonParser.parseString(response).getAsJsonObject();
            
            return parseSearchResult(jsonResponse);
            
        } catch (Exception e) {
            throw new RuntimeException("搜索商品失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 构建带参数的 URL
     */
    private String buildUrl(String baseUrl, Map<String, String> params) {
        StringBuilder url = new StringBuilder(baseUrl);
        url.append("?");
        params.forEach((key, value) -> {
            try {
                url.append(key).append("=")
                   .append(URLEncoder.encode(value, "UTF-8"))
                   .append("&");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("URL 编码失败", e);
            }
        });
        // 移除末尾的 &
        return url.substring(0, url.length() - 1);
    }
    
    /**
     * 解析搜索响应
     */
    private SearchResult parseSearchResult(JsonObject response) {
        SearchResult result = new SearchResult();
        
        // 检查错误
        if (response.has("error")) {
            JsonObject error = response.getAsJsonObject("error");
            result.setSuccess(false);
            result.setErrorCode(error.has("code") ? error.get("code").getAsString() : "UNKNOWN");
            result.setErrorMessage(error.has("message") ? error.get("message").getAsString() : "未知错误");
            return result;
        }
        
        result.setSuccess(true);
        
        // 解析商品列表
        if (response.has("items") && response.get("items").isJsonObject()) {
            JsonObject itemsObj = response.getAsJsonObject("items");
            
            if (itemsObj.has("item") && itemsObj.get("item").isJsonArray()) {
                JsonArray items = itemsObj.getAsJsonArray("item");
                List<Product> productList = new ArrayList<>();
                
                for (int i = 0; i < items.size(); i++) {
                    JsonObject item = items.get(i).getAsJsonObject();
                    Product product = new Product();
                    product.setProductId(item.has("num_iid") ? item.get("num_iid").getAsString() : "");
                    product.setTitle(item.has("title") ? item.get("title").getAsString() : "");
                    product.setPrice(item.has("price") ? item.get("price").getAsString() : "0");
                    product.setOriginalPrice(item.has("orginal_price") ? item.get("orginal_price").getAsString() : "0");
                    product.setPicUrl(item.has("pic_url") ? item.get("pic_url").getAsString() : "");
                    product.setDetailUrl(item.has("detail_url") ? item.get("detail_url").getAsString() : "");
                    product.setSales(item.has("sales") ? item.get("sales").getAsInt() : 0);
                    product.setSellerName(item.has("seller_nick") ? item.get("seller_nick").getAsString() : "");
                    product.setLocation(item.has("area") ? item.get("area").getAsString() : "");
                    product.setPostFee(item.has("post_fee") ? item.get("post_fee").getAsString() : "0");
                    
                    productList.add(product);
                }
                result.setProducts(productList);
            }
            
            // 解析分页信息
            if (itemsObj.has("page")) {
                result.setCurrentPage(itemsObj.get("page").getAsInt());
            }
            if (itemsObj.has("real_total_results")) {
                result.setTotalResults(itemsObj.get("real_total_results").getAsInt());
            }
        }
        
        return result;
    }
}

4. 实体类定义


import java.util.List;

/**
 * 搜索结果封装类
 */
public class SearchResult {
    private boolean success;
    private String errorCode;
    private String errorMessage;
    private List<Product> products;
    private int currentPage;
    private int totalResults;
    
    // Getters and Setters
    public boolean isSuccess() { return success; }
    public void setSuccess(boolean success) { this.success = success; }
    
    public String getErrorCode() { return errorCode; }
    public void setErrorCode(String errorCode) { this.errorCode = errorCode; }
    
    public String getErrorMessage() { return errorMessage; }
    public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
    
    public List<Product> getProducts() { return products; }
    public void setProducts(List<Product> products) { this.products = products; }
    
    public int getCurrentPage() { return currentPage; }
    public void setCurrentPage(int currentPage) { this.currentPage = currentPage; }
    
    public int getTotalResults() { return totalResults; }
    public void setTotalResults(int totalResults) { this.totalResults = totalResults; }
    
    @Override
    public String toString() {
        return "SearchResult{" +
            "success=" + success +
            ", totalResults=" + totalResults +
            ", productsCount=" + (products != null ? products.size() : 0) +
            '}';
    }
}

/**
 * 商品信息实体类
 */
public class Product {
    private String productId;      // 商品ID
    private String title;          // 商品标题
    private String price;          // 当前价格
    private String originalPrice;  // 原价
    private String picUrl;         // 商品图片
    private String detailUrl;      // 商品详情页链接
    private int sales;             // 销量
    private String sellerName;     // 卖家昵称
    private String location;       // 发货地
    private String postFee;        // 运费
    
    // Getters and Setters
    public String getProductId() { return productId; }
    public void setProductId(String productId) { this.productId = productId; }
    
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    
    public String getPrice() { return price; }
    public void setPrice(String price) { this.price = price; }
    
    public String getOriginalPrice() { return originalPrice; }
    public void setOriginalPrice(String originalPrice) { this.originalPrice = originalPrice; }
    
    public String getPicUrl() { return picUrl; }
    public void setPicUrl(String picUrl) { this.picUrl = picUrl; }
    
    public String getDetailUrl() { return detailUrl; }
    public void setDetailUrl(String detailUrl) { this.detailUrl = detailUrl; }
    
    public int getSales() { return sales; }
    public void setSales(int sales) { this.sales = sales; }
    
    public String getSellerName() { return sellerName; }
    public void setSellerName(String sellerName) { this.sellerName = sellerName; }
    
    public String getLocation() { return location; }
    public void setLocation(String location) { this.location = location; }
    
    public String getPostFee() { return postFee; }
    public void setPostFee(String postFee) { this.postFee = postFee; }
    
    @Override
    public String toString() {
        return "Product{" +
            "productId='" + productId + '\'' +
            ", title='" + title + '\'' +
            ", price='" + price + '\'' +
            ", sales=" + sales +
            ", sellerName='" + sellerName + '\'' +
            '}';
    }
}

5. 主程序入口


public class ItemSearchDemo {
    
    public static void main(String[] args) {
        // 配置信息(请替换为实际值)
        String appKey = "your_app_key";
        String appSecret = "your_app_secret";
        String keyword = "手机支架";  // 搜索关键词
        
        try {
            // 初始化搜索服务
            ItemSearchService searchService = new ItemSearchService(appKey, appSecret);
            
            System.out.println("正在搜索关键词: " + keyword);
            System.out.println("=".repeat(60));
            
            // 执行搜索(第1页,每页40条,按价格排序)
            SearchResult result = searchService.searchByKeyword(
                keyword, 
                1,           // 页码
                40,          // 每页数量
                "price",     // 排序方式:price/sale/credit
                10.0,        // 起始价格
                100.0        // 结束价格
            );
            
            // 输出结果
            if (result.isSuccess()) {
                System.out.println("搜索成功!");
                System.out.println("总结果数: " + result.getTotalResults());
                System.out.println("当前页: " + result.getCurrentPage());
                System.out.println("商品数量: " + result.getProducts().size());
                System.out.println("-".repeat(60));
                
                // 遍历商品列表
                for (Product product : result.getProducts()) {
                    System.out.println("商品ID: " + product.getProductId());
                    System.out.println("标题: " + product.getTitle());
                    System.out.println("价格: ¥" + product.getPrice());
                    System.out.println("原价: ¥" + product.getOriginalPrice());
                    System.out.println("销量: " + product.getSales());
                    System.out.println("卖家: " + product.getSellerName());
                    System.out.println("发货地: " + product.getLocation());
                    System.out.println("图片: " + product.getPicUrl());
                    System.out.println("链接: " + product.getDetailUrl());
                    System.out.println("-".repeat(60));
                }
            } else {
                System.err.println("搜索失败!");
                System.err.println("错误码: " + result.getErrorCode());
                System.err.println("错误信息: " + result.getErrorMessage());
            }
            
        } catch (Exception e) {
            System.err.println("程序执行异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

四、接口返回数据示例

调用成功后,接口返回的 JSON 数据结构如下:

JSON

{
    "items": {
        "page": 1,
        "real_total_results": 1250,
        "total_results": 1250,
        "pagecount": 32,
        "item": [
            {
                "num_iid": "123456789012",
                "title": "手机支架桌面懒人支架可折叠",
                "price": "15.80",
                "orginal_price": "25.00",
                "pic_url": "https://cbu01.alicdn.com/xxx.jpg",
                "detail_url": "https://detail.1688.com/offer/123456789012.html",
                "sales": 5000,
                "seller_nick": "某某电子厂",
                "area": "广东深圳",
                "post_fee": "0.00"
            }
        ]
    }
}

五、请求参数说明


参数名 类型 是否必填 说明
key String AppKey
secret String AppSecret
q String 搜索关键词
page Integer 页码,默认1
page_size Integer 每页数量,默认20,最大50
sort String 排序:price(价格)、sale(销量)、credit(信用)
start_price Double 起始价格
end_price Double 结束价格
cat String 类目ID
filter String 额外过滤条件

六、常见问题与解决方案


问题 原因 解决方案
401 认证失败 AppKey/AppSecret 错误或过期 检查凭证是否正确,确认应用状态正常
403 权限不足 未申请接口权限 在开放平台申请 商品搜索 相关 API 权限
429 请求频繁 超出调用频率限制 增加请求间隔,实现限流策略
签名错误 签名算法或参数排序有误 确认使用 HMAC-SHA1 算法,参数按字典序排序
数据为空 关键词无匹配结果或参数错误 更换关键词,检查价格区间是否合理
Token 过期 AccessToken 有效期通常为2小时 实现 Token 自动刷新机制

七、进阶优化建议

1. Token 自动刷新与缓存


public class TokenManager {
    private String accessToken;
    private long expireTime;
    private final String clientId;
    private final String clientSecret;
    
    public TokenManager(String clientId, String clientSecret) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }
    
    public synchronized String getValidToken() {
        // 提前5分钟刷新
        if (accessToken == null || System.currentTimeMillis() > expireTime - 300000) {
            accessToken = AuthService.getAccessToken(clientId, clientSecret);
            expireTime = System.currentTimeMillis() + 7200 * 1000;
        }
        return accessToken;
    }
}

2. 连接池配置


import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

public class HttpClientPool {
    private static final CloseableHttpClient httpClient;
    
    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(200);
        cm.setDefaultMaxPerRoute(50);
        
        httpClient = HttpClients.custom()
            .setConnectionManager(cm)
            .setConnectionManagerShared(true)
            .build();
    }
    
    public static CloseableHttpClient getClient() {
        return httpClient;
    }
}

3. 批量搜索与异步处理

对于需要批量获取多页数据的场景,建议使用 CompletableFuture 实现异步并行调用:


import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class BatchSearchService {
    
    public List<Product> searchAllPages(String keyword, int totalPages) {
        List<CompletableFuture<List<Product>>> futures = IntStream.rangeClosed(1, totalPages)
            .mapToObj(page -> CompletableFuture.supplyAsync(() -> {
                SearchResult result = searchService.searchByKeyword(keyword, page, 40, "price", null, null);
                return result.getProducts();
            }))
            .collect(Collectors.toList());
        
        return futures.stream()
            .map(CompletableFuture::join)
            .flatMap(List::stream)
            .collect(Collectors.toList());
    }
}


返回列表

上一篇:Java 获取微店商品详情接口完整指南

没有最新的文章了...

相关文章

1688 包装运费尺寸接口开发实战指南

在跨境电商、供应链管理和 ERP 系统集成中,获取 1688 商品的包装、运费和尺寸信息是核心需求。本文将系统性地介绍如何通过官方 API 和第三方方案获取这些数据,并提供完整的代码实现。一、接口体系...

1688图搜+关键词搜索商品API:从入门到实战,让你效率翻倍

一、概述1688作为国内最大的B2B批发采购平台,拥有海量商品数据。对于电商开发者、选品工具和供应链系统而言,图片搜索(图搜/拍立淘)和关键词搜索是两种最高效的找货方式。本文将带你从0到1掌握1688...

1688商品详情接口(item_get)完整指南:从接入到实战

一、接口概述1688.item_get(官方名称为 alibaba.item.get)是1688开放平台提供的核心商品详情查询接口,用于根据商品ID获取1688批发商品的全维度结构化数据。该接口区别于...

1688详情关键词搜索图搜API接口:B2B智能选品系统核心技术解析

一、1688图搜API的技术定位与选品价值为什么1688图搜是供应链选品的核心?plain复制传统B2B选品模式:       &...

1688 跨境寻源通详情接口深度解析:从接入到实战

一、接口概述与定位1688 跨境寻源通 API 是 1688 开放平台专为跨境电商打造的供应链数据解决方案,其中商品详情接口是核心能力之一。该接口为跨境卖家、ERP 系统及铺货工具提供全维度商品数据支...

Java实现1688拍立淘接口(item_search_img)完整技术指南

在B2B电商场景中,以图搜货已成为供应链选品、竞品监控、货源溯源的核心能力。1688的item_search_img接口(拍立淘)允许开发者通过上传图片,秒级匹配平台上的同款或相似商品。本文将详细介绍...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。