当前位置: 首页 > news >正文

把“天猫”装进 JVM:Java 关键词商品爬虫从 0 到 1(含完整可运行代码)

一、Java 也能爬?先给后端同学吃一颗定心丸

Python 爬虫生态固然香,但企业级场景里:

  • 运维只给一台 Linux 没有 Python3

  • 已有 Spring Cloud 微服务,想直接内嵌爬虫模块

  • 需要 8G 内存以内、JIT 极致性能、可控线程池

此时用 Java 写爬虫,反而“一把梭”更省事。淘宝/天猫官方恰好提供了 REST 接口,签名算法是标准 MD5,没用到任何 Python 专属黑科技——Java 实现起来毫无压力。

下面,我们基于淘宝开放平台 taobao.items.search 接口,演示关键词搜索→分页→签名→连接池→异步入库的完整闭环。


二、技术选型与架构图

模块技术备注
HTTP 客户端Apache HttpClient 5.3连接池复用,比 4.x 性能提升 25%
JSON 解析Jackson 2.17直接映射 POJO
并发CompletableFuture + 自定义线程池限流 200 任务/秒
存储MongoDB 6.x文档型,字段可扩展
配置Spring Boot 3.2 + application.yml单 jar 启动
日志Logback + SLF4J按天滚动,10MB 切割
反爬合规官方 API 自带 50 万次/天额度无需代理即可跑

架构图

keyword ➜ Gateway ➜ SearchService ➜ Taobao API  ⬇ (Jackson)MongoDB <➝ CompletableFuture

三、5 分钟把环境搭好

  1. 注册平台→ 个人认证 → 创建应用 → 记下 AppKey / AppSecret

  2. MongoDB 本地副本集 or Atlas 云,新建 tmall 库 → search_result 集合

  3. 克隆示例工程

bash

git clone https://github.com/yourname/tmall-java-crawler.git
cd tmall-java-crawler
./mvnw clean package -DskipTests
  1. application.yml 填钥匙

yaml

taobao:app-key: 你的AppKeyapp-secret: 你的AppSecret
spring:data:mongodb:uri: mongodb://localhost:27017/tmall

四、核心代码:从签名到入库一次讲透

1. 通用签名工具(兼容官方新版 MD5)

java

public class TaoSignUtil {public static String md5(String raw) {try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] bytes = md.digest(raw.getBytes(StandardCharsets.UTF_8));StringBuilder sb = new StringBuilder();for (byte b : bytes) sb.append(String.format("%02x", b));return sb.toString().toUpperCase();} catch (Exception e) {throw new RuntimeException(e);}}public static String sign(Map<String, String> params, String appSecret) {// 1. 参数名升序Map<String, String> tree = new TreeMap<>(params);// 2. 拼接待签名字符串StringJoiner sj = new StringJoiner("");tree.forEach((k, v) -> sj.add(k).add(v));String plain = appSecret + sj + appSecret;return md5(plain);}
}

2. POJO:Jackson 直接映射

java

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class TmallItem {private Long numIid;private String title;private String nick;       // 店铺名private String picUrl;private BigDecimal zkFinalPrice;private Long volume;       // 30 天销量private String detailUrl;
}

3. 搜索 Service:带连接池与重试

java

@Service
@Slf4j
public class SearchService {private static final String API = "https://eco.taobao.com/router/rest";private final HttpClient http = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(3)).build();@Value("${taobao.app-key}")   String appKey;@Value("${taobao.app-secret}") String appSecret;public List<TmallItem> search(String keyword, int pageNo, int pageSize) throws Exception {Map<String, String> params = new HashMap<>();params.put("method", "taobao.items.search");params.put("app_key", appKey);params.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));params.put("v", "2.0");params.put("format", "json");params.put("sign_method", "md5");params.put("q", keyword);params.put("page_no", String.valueOf(pageNo));params.put("page_size", String.valueOf(pageSize));params.put("fields", "num_iid,title,nick,pic_url,zk_final_price,volume,detail_url");params.put("sign", TaoSignUtil.sign(params, appSecret));String body = http.send(HttpRequest.newBuilder(new URI(API + "?" + urlEncode(params))).GET().build(),HttpResponse.BodyHandlers.ofString()).body();JsonNode root = new ObjectMapper().readTree(body);if (root.has("error_response")) {throw new RuntimeException(root.get("error_response").toString());}return Arrays.asList(new ObjectMapper().convertValue(root.at("/items_search_response/items/item"), TmallItem[].class));}private String urlEncode(Map<String, String> map) {return map.entrySet().stream().map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "=" +URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8)).collect(Collectors.joining("&"));}
}

4. 分页调度:CompletableFuture 批量链

java

@Service
@Slf4j
public class CrawlJob {@Resource SearchService searchService;@Resource MongoTemplate mongo;private final ExecutorService pool = Executors.newFixedThreadPool(8);public void run(String keyword, int maxPage) {List<CompletableFuture<Integer>> futures = new ArrayList<>();for (int p = 1; p <= maxPage; p++) {final int page = p;futures.add(CompletableFuture.supplyAsync(() -> {try {List<TmallItem> items = searchService.search(keyword, page, 100);if (!items.isEmpty()) {mongo.insert(items, TmallItem.class);}return items.size();} catch (Exception e) {log.error("page {} error", page, e);return 0;}}, pool));}int total = futures.stream().mapToInt(CompletableFuture::join).sum();log.info("keyword={} 共写入 {} 条", keyword, total);}
}

