您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Java爬虫之如何实现B站粉丝取关人排查
## 目录
1. [背景与需求分析](#背景与需求分析)
2. [技术选型与准备工作](#技术选型与准备工作)
3. [B站接口分析与逆向工程](#B站接口分析与逆向工程)
4. [核心代码实现](#核心代码实现)
5. [数据存储与比对策略](#数据存储与比对策略)
6. [异常处理与反爬应对](#异常处理与反爬应对)
7. [可视化展示方案](#可视化展示方案)
8. [完整项目部署指南](#完整项目部署指南)
9. [法律与道德风险提示](#法律与道德风险提示)
10. [总结与扩展思考](#总结与扩展思考)
---
## 背景与需求分析
在B站UP主运营过程中,粉丝流失分析是重要课题。传统手动记录方式存在:
- 无法实时监控粉丝变动
- 难以追溯历史取关用户
- 缺乏数据可视化分析
本项目通过Java爬虫技术实现:
1. 定时抓取当前粉丝列表
2. 自动比对历史数据
3. 识别取关用户特征
4. 生成可视化报表
典型应用场景:
- 内容质量评估
- 粉丝运营策略调整
- 异常掉粉预警
## 技术选型与准备工作
### 技术栈组合
| 技术组件 | 版本 | 用途 |
|----------------|---------|------------------------|
| Java | 11+ | 主开发语言 |
| Jsoup | 1.15.3 | HTML解析 |
| HttpClient | 4.5.13 | HTTP请求 |
| Gson | 2.8.9 | JSON处理 |
| Quartz | 2.3.2 | 任务调度 |
| SQLite | 3.36.0 | 本地数据存储 |
### 开发环境准备
```bash
# Maven依赖配置示例
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
</dependencies>
通过浏览器开发者工具分析发现:
- 粉丝列表接口:https://api.bilibili.com/x/relation/followers
- 必需参数:
- vmid
: 用户UID
- pn
: 页码
- ps
: 每页数量(建议20)
- csrf
: 登录凭证
B站新版接口采用Wbi签名认证
,核心算法:
// 签名生成算法示例
public static String getWbiSign(Map<String, String> params, String imgKey, String subKey) {
String salt = imgKey.substring(0, 6) + subKey.substring(0, 6);
params.put("wts", System.currentTimeMillis()/1000+"");
StringJoiner sj = new StringJoiner("&");
params.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> sj.add(entry.getKey()+"="+URLEncoder.encode(entry.getValue())));
return DigestUtils.md5Hex(sj.toString() + salt);
}
public class BiliBiliFetcher {
private static final String API_URL = "https://api.bilibili.com/x/relation/followers";
public List<FanInfo> fetchAllFans(long mid) throws IOException {
List<FanInfo> fans = new ArrayList<>();
int page = 1;
while(true) {
String json = fetchPage(mid, page);
FanResponse response = new Gson().fromJson(json, FanResponse.class);
if(response.getData().getList().isEmpty()) break;
fans.addAll(response.getData().getList());
page++;
Thread.sleep(1000); // 礼貌性延迟
}
return fans;
}
private String fetchPage(long mid, int page) throws IOException {
Map<String, String> params = new HashMap<>();
params.put("vmid", String.valueOf(mid));
params.put("pn", String.valueOf(page));
params.put("ps", "20");
String signedUrl = SignUtils.signUrl(API_URL, params);
return HttpUtil.get(signedUrl);
}
}
public class FanComparator {
public static List<UnfollowRecord> compare(List<FanInfo> oldFans,
List<FanInfo> newFans) {
Map<Long, FanInfo> oldMap = oldFans.stream()
.collect(Collectors.toMap(FanInfo::getMid, Function.identity()));
return newFans.stream()
.filter(newFan -> !oldMap.containsKey(newFan.getMid()))
.map(newFan -> new UnfollowRecord(
newFan.getMid(),
newFan.getUname(),
LocalDateTime.now()
)).collect(Collectors.toList());
}
}
CREATE TABLE fans (
mid INTEGER PRIMARY KEY,
uname TEXT,
sign TEXT,
level INTEGER,
follow_time DATETIME,
last_seen DATETIME
);
CREATE TABLE unfollow_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mid INTEGER,
uname TEXT,
detect_time DATETIME,
FOREIGN KEY(mid) REFERENCES fans(mid)
);
try {
// 爬虫代码
} catch (HttpStatusException e) {
if(e.getStatusCode() == 412) {
// 触发B站风控
antiAntiCrawler.refreshCookies();
}
} catch (SocketTimeoutException e) {
// 网络超时重试
Thread.sleep(30000);
retryCount++;
}
IP轮换:使用代理池
public class ProxyManager {
private static List<Proxy> proxies = loadProxies();
private static AtomicInteger index = new AtomicInteger(0);
public static Proxy getNextProxy() {
return proxies.get(index.getAndIncrement() % proxies.size());
}
}
请求特征模拟:完整浏览器指纹
行为模式随机化:随机延迟、滚动鼠标等
public class Dashboard extends Application {
@Override
public void start(Stage stage) {
LineChart<Number, Number> chart = new LineChart<>(
new NumberAxis(), new NumberAxis());
// 绑定数据库数据...
stage.setScene(new Scene(chart, 800, 600));
stage.show();
}
}
# config.properties示例
bilibili.uid=12345678
schedule.interval=3600
db.path=./data/fans.db
public class MainScheduler {
public static void main(String[] args) {
Trigger trigger = newTrigger()
.withSchedule(cronSchedule("0 0 3 * * ?")) // 每天3点执行
.build();
scheduler.scheduleJob(jobDetail, trigger);
}
}
本项目代码仅供学习交流,不当使用造成的账号封禁等后果自负
本文代码仓库:https://github.com/example/bilibili-unfollow-detector
持续更新日期:2023-08-20 “`
注:实际实现时需要注意: 1. 遵守B站接口调用频率限制 2. 用户敏感信息脱敏处理 3. 建议使用官方开放API(如有) 4. 添加合理的异常处理机制 5. 关键操作记录日志备查
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。