效果
代码
BilibiliVideoDurationCrawler
import org. jsoup. Jsoup ;
import org. jsoup. nodes. Document ;
import org. jsoup. nodes. Element ;
import org. jsoup. select. Elements ;
import java. io. IOException ;
import java. text. ParseException ;
import java. util. ArrayList ;
import java. util. List ;
import java. util. regex. Matcher ;
import java. util. regex. Pattern ; public class BilibiliVideoDurationCrawler { private static final Pattern VIDEO_PART_PATTERN = Pattern . compile ( "\"part\":\"(.*?)\",\"duration\":(\\d+)," ) ; public static void main ( String [ ] args) { String url = "https://www.bilibili.com/video/BV1834y1676P/" ; List < VideoPart > videoParts = new ArrayList < > ( ) ; try { videoParts = getVideoPartsFromUrl ( url) ; } catch ( IOException | ParseException e) { System . err. println ( "获取视频信息失败:" + e. getMessage ( ) ) ; return ; } if ( ! videoParts. isEmpty ( ) ) { for ( int i = 0 ; i < videoParts. size ( ) ; i++ ) { String progress = getProgressStr ( videoParts, i) ; System . out. println ( "p" + ( i + 1 ) + " " + videoParts. get ( i) . getPart ( ) + " " + progress) ; } } } public static List < VideoPart > getVideoPartsFromUrl ( String url) throws IOException , ParseException { Document doc = Jsoup . connect ( url) . userAgent ( "Mozilla/5.0" ) . get ( ) ; Elements elements = doc. select ( "script" ) ; List < String > result = new ArrayList < > ( ) ; for ( Element element : elements) { Matcher matcher = VIDEO_PART_PATTERN . matcher ( element. html ( ) ) ; while ( matcher. find ( ) ) { result. add ( "Part: " + matcher. group ( 1 ) + ", Duration: " + matcher. group ( 2 ) ) ; } } List < VideoPart > videoParts = new ArrayList < > ( ) ; long totalDuration = 0 ; for ( String str : result) { String [ ] parts = str. split ( ", " ) ; String part = parts[ 0 ] . split ( ": " ) [ 1 ] ; long duration = Long . parseLong ( parts[ 1 ] . split ( ": " ) [ 1 ] ) ; totalDuration += duration; VideoPart videoPart = new VideoPart ( part, duration) ; videoParts. add ( videoPart) ; } VideoPart . setTotalDuration ( totalDuration) ; return videoParts; } public static String getProgressStr ( List < VideoPart > videoParts, int p) { if ( videoParts == null || p < 0 || p >= videoParts. size ( ) ) { throw new IllegalArgumentException ( "Invalid video parts or index p" ) ; } long totalLength = VideoPart . getTotalDuration ( ) ; long lengthBeforeP = 0 ; for ( int i = 0 ; i <= p; i++ ) { lengthBeforeP += videoParts. get ( i) . getDuration ( ) ; } double progress = ( double ) lengthBeforeP / ( totalLength == 0 ? 1 : totalLength) * 100 ; return String . format ( "%.2f%%" , progress) ; }
}
VideoPart
import lombok. AllArgsConstructor ;
import lombok. Data ; import java. util. Date ; @Data
@AllArgsConstructor
public class VideoPart { public VideoPart ( String part, long duration) { this . part = part; this . duration = duration; } public static void setTotalDuration ( long totalDuration) { VideoPart . totalDuration = totalDuration; } public static long getTotalDuration ( ) { return totalDuration; } public void Duration2NeedTime ( ) { this . needTime = new Date ( duration* 1000 ) ; } private String part; private long duration; private Date needTime; private double progress; private static long totalDuration = - 1 ;
}