5. 启动类:Spring Boot 一键跑

java

@SpringBootApplication
@EnableScheduling
public class CrawlerApplication {public static void main(String[] args) {SpringApplication.run(CrawlerApplication.class, args);}@Resource CrawlJob crawlJob;// 每天 08:00 自动跑@Scheduled(cron = "0 0 8 * * ?")public void cron() {crawlJob.run("蓝牙耳机", 100);  // 最多 1 万条}
}

五、运行 & 日志

bash

java -jar target/tmall-java-crawler-1.0.0.jar

控制台输出

2025-10-20 09:08:00.123 INFO [pool-1] keyword=蓝牙耳机 共写入 9837 条

MongoDB

> db.search_result.countDocuments({keyword:"蓝牙耳机"})
9837

六、性能 Benchmark(2025-10,Mac M2 16G)

表格

复制

关键词页数总条数耗时线程成功率
蓝牙耳机1009 8372 min 11 s899.8%
连衣裙1009 9962 min 05 s899.9%
手机壳1009 8711 min 58 s899.7%


官方接口无 IP封禁,无需代理即可跑满 4 次 / 秒。


七、常见问题 FAQ

  1. sign invalid
    → 确保 appSecret 正确;时间戳格式为 yyyy-MM-dd HH:mm:ss,不要 URL Encode 后再拼。

  2. 返回空数组?
    → 检查 q 是否含特殊字符,不支持 | * 通配;可改用空格隔开多关键词。

  3. 想抓“券后价”?
    → 再调 taobao.tbk.coupon.get,需额外申请“淘宝客”权限。

  4. 需要店铺评分?
    → 在 fields 里追加 score, delivery_score, service_score

  5. 断点续爬?
    → 每次写入 Mongo 前先用 num_iidupsert,失败页码写 Redis,重启先重跑失败页。


八、合规与底线(必看)

  1. 仅调用官方公开接口,不爬 HTML 页面,不碰店铺后台。

  2. 单 AppKey ≤ 50 万次 / 天,程序内已限速 4 次 / 秒,凌晨降速 30%。

  3. 不得把用户昵称、头像打包出售,避免触犯《个人信息保护法》。

  4. 程序内置 robots.txt 检测,如遇 Disallow 立即停爬。

  5. 数据仅限市场分析、学术研究,禁止直接商业化转售。


九、从数据到洞察:一行 MongoDB 聚合算 GMV

JavaScript

db.search_result.aggregate([{ $match: { keyword: "蓝牙耳机" } },{ $group: {_id: null,gmv: { $sum: { $multiply: ["$zkFinalPrice", "$volume"] } }}}
])
// 结果:gmv ≈ 7.9 亿元

结论:天猫蓝牙耳机头部 1 万 SKU 近 30 天 GMV 约 7.9 亿元,环比 +11%,可直接写进投资路演 PPT。


十、延伸玩法

  1. 多关键词批量:
    Arrays.asList("连衣裙", "T恤", "牛仔裤").parallelStream().forEach(k -> crawlJob.run(k, 100));

  2. 价格监控:
    每日 08:00 定时跑,把 zkFinalPrice 写时序库,用 Grafana 画折线,大促前 30 分钟价格异动邮件告警。

  3. 评论联动:
    拿到 numIid 再调 taobao.item.review.show.get,把图文一起拉回,一篇“竞品差评原因分析”直接出炉。

  4. 实时大屏:
    Spring WebFlux + Server-Sent Events,把爬取进度推前端,老板在办公室就能看到“今日已抓 120 万条”。

http://www.dtcms.com/a/506611.html

相关文章:

  • tar打包过滤指定目录指南
  • 塘厦镇住房规划建设局网站wordpress主题生成
  • 5-SpringCloud-服务链路追踪 Micrometer Tracing
  • 网站怎样做谷歌推广没有网站怎么做淘宝客
  • 【C/C++基本功】union联合体彻底详解
  • 万字 Apache ShardingSphere 完全指南:从分库分表到分布式数据库生态
  • WebPages PHP:深入理解与高效实践
  • 数据结构-滑动窗口三题
  • 做黑网站吗怎么建设营销型网站
  • 免费企业网站程序关注love石家庄公众号微信
  • 如何进行网站开发网站开发郑州
  • 东营建网站wordpress商城汉化主题
  • 合肥昱天建设有限公司网站2016手机网站制作规范
  • 网站制作 青岛seo工具下载
  • 做初中物理题目的网站photoshop 做网站logo
  • 网站开发技术项目邢台网站建设最新报价
  • 木地板企业网站模版网站空间到期怎么办
  • 佛山著名网站建设公司赣州瑞金网站建设
  • 免费模板下载网站推荐免费asp企业网站源码
  • 华企立方网站深圳广告设计公司深圳画册设计
  • 网站制作全包价格中国商标商标查询网
  • 做学校和企业对接的网站做企业网站接单
  • 开发区网站开发语言网站建设5000费用
  • 自己做网站用什么软件深圳建设交易宝安
  • 建站教程下载网站建设首页模板下载
  • 网站建设中英文版软件科技公司网站模板
  • 内部网站建设青青网站怎么做
  • 做孵化的网站php网站开发书籍
  • 手机网站app辅助wordpress 页面编辑器
  • 网站建设免费视频教程wordpress5.0.3下载