我有个好朋友,喜欢听音乐,在twitter上收藏了很多音乐博主,想要每天定时拉取音乐MV视频,问我能不能做,我当然很热心的帮助他.
一. 创建twitter app 拿到Bearer Token
地址:https://developer.twitter.com/en/portal/projects-and-apps
具体怎么创建网上很多文章有说明,但大多数是过时的,创建应用无非是验证邮箱,然后填一些个人信息,创建应用原因等等,自己慢慢摸索,最需要注意:
twitter帐号关联的邮箱地址不要用国内的,不然收不到验证码
我改成gmail邮箱,才成功收到验证码
二.查看api 找到获取列表推文接口
https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/create-manage-lists/api-reference/get-lists-statuseshttps://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/create-manage-lists/api-reference/get-lists-statusesapi接口:https://api.twitter.com/1.1/lists/statuses.json
参数:
list_id:列表ID(自己去twitter列表F12去看自己的列表ID)
since_id:最小推文id
count:推文数量
三.通过接口获取数据解析并下载视频
- 获取列表的推文内容
/**
* 获取推特列表内容
*
* @param listId 列表ID
* @param startListId 最小推文ID
* @param count 推文数量
* @return
*/
public static List<Tweet> getListTweets(String listId, String startListId, Integer count) {
try {
if (StringUtils.isEmpty(listId)) return null;
trustEveryone();
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3");
String baseUrl = listApi + "?list_id=" + listId;
if (StringUtils.isNotEmpty(startListId)) {
baseUrl += "&since_id=" + startListId;
}
if (count != null && count > 0) {
baseUrl += "&count" + count;
}
URL url = new URL(baseUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + token);
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36");
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("Connection", "keep-alive");
conn.setUseCaches(false);
conn.setConnectTimeout(20000);
conn.setReadTimeout(20000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
InputStream is = null;
if (conn.getResponseCode() == 200) {
is = conn.getInputStream();
} else if (conn.getResponseCode() == 400) {
is = conn.getErrorStream();
}
if (is == null) return null;
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuilder sb = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
JSONArray array = JSONArray.parseArray(sb.toString());
if (array != null && array.size() > 0) {
List<Tweet> list = new ArrayList<>();
for (Object obj : array) {
JSONObject json = (JSONObject) obj;
Tweet tweet = formaterTweet(json);
if (tweet != null) {
list.add(tweet);
}
}
return list;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
注意:请求https可能会报错:Remote host closed connection during handshake
解决办法参考:https://blog.csdn.net/qq_30831935/article/details/94299220
- 解析推文
/**
* 推文内容解析
* json.id : tweetId
* json.text: (unicode) tweetText;
* json.created_at: 创建时间(new Date("Wed Nov 17 14:39:51 +0000 2021"))
* json.{extended_entities}.[media].id : 视频ID
* json.{extended_entities}.[media].media_url : 视频缩略图
* json.{extended_entities}.[media].{video_info}.[variants] : (list[{bitrate:"分辨率",content_type:"video/mp4",url:"视频地址"}]) 视频集合,取分辨率最高的一个
* json.user.id: 发布用户ID
* json.user.name: 发布用户名称
*
* @param json
* @return
*/
private static Tweet formaterTweet(JSONObject json) {
try {
if (json == null || !json.containsKey("extended_entities")) return null;
Tweet tweet = new Tweet();
tweet.setTwId(json.getString("id"));
tweet.setTwText(UnicodeUtil.toString(json.getString("text")));
tweet.setTwTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(json.getString("created_at"))));
tweet.setMediaId(json.getJSONObject("extended_entities").getJSONArray("media").getJSONObject(0).getString("id"));
tweet.setMediaPic(json.getJSONObject("extended_entities").getJSONArray("media").getJSONObject(0).getString("media_url"));
if (json.getJSONObject("extended_entities").getJSONArray("media").size() > 1 || !json.getJSONObject("extended_entities").getJSONArray("media").getJSONObject(0).containsKey("video_info"))
return null;
//获取分辨率最高的视频地址
List<JSONObject> mediaz = json.getJSONObject("extended_entities").getJSONArray("media").getJSONObject(0).getJSONObject("video_info").getJSONArray("variants").toJavaList(JSONObject.class);
List<JSONObject> medias = mediaz.stream().filter(t -> t.getString("content_type").equals("video/mp4")).sorted((t1, t2) -> {
return t2.getInteger("bitrate") - t1.getInteger("bitrate");
}).collect(Collectors.toList());
tweet.setMediaUrl(medias.get(0).getString("url"));
tweet.setUserId(json.getJSONObject("user").getString("id"));
tweet.setUserName(json.getJSONObject("user").getString("name"));
return tweet;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Tweet 实体:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Tweet {
private String twId; //推文ID
private String twText; //推文内容
private String twTime; //发推时间
private String mediaId; //视频ID
private String mediaPic; //视频缩略图
private String mediaUrl; //视频地址
private String userId; //发推用户ID
private String userName; //发推用户名
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
- 下载内容
/**
* 下载推文文字,视频和缩略图
*
* @param tweet
*/
private static void downloadMedia(Tweet tweet) {
try {
String tweetFile = localSource + "/" + tweet.getTwId() + "/" + tweet.getTwId();
//保存缩略图
if (!FileUtil.exist(tweetFile + ".jpg")) {
saveToFile(tweet.getMediaPic(), tweetFile + ".jpg");
}
//保存视频
if (!FileUtil.exist(tweetFile + ".mp4")) {
saveToFile(tweet.getMediaUrl(), tweetFile + ".mp4");
}
//保存推文内容
if (!FileUtil.exist(tweetFile + ".txt")) {
FileUtil.writeString(tweet.getTwText(), tweetFile + ".txt", Charset.forName("utf-8"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
效果:
代码完整内容查看:https://download.csdn.net/download/D_lady/44794742
github:https://github.com/doodt/TwitterUtil/blob/main/TwitterUtil.java