Spring Boot初体验:带娃间隙搭了个爬虫服务
上周五晚上十一点,娃刚睡着,我正瘫在沙发上刷手机,突然收到产品经理的钉钉消息:“明天上线前能不能加个数据抓取功能?就一个小需求。”
我盯着屏幕,内心一万只羊驼奔腾而过——这都第几次了?但转念一想,反正也睡不着(毕竟凌晨三点还要起来喂奶),不如顺手用 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