在通过对微博正文内容中的图片 or 视频内容进行分析后,图片 or 视频 链接是可以直接通过 Java 代码下载或者转存的,那么这样就可以补充我们在 【基于Java爬取微博数据(一) 微博主页正文列表数据】 时缺失的图片 or 视频信息了,当然,如果你的需求并不需要转存微博正文列表内容中的图片 or 视频的话,那么你就无需进行下面的操作了。在开始进行微博主页正文列表数据 补充 图片 or 视频内容之前,先来分析一下获取到的微博正文列表数据的内容。
数据分析
同样的,我们先找到获取微博正文列表数据的 ajax 请求 /ajax/statuses/mymblog?uid=1686546714&page=1&feature=0 的响应返回数据
获取到微博正文列表请求响应返回的数据之后,我从中取出一个含图片的完整的微博正文 json 对象 以及 一个含视频的完整的微博正文 json 对象来做一个比较
通过对比工具 Beyond Compare 进行比较这两种情况下返回数据格式的不同,可以看到 含图片的微博正文 返回数据比 含视频的微博正文多了 pic_infos 对象
继续向下比较可以看到 含图片的微博正文 比 含视频的微博正文 少了 page_info对象 而 page_info对象 里面的 media_info 对象正是视频所在对象
到这里,对于微博正文列表内容 含图片微博正文 以及 含视频微博正文 的数据格式基本的分析及对比就结束了,下面开始在 获取微博正文列表内容 DemoWeiBo 的 main 方法中补充这一块内容的获取。
补充图片 or 视频
下面开始补充图片 or 视频 链接的获取操作,考虑到视频链接有 Expires 过期时间字段,那么这里在导出 微博正文列表内容 到 Excel 中时保存 转存后的图片 or 视频路径,首先给导出实体类 ExcelData 增加如下字段
然后在获取微博正文内容列表 main 方法中增加如下代码,这里需要注意的是 pic_ids 和 pic_infos 是配套出现的, pic_ids 的值 就是 pic_infos 子对象的 key
关于 pic_infos 的子对象包括多种 宽高 尺寸的图片链接地址,你可以根据自己的需要选择不同宽高的图片进行转存
最后补充的转存微博正文图片的代码如下
下面再来看获取视频操作,你可以选择 media_info 对象内的以下几种清晰度的视频,
或者也可以选择 media_info 对象内的 playback_list ,里面是 四种清晰度的视频选择,你可以选择其中一种或者多种清晰度的视频链接
最终补充获取微博正文视频内容的代码如下
到这里,补充微博正文内容列表 获取 图片 or 视频的操作就完成了,改造后的 DemoWeiBo.java 代码完整版如下
package com.ruoyi.web.controller.demo.controller; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; public class DemoWeiBo { /** * 主函数入口,用于从微博抓取数据并存储到Excel中。 * * @param args 命令行参数(未使用) * @throws ParseException 当日期解析发生错误时抛出 */ public static void main(String[] args) throws ParseException { // 定义微博数据抓取的URL模板 String url = "https://weibo.com/ajax/statuses/mymblog?uid=1686546714&feature=0&page=%s"; String unfoldurl = "https://weibo.com/ajax/statuses/longtext?id=%s"; String cookie = "你的 Cookie"; // 初始化日期格式 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //初始化导出Excel数据列表 List<ExcelData> excelDataList = new ArrayList<>(); // 循环抓取前2页数据 for (int i = 1; i <= 3; i++) { try { // 输出开始抓取的提示信息 System.out.println("开始获取第" + i + "页数据"); // 格式化URL并发送HTTP请求获取响应 String urlstr = String.format(url, i); HttpResponse response = HttpUtil.createGet(urlstr) .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36") .header("Cookie",cookie) .execute(); // 解析响应体 String body = response.body(); //System.out.println(body); JSONObject jsonObject = JSON.parseObject(body).getJSONObject("data"); JSONArray list = null; if (Objects.nonNull(jsonObject)) { // 处理数据列表 list = jsonObject.getJSONArray("list"); // 遍历并处理每条微博数据 for (Object o : list) { JSONObject data = (JSONObject) o; // 解析并处理微博的其他信息 Date created = new Date(data.getString("created_at")); System.out.println("created:"+dateFormat.format(created)); String regex = "<[^<>]*>"; String text = data.getString("text").replaceAll(regex, ""); String repost = data.getString("reposts_count"); String comment = data.getString("comments_count"); String like = data.getString("attitudes_count"); //获取微博正文图片信息 StringBuffer pic_url = new StringBuffer(); Long pic_num = data.getLong("pic_num"); if (pic_num > 0 ) { JSONArray pic_ids = data.getJSONArray("pic_ids"); JSONObject pic_infos = data.getJSONObject("pic_infos"); // 遍历 pic_ids 获取 pic_infos 子对象 key for (Object json : pic_ids) { String key = (String) json; JSONObject pic = pic_infos.getJSONObject(key); JSONObject largest = pic.getJSONObject("largest"); // 提取图片URL并处理 String imageUrl = largest.getString("url"); String filename = imageUrl.substring(imageUrl.lastIndexOf("/") + 1); // 下载图片 String savePath = "E:\\2024weibo\\" + filename; downloadPicture(imageUrl, savePath); pic_url = pic_url.append(savePath).append(","); } } //获取微博正文视频信息 String video_url = ""; JSONObject page_info = data.getJSONObject("page_info"); if (Objects.nonNull(page_info)) { JSONObject media_info = page_info.getJSONObject("media_info"); String mp4_hd_url = media_info.getString("mp4_hd_url"); String filename = mp4_hd_url.substring(mp4_hd_url.lastIndexOf("/") + 1, mp4_hd_url.indexOf("?")); // 下载视频 String savePath = "E:\\2024weibo\\" + filename; downloadPicture(mp4_hd_url, savePath); video_url = savePath; } //有一种情况,就是当页面文本内容过多的时候,微博默认不展示全部,而是出现 【...展示】 按钮,此时需要再请求一个 URL 获取展开后的文本内容 if (text.lastIndexOf("...展开") != -1) { //说明存在 展开 需要重新获取 text 内容 String mblogid = data.getString("mblogid"); // 格式化URL并发送HTTP请求获取响应 String unfoldurlstr = String.format(unfoldurl, mblogid); HttpResponse response2 = HttpUtil.createGet(unfoldurlstr) .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36") .header("Cookie",cookie) .execute(); // {"ok": 1,"http_code": 200,"data": {}} String body2 = response2.body(); JSONObject jsonObject2 = JSONObject.parseObject(body2).getJSONObject("data"); String longTextContent = jsonObject2.getString("longTextContent"); System.out.println("longTextContent:"+longTextContent); //补全后的内容赋给 text text = longTextContent; } // 创建ExcelData对象并填充数据 ExcelData excelData = new ExcelData(); //发布时间 excelData.setDate(created); //点赞数 excelData.setLike(Long.parseLong(like)); //评论数 excelData.setComment(Long.parseLong(comment)); //转发数 excelData.setRepost(Long.parseLong(repost)); //原始内容 excelData.setContent(text); //图片地址 excelData.setImgUrl(pic_url.toString()); //视频地址 excelData.setVideoUrl(video_url); excelDataList.add(excelData); } } // 输出完成提示并关闭响应,休眠以避免频繁请求 System.out.println("第" + i + "页数据获取完毕"); response.close(); // 如果列表为空,终止循环 if (list == null || list.size() == 0) { break; } Thread.sleep(700); } catch (Exception e) { // 打印异常信息 e.printStackTrace(); } } // 输出开始写入Excel的提示 System.out.println("Excel写入数据开始"); // 写入Excel的函数调用 EasyExcel.write("E:/微博.xlsx", ExcelData.class) .sheet("Sheet1") .doWrite(excelDataList); System.out.println("Excel写入数据结束"); } /** * 下载图片到指定路径 * * @param imageUrl 图片的URL地址 * @param savePath 图片保存的本地路径 */ public static void downloadPicture(String imageUrl, String savePath){ BufferedInputStream in = null; FileOutputStream out = null; HttpURLConnection connection = null; try { // 创建URL对象并打开连接 URL url = new URL(imageUrl); connection = (HttpURLConnection) url.openConnection(); // 设置请求方法为GET connection.setRequestMethod("GET"); // 建立连接 connection.connect(); // 获取响应码并判断是否下载成功 int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { // 创建输入流和输出流,用于读取和保存图片 in = new BufferedInputStream(connection.getInputStream()); out = new FileOutputStream(savePath); // 缓冲区,用于一次读取和写入一定量的数据 byte[] buffer = new byte[1024]; int bytesRead; // 循环读取直到没有数据 while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } System.out.println("图片/视频 下载成功,保存路径:" + savePath); } else { // 响应码不为HTTP_OK,下载失败 System.out.println("无法下载图片/视频,响应码:" + responseCode); } }catch (Exception e) { // 捕获异常并打印堆栈信息 e.printStackTrace(); }finally { // 无论成功或失败,最后都关闭流和连接 // 关闭输入流 if (in != null) { try { in.close(); } catch (IOException e) { // 将IO异常转为运行时异常抛出 throw new RuntimeException(e); } } // 关闭输出流 if (out != null) { try { out.close(); } catch (IOException e) { // 将IO异常转为运行时异常抛出 throw new RuntimeException(e); } } // 关闭连接 if (connection != null) { connection.disconnect(); } } } }
执行结果
执行main 方法后的输出 Excel 结果如图
图片链接和视频链接都已经转存记录成功。