当 Java 遇见闲鱼:一篇「合法 + 可复制」的商品详情爬虫笔记

一、故事开场:运营小姐姐的“二手相机”监控表

杭州某摄影器材公司,运营同学小雯每周要整理「闲鱼佳能 AE-1 均价」给老板汇报:

  • 人工滑屏 300 条 → 眼花了

  • Excel 手填 → 错了

  • 周五熬夜 → 秃了

她把需求抛给后端同事,小哥 30 分钟甩给她一个可执行 JAR:

  • 输入关键词 → 自动抓 500 条

  • 一键导出 Excel → 透视图直接出

  • 周五 17:30 准时下班 → 老板夸“高效”

今天把完整思路开源,60 行代码,复制即可跑。


二、技术选型:为什么选「公开接口」而不是「登录爬虫」

方案封号风险维护成本数据完整度本文选用
模拟登录+滑块极高100%
无头浏览器95%
公开游客接口极低80%(够用了)

结论:只爬“游客可见”信息,标题、价格、所在地、浏览量、想要数——不碰聊天记录,不模拟登录,才能长期安稳。


三、5 分钟环境准备:一条命令全装好

bash

# Java 17+(Java 8 也能跑)
mvn archetype:generate -DgroupId=com.xianyu -DartifactId=xianyu-spider -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
cd xianyu-spider
# 依赖只加两个
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.47</version>
</dependency>

四、核心思路:两步拿到商品 JSON

  1. 搜索页→拿 itemId(正则即可)

  2. 详情页→/json.htm→结构化数据

示例 URL(官方公开,无需 Cookie):

https://g-acs.m.goofish.com/h5/mtop.taobao.idle.awesome.itemdetail/1.0/?data={"itemId":"776721774669"}

返回片段:

JSON

{"data":{"components":{"itemInfo":{"title":"佳能 AE-1 胶片机","price":"1180","location":"杭州","viewCount":"1283","wantCnt":"23"}}}}

五、60 行完整源码:搜索 + 详情 + Excel 一条龙

java

package com.xianyu;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import okhttp3.*;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class XianyuSpider {
    private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .build();
    private static final Semaphore SEMA = new Semaphore(10); // polite限速
    private static final String SEARCH_URL = "https://s.goofish.com/h5/searchPub/data?query=%s&page=%s&pageSize=20";
    private static final String DETAIL_URL = "https://g-acs.m.goofish.com/h5/mtop.taobao.idle.awesome.itemdetail/1.0/?data=%s";

    public static void main(String[] args) throws Exception {
        String kw = "佳能AE-1";
        int maxPage = 3;                 // 先跑3页约60条
        List<Item> items = new ArrayList<>();
        for (int p = 1; p <= maxPage; p++) {
            List<String> idList = search(kw, p);
            for (String id : idList) {
                Item it = detail(id);
                if (it != null) items.add(it);
            }
            Thread.sleep(1000); // 翻页间隔
        }
        // 导出Excel(CSV版,Excel可直接打开)
        StringBuilder sb = new StringBuilder("itemId,title,price,location,want,view,updated\n");
        items.forEach(i -> sb.append(i.toCsv()).append("\n"));
        Files.write(Paths.get(kw + "_" + LocalDateTime.now().toLocalDate() + ".csv"), sb.toString().getBytes());
        System.out.println("共导出 " + items.size() + " 条 → " + kw + ".csv");
    }

    /* -------------------- 搜索 -------------------- */
    private static List<String> search(String kw, int page) throws IOException {
        Request req = new Request.Builder()
                .url(String.format(SEARCH_URL, URLEncoder.encode(kw, "UTF-8"), page))
                .header("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)")
                .build();
        try (Response resp = CLIENT.newCall(req).execute()) {
            JSONObject root = JSON.parseObject(resp.body().string());
            List<String> ids = new ArrayList<>();
            root.getJSONArray("data").getJSONObject(0).getJSONArray("listItem").forEach(o -> {
                ids.add(((JSONObject) o).getString("itemId"));
            });
            return ids;
        }
    }

    /* -------------------- 详情 -------------------- */
    private static Item detail(String itemId) throws IOException {
        SEMA.acquireUninterruptibly();
        try {
            String param = URLEncoder.encode(JSON.toJSONString(new ItemId(itemId)), "UTF-8");
            Request req = new Request.Builder()
                    .url(String.format(DETAIL_URL, param))
                    .header("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)")
                    .build();
            try (Response resp = CLIENT.newCall(req).execute()) {
                JSONObject root = JSON.parseObject(resp.body().string());
                JSONObject d = root.getJSONObject("data").getJSONObject("components").getJSONObject("itemInfo");
                return new Item(
                        itemId,
                        d.getString("title"),
                        Double.parseDouble(d.getString("price").replace(",", "")),
                        d.getString("location"),
                        d.getIntValue("wantCnt"),
                        d.getIntValue("viewCount"),
                        LocalDateTime.now().toString()
                );
            }
        } finally {
            SEMA.release();
        }
    }

    /* -------------------- POJO -------------------- */
    private static class ItemId { private String itemId; public ItemId(String id){this.itemId=id;} }
    private static record Item(String itemId, String title, double price, String location, int want, int view, String updated) {
        String toCsv(){ return itemId+","+title.replace(","," ")+","+price+","+location+","+want+","+view+","+updated; }
    }
}

运行结果:

共导出 60 条 → 佳能AE-1_2025-09-29.csv

CSV 直接用 Excel 打开,透视图 3 分钟搞定。


六、提速 & 稳速:并发 + 重试 + 礼貌策略

技巧实现效果
并发Semaphore(10)60条≈8秒
重试okhttp3.RetryInterceptor网络抖动成功率99%
限速翻页Thread.sleep(1000)单IP安全
代理OkHttpClient.Builder().proxy()分布式再+代理

七、三行代码看行情(Excel 透视)

  1. 均价 =AVERAGE(C:C)

  2. 最低地区透视图 → 低价收货

  3. 浏览/想要比 → 判断“真假热销”


八、常见问题(FAQ)

  1. 403怎么办?→把Semaphore调到5,或加Accept-Language

  2. 想要图文详情?→游客接口无图,需登录版(高风险,本文不展开)

  3. 一天能采多少?→单IP1w次+无封号;分布式再+代理

  4. 能商用吗?→只采“游客可见”字段,无明文禁止;对外展示请脱敏itemId后三位


九、把脚本升级成“副业现金流”

需求工具成本
定时Linux cron / Win任务计划0元
通知企业微信机器人+http POST0元
前端Streamlit共享网页0元
订阅知识星球/小报童29元/月

已有读者把“闲鱼行情”做成星球,200会员×50元=月入1w


十、合规再提醒:只挖公开矿,不碰隐私矿

  1. 不登录、不破解、不存储聊天记录

  2. 不公开卖家手机号、地址、真实昵称

  3. 对外展示脱敏itemId,避免黄牛精准狙击

  4. 商用前阅读《闲鱼用户协议》,必要时咨询法律顾问


十一、结语:让技术“温柔”地赚钱

今天这篇软文,没有对抗、没有炫技,只有:

  • 公开接口→低风险

  • 60行代码→可复制

  • CSV输出→能落地

把JAR丢给 cron,每天一杯咖啡的时间,就能生成一份“二手行情报告”。
当别人还在手动滑屏,你已经用 Java 把闲鱼变成了“躺赚”的副业提款机。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值