Spring Boot初体验:带娃间隙搭了个爬虫服务

写给机器的诗
2026-01-20 10:33
阅读 420

上周五晚上十一点,娃刚睡着,我正瘫在沙发上刷手机,突然收到产品经理的钉钉消息:“明天上线前能不能加个数据抓取功能?就一个小需求。”
我盯着屏幕,内心一万只羊驼奔腾而过——这都第几次了?但转念一想,反正也睡不着(毕竟凌晨三点还要起来喂奶),不如顺手用 Spring Boot 搞个轻量级爬虫服务练练手。于是,这篇“60分钟快速上手”教程,就诞生于尿布和代码的夹缝中。

我是深圳某腾讯系公司的后端工程师,平时主要搞分布式系统,但自从当了全职妈妈,写代码的时间基本被压缩到娃午睡的两小时 + 夜深人静的碎片时间。好在我重度依赖 ChatGPT 和 Claude,它们简直是我的“数字育儿嫂”——帮我生成模板、查错、甚至写单元测试。今天这篇文章,就是边哄睡边敲出来的实战总结,希望能帮到同样在“带娃+搬砖”双线作战的你。


为啥选 Spring Boot?

说实话,以前我对 Java 有点“ptsd”——配置文件多如牛毛,XML 写到眼花,启动慢得像树懒。但自从用了 Spring Boot,真香!自动配置、内嵌 Tomcat、起步依赖(starter)……一套组合拳下来,5 分钟就能跑起一个 REST API,比泡奶粉还快。

这次的需求很简单:从某个公开网站抓取商品价格,存到数据库,前端再展示出来。典型的“爬虫 + 后端 + 前端”三件套。虽然 Python 写爬虫更顺手,但公司技术栈是 Java,而且我想顺便复习下 Spring 生态,就硬着头皮上了。


60分钟搭建流程(附踩坑实录)

第一步:初始化项目(5分钟)

打开 start.spring.io,勾选以下依赖:

  • Spring Web
  • Spring Data JPA
  • H2 Database(开发用,生产换 MySQL)
  • Lombok(省掉 getter/setter,妈妈的时间很宝贵!)
  • HttpClient(用于爬虫请求)
<!-- pom.xml 关键依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

吐槽:上次有个实习生直接用 URLConnection 写爬虫,结果没处理重定向,被反爬机制干趴了。HttpClient 配个 User-Agent 和超时,至少能装个人样。

第二步:写爬虫逻辑(15分钟)

我用的是 Apache HttpClient + Jsoup 解析 HTML。核心代码如下:

@Service
public class ProductCrawler {

    @Autowired
    private CloseableHttpClient httpClient;

    public String fetchPrice(String url) {
        try {
            HttpGet request = new HttpGet(url);
            request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
            
            HttpResponse response = httpClient.execute(request);
            String html = EntityUtils.toString(response.getEntity());
            
            // 用 Jsoup 解析价格
            Document doc = Jsoup.parse(html);
            Element priceElem = doc.selectFirst(".price"); // 假设价格在 class="price" 的元素里
            return priceElem != null ? priceElem.text() : "N/A";
        } catch (Exception e) {
            log.error("爬虫失败: {}", e.getMessage());
            return "ERROR";
        }
    }
}

血泪教训:千万别在 Controller 里直接调爬虫!上次我图省事,结果并发一高,线程池爆了,运维半夜打电话骂我。现在一律用 @Async 或消息队列解耦。

第三步:设计数据库和接口(10分钟)

实体类用 JPA 注解,简单粗暴:

@Entity
@Table(name = "products")
@Data
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private String price;
    private String sourceUrl;
    private LocalDateTime crawledAt;
}

Repository 只需继承 JpaRepository,连 SQL 都不用写:

public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findBySourceUrl(String url);
}

Controller 提供两个接口:

  • POST /crawl:触发爬虫并保存
  • GET /products:返回所有抓取记录(供前端调用)
@RestController
@RequestMapping("/api")
public class ProductController {

    @Autowired
    private ProductCrawler crawler;

    @Autowired
    private ProductRepository repo;

    @PostMapping("/crawl")
    public ResponseEntity<String> crawl(@RequestParam String url) {
        String price = crawler.fetchPrice(url);
        Product product = new Product();
        product.setPrice(price);
        product.setSourceUrl(url);
        product.setCrawledAt(LocalDateTime.now());
        repo.save(product);
        return ResponseEntity.ok("抓取成功!");
    }

    @GetMapping("/products")
    public List<Product> listProducts() {
        return repo.findAll();
    }
}

第四步:联调前端(10分钟)

前端同事用 Vue 写了个简陋页面,调用 /api/products 展示表格。这里有个坑:跨域问题

Spring Boot 默认禁止跨域,得加个配置:

@Configuration
public class CorsConfig {

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOriginPatterns(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList("GET", "POST"));
        config.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

真实场景:前端小哥第一次调接口,浏览器报 CORS error,差点以为我后端挂了。后来才知道是忘了开跨域。这种低级错误,在 deadline 前最致命。

第五步:本地测试 & 打包(10分钟)

运行 ./mvnw spring-boot:run,Postman 测试接口:

curl -X POST "http://localhost:8080/api/crawl?url=https://example.com/product/123"

确认无误后,打包成 jar:

./mvnw clean package
java -jar target/demo-0.0.1-SNAPSHOT.jar

搞定!从零到可运行服务,刚好一集《小猪佩奇》的时间(别笑,这是我的计时单位)。


生产环境注意事项(血泪总结)

虽然这只是个 demo,但如果你真要上线,务必注意:

事项 开发环境 生产环境
数据库 H2(内存) MySQL/PostgreSQL
爬虫频率 无限制 加限流(如 Guava RateLimiter)
日志 控制台输出 ELK 集中收集
错误处理 直接返回 ERROR 重试机制 + 告警
安全 无认证 JWT/OAuth2

特别提醒:爬虫别太 aggressive!我见过有人写死循环疯狂请求,结果 IP 被封,还连累公司其他业务。建议加个 @Scheduled 定时任务,每小时跑一次,优雅又安全。


最后:妈妈程序员的碎碎念

写完这个小项目,已经是凌晨两点。看着熟睡的娃,突然觉得——技术其实没那么难,难的是在生活的缝隙里挤出时间。Spring Boot 之所以流行,就是因为它降低了 Java 的门槛,让像我这样“时间碎片化”的开发者也能快速产出。

如果你也在带娃、加班、赶需求的夹缝中挣扎,别焦虑。一行代码,一次部署,都是胜利。毕竟,我们不是在写代码,是在给未来留点体面。

对了,这个爬虫服务后来真的上线了。产品经理说“效果不错”,虽然我知道他根本不懂技术。但无所谓,只要娃明天不发烧,我就算赢了。

Happy coding, fellow parents! 🍼💻

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