2012-07-16 60 views
21

我基本上有一個Youtube網址作爲NSString,但我需要提取顯示在URL中的視頻ID。我發現很多關於如何在php或其他基於網絡的編程語言中做到這一點的教程,但沒有在目標c ...Youtube視頻ID從URL - 目標C

我基本上尋找一種方法,要求NSString url作爲參數和作爲另一個nsstring返回視頻ID ...

任何人都知道如何做到這一點或任何教程呢?

+0

有您正在使用一個不同的格式?你能舉個例子嗎? – Stavash 2012-07-16 17:12:38

+0

@stavash不只是基本的Youtube桌面網址... – 2012-07-16 17:40:42

+1

你可以提供一個示例URL和你已經嘗試過的東西。 – eternalmatt 2012-07-16 17:53:45

回答

15

所以YouTube的URL類似於:

http://www.youtube.com/watch?v=oHg5SJYRHA0

你感興趣的是視頻ID是在年底(oHg5SJYRHA0)部分....雖然它不一定是在年底,因爲YouTube網址可以在查詢字符串中包含其他參數。

最好的辦法是使用正則表達式和Foundation的NSRegularExpression類。我認爲這種方法在你找到的其他語言教程中使用 - 請注意,正則表達式的內容在包含它們的任何語言或工具包中幾乎相同,所以在這些教程中找到的任何正則表達式都應該可以工作爲你。 (我建議不要採用打破v=的方法,並採用正確的11個字符,因爲這容易導致各種失敗模式的正則表達式更強大。)

要找到視頻ID,您可能需要一個正則表達式像v=([^&]+)v=讓我們訪問查詢網址的右側部分(如果我們得到類似watch?fmt=22&v=oHg5SJYRHA0的東西)。圓括號使捕獲組因此我們只能提取視頻ID而不提取其他我們用來找到它的匹配字符,在括號內我們尋找一個或多個字符序列,這不是一個&符號 - 這個請確保我們獲得了v=whatever字段中的所有內容,並且如果您獲得了像watch?v=oHg5SJYRHA0&rel=0這樣的URL,則不會有任何字段。

無論您使用這個還是另一個正則表達式,您可能會使用捕獲組。 (如果沒有的話,rangeOfFirstMatchInString:options:range:就是你需要的全部東西,正如Dima的答案中所見。)你可以在拍攝團體使用firstMatchInString:options:range:內容(如NSTextCheckingResult對象)或類似的方法:

NSError *error = NULL; 
NSRegularExpression *regex = 
[NSRegularExpression regularExpressionWithPattern:@"?.*v=([^&]+)" 
              options:NSRegularExpressionCaseInsensitive 
              error:&error]; 
NSTextCheckingResult *match = [regex firstMatchInString:youtubeURL 
               options:0 
                range:NSMakeRange(0, [youtubeURL length])]; 
if (match) { 
    NSRange videoIDRange = [match rangeAtIndex:1]; 
    NSString *substringForFirstMatch = [youtubeURL substringWithRange:videoIDRange]; 
} 
+2

不是regexes有點矯枉過正**和**不夠靈活,在同一時間?我發現這一切*只是可怕的。* – 2013-07-22 22:13:02

+0

不適合我,你應該刪除?從你的正則表達式 – 2017-03-16 19:11:25

8

你可能看到的教程只是關於如何使用正則表達式的說明,這也是你想要在這種情況下使用的。

您需要使用的可可類是NSRegularExpression

您的實際正則表達式字符串將取決於您期待的網址格式,因爲它看起來像youtube有幾個。一般功能看起來像:

+ (NSString *)extractYoutubeID:(NSString *)youtubeURL 
{ 
    NSError *error = NULL; 
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"your regex string goes here" options:NSRegularExpressionCaseInsensitive error:&error]; 
    NSRange rangeOfFirstMatch = [regex rangeOfFirstMatchInString:youtubeURL options:0 range:NSMakeRange(0, [youtubeURL length])]; 
    if(!NSEqualRanges(rangeOfFirstMatch, NSMakeRange(NSNotFound, 0))) 
    { 
    NSString *substringForFirstMatch = [youtubeURL substringWithRange:rangeOfFirstMatch]; 

    return substringForFirstMatch; 
    } 
    return nil; 
} 
+1

你應該使用'nil'而不是'NULL'。 http://stackoverflow.com/a/1564419/11976 – 2012-07-16 17:54:31

+0

更新,因爲我同意,但總的來說這沒關係。 – Dima 2012-07-16 18:03:06

-3

我想通了我自己......

NSArray *videoURLSplit = [videoURL componentsSeparatedByString:@"v="]; 
NSString *videoID = [[videoURLSplit objectAtIndex:1] substringToIndex:11]; 
NSLog(@"%@",videoID); 

很簡單......所有視頻網址包含V = VIDEO_ID我剛剛分離的網址爲數組,然後採取前v =後面的第一個11位數字,因爲可能有更多http GET信息在url中...

感謝您的幫助!

+1

請小心,因爲這實際上並非如此。還有其他幾種格式的視頻可以進來,例如'http://youtu.be/###########'和'youtube.com/v/######### ##'。 – Dima 2012-07-16 18:08:00

+1

當youtube開始使用超過11個字符的ID時會發生什麼情況。你也需要考慮這一點。 – eternalmatt 2012-07-16 18:11:40

+0

@迪瑪謝謝你,但我加載了http://www.youtube.com/watch?nomobile=1在web視圖中的這個特定的地址,我只允許用戶搜索視頻並點擊它們,所以幾乎每次網址應該是... http://www.youtube.com/v=VIDEO_ID – 2012-07-16 18:12:12

38

對不起 - 我沒有足夠的代表處,以註釋添加到所選擇的答案。

花了很多時間試圖爲正則表達式找到正確的語法後,我遇到了這個幫助我的東西。希望它可以幫助別人!

NSString *regexString = @"(?<=v(=|/))([-a-zA-Z0-9_]+)|(?<=youtu.be/)([-a-zA-Z0-9_]+)"; 

摘自here。這適用於以下網址格式:

+3

這對我來說,只是提及你可以訪問範圍編號0來得到它: NSRange videoIDRange = [match rangeAtIndex:0]; NSString * videoID = [message substringWithRange:videoIDRange]; – 2013-02-22 11:56:07

+1

Dis da da cooolest answer evaaa!如果它與dat dima homie的答案相結合 – GangstaGraham 2013-06-19 04:16:40

1

這裏是我的解決方案,將r從StackOveFlow引用。 (Youtube I.D parsing for new URL formats

我做了一些修改。

///這是.H

#import <Foundation/Foundation.h> 
@interface YoutubeParser : NSObject 

+(BOOL) isValidateYoutubeURL:(NSString *)youtubeURL; 

+(NSArray *) parseHTML:(NSString *)html ; 

@end 

///This is the .m 
#import "YoutubeParser.h" 

@interface YoutubeParser() { 
} 

@end 

@implementation YoutubeParser 

#define YOUTUBE_PATTERN @"(https?://)?(www\\.)?(youtu\\.be/|youtube\\.com)?(/|/embed/|/v/|/watch\\?v=|/watch\\?.+&v=)([\\w_-]{11})(&.+)?" 

+(NSRegularExpression *)regex { 

    static NSRegularExpression * regex = nil; 

    regex =  [NSRegularExpression regularExpressionWithPattern:YOUTUBE_PATTERN 
                   options:NSRegularExpressionCaseInsensitive 
                   error:nil]; 
    return regex; 
} 

+(BOOL) isValidateYoutubeURL:(NSString *)youtubeURL { 
    NSInteger cnt = [[YoutubeParser regex] numberOfMatchesInString:youtubeURL options:0 range:NSMakeRange(0, [youtubeURL length]) ]; 

    return cnt > 0 ? YES : NO; 
} 

typedef void (^matching_block_t) (NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop); 

+(NSArray *) parseHTML:(NSString *)html { 
    NSMutableArray * youtubeURLArray = [[NSMutableArray alloc] init]; 

    matching_block_t parseTask = ^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { 
     NSRange matchRange = [result range]; 
     NSRange youtubeKey = [result rangeAtIndex:5]; //the youtube key 
     NSString * strKey = [html substringWithRange:youtubeKey] ; 
     NSLog(@"youtubeKey=%@ , with url=%@ " ,strKey , [html substringWithRange:matchRange]); 

     [youtubeURLArray addObject:strKey]; 
    }; 

    [[YoutubeParser regex] enumerateMatchesInString:html options:0 range:NSMakeRange(0, [html length]) usingBlock:parseTask ]; 

    return youtubeURLArray; 
} 

@end 
7

你甚至不需要正則表達式。以下作品在URL中的視頻ID的長度和位置,無論:

NSString *vID = nil; 
NSString *url = @"http://www.youtube.com/watch?v=cAcqdjLCN7s"; 

NSString *query = [url componentsSeparatedByString:@"?"][1]; 
NSArray *pairs = [query componentsSeparatedByString:@"&"]; 
for (NSString *pair in pairs) { 
    NSArray *kv = [pair componentsSeparatedByString:@"="]; 
    if ([kv[0] isEqualToString:@"v"]) { 
     vID = kv[1]; 
     break; 
    } 
} 

NSLog(@"%@", vID); 
+0

比正則表達式好得多。仍然不會像[youtube-dl]那樣考慮[this](http://stackoverflow.com/questions/11509164/youtube-video-id-from-url-objective-c/17798344#comment15209148_11509927),但是哦。 ;) – Ryan 2013-07-23 19:46:21

+0

@minitech我的原則:如果我們開發buggy黑客,那麼我們至少應該是優雅的:P謝謝。 (順便說一句:如果我正確理解了你的觀點,我的錯誤很容易通過用'?#'字符集中的字符分隔URL來解決......) – 2013-07-23 19:50:58

+0

+1:這隻適用於我... – 2014-06-02 08:09:01

2
- (NSString*)getYoutubeVideoID:(NSString*)url { 
    NSError *error = NULL; 
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=watch\\?v=|/videos/|embed\\/)[^#\\&\\?]*" 
               options:NSRegularExpressionCaseInsensitive 
               error:&error]; 

    NSTextCheckingResult *match = [regex firstMatchInString:url 
                options:0 
                 range:NSMakeRange(0, [url length])]; 
    NSString *substringForFirstMatch; 
    if (match) { 
     NSRange videoIDRange = [match rangeAtIndex:0]; 
     substringForFirstMatch = [url substringWithRange:videoIDRange]; 
    } 
    return substringForFirstMatch; 
} 
2

我使用的最高投票的答案寫一個更好的,更嚴格的正則表達式。

NSString *regex = @"(?:youtube.com.+v[=/]|youtu.be/)([-a-zA-Z0-9_]+)"; 

,然後你可以得到的ID

NSTextCheckingResult *match = [regex firstMatchInString:url 
               options:0 
                range:NSMakeRange(0, [url length])]; 
NSRange videoIDRange = [match rangeAtIndex:1]; 
NSString *youTubeID = [url substringWithRange:videoIDRange]; 
62

這裏是正則表達式它涵蓋這些情況

目標C

- (NSString *)extractYoutubeIdFromLink:(NSString *)link { 
    NSString *regexString = @"((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)"; 
    NSRegularExpression *regExp = [NSRegularExpression regularExpressionWithPattern:regexString 
                      options:NSRegularExpressionCaseInsensitive 
                       error:nil]; 

    NSArray *array = [regExp matchesInString:link options:0 range:NSMakeRange(0,link.length)]; 
    if (array.count > 0) { 
     NSTextCheckingResult *result = array.firstObject; 
     return [link substringWithRange:result.range]; 
    } 
    return nil; 
} 

斯威夫特

func extractYoutubeIdFromLink(link: String) -> String? { 
    let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    guard let regExp = try? NSRegularExpression(pattern: pattern, options: .CaseInsensitive) else { 
     return nil 
    } 
    let nsLink = link as NSString 
    let options = NSMatchingOptions(rawValue: 0) 
    let range = NSRange(location: 0,length: nsLink.length) 
    let matches = regExp.matchesInString(link as String, options:options, range:range) 
    if let firstMatch = matches.first { 
     return nsLink.substringWithRange(firstMatch.range) 
    } 
    return nil 
} 

斯威夫特3

func extractYoutubeIdFromLink(link: String) -> String? { 
    let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    guard let regExp = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) else { 
     return nil 
    } 
    let nsLink = link as NSString 
    let options = NSRegularExpression.MatchingOptions(rawValue: 0) 
    let range = NSRange(location: 0, length: nsLink.length) 
    let matches = regExp.matches(in: link as String, options:options, range:range) 
    if let firstMatch = matches.first { 
     return nsLink.substring(with: firstMatch.range) 
    } 
    return nil 
} 
+0

This works對我來說,謝謝! – 2015-03-06 16:35:26

+1

@ZianChen,歡迎您 – Alex 2015-04-21 14:21:36

+0

爲什麼不讓這些方法變爲靜態?不必使用自己的... – 2016-09-21 15:09:03

3

基於這樣的回答:PHP Regex to get youtube video ID?

我適應一個正則表達式的C/objc/C++字符串,這裏最重要的是,正則表達式沒有得到來自Facebook的視頻或其他服務。 iOS的正則表達式是基於:ICU

NSString *regexString = @"^(?:http(?:s)?://)?(?:www\\.)?(?:m\\.)?(?:youtu\\.be/|youtube\\.com/(?:(?:watch)?\\?(?:.*&)?v(?:i)?=|(?:embed|v|vi|user)/))([^\?&\"'>]+)"; 

NSError *error; 
NSRegularExpression *regex = 
[NSRegularExpression regularExpressionWithPattern:regexString 
              options:NSRegularExpressionCaseInsensitive 
              error:&error]; 
NSTextCheckingResult *match = [regex firstMatchInString:message 
               options:0 
                range:NSMakeRange(0, [message length])]; 

if (match && match.numberOfRanges == 2) { 
    NSRange videoIDRange = [match rangeAtIndex:1]; 
    NSString *videoID = [message substringWithRange:videoIDRange]; 

    return videoID; 
} 

匹配:

不匹配:

0

下面是一個使用@ jt_ik的正則表達式迅速版本:

func extractYoutubeID(youtubeURL: String) -> String { 
    var error: NSError? 
    let pattern: String = "(?<=v(=|/))([-a-zA-Z0-9_]+)|(?<=youtu.be/)([-a-zA-Z0-9_]+)" 
    let regex = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)! 

    if error == nil { 
     if let regexMatch = regex.firstMatchInString(youtubeURL, options: nil, range: NSRange(location: 0, length: youtubeURL.utf16Count)) { 
      return (youtubeURL as NSString).substringWithRange(regexMatch.range) 
     } 
     // Handle no match here 
     return "" 
    } else { 
     // Handle error here 
     println(error?.userInfo) 
     return "" 
    } 
} 
+0

它不適用於包含「vi」的鏈接。 – 2016-10-07 05:30:05

1

這裏有很多很好的答案,但我認爲它可能會b e有益於從字符串解析多個video_ID。這可能是一個網頁或一個不同的URL的數組。頁面內容

NSString *content = @"http://www.youtube.com/user/Scobleizer#p/u/1/1p3vcRhsYGo,http://youtu.be/NLqAF9hrVbY,http://www.youtube.com/watch?v=NLqAF9hrVbY,http://facebook.com,http://www.youtube.com/watch?v=cAcqdjLCN7s"; 

方法

-(NSArray *)extractVideos:(NSString *)content { 
    NSString *extractRegex = @"(?<=v(=|/))([-a-zA-Z0-9_]+)|(?<=youtu.be/)([-a-zA-Z0-9_]+)" 
    NSMutableArray *extractedContent = [[NSMutableArray alloc] init]; 
    if ([content hasPrefix:@"http://"] || [content hasPrefix:@"https://"]) { 
     NSURL *extractURL = [NSURL URLWithString:content]; 
     if ([extractURL.host rangeOfString:@"youtu"].location != NSNotFound) { 
      NSRegularExpression *extractRegex = [NSRegularExpression regularExpressionWithPattern:extractRegex options:NSRegularExpressionCaseInsensitive error:nil]; 
      NSArray *extractResults = [extractRegex matchesInString:content options:0 range:NSMakeRange(0, content.length)]; 
      for (NSTextCheckingResult *match in extractResults) { 
       [extractedContent addObject:[content substringWithRange:match.range]]; 

      } 

     } 

    } 

    return extractedContent; 

} 

輸出

( 
    NLqAF9hrVbY, 
    QLqAF9eeVbY, 
    cAcqdjLCN7s 
) 

感謝@Alex的正則表達式

+0

謝謝你的詳細答案,但我一直無法弄清楚什麼是YT_ID_EXTRACT_REGEX – Rimon 2015-06-28 07:29:30

+0

沒關係,搞清楚了。對於其他人也想知道,只需添加 '#define YT_ID_EXTRACT_REGEX @「(?<= v(= | /))([ - a-zA-Z0-9 _] +)|(?<= youtu.be/) ([ - a-zA-Z0-9 _] +]「'而不是這行'NSString * extractRegex = @」(?<= v(= | /))([ - a-zA- Z0-9 _] +)|(?<= youtu.be/)([-a-zA-Z0-9_]+)「' – Rimon 2015-06-28 07:33:25

+0

@RimonRagaie抱歉代碼中存在錯誤,現在我改變了它,但有點太晚了。希望答案的其餘部分有所幫助。 – 2015-06-28 17:05:59

1

合併一些你的答案,我d說這是bes噸答案:

+ (NSString *)extractYoutubeID:(NSString *)youtubeURL 
{ 
    NSError *error = NULL; 
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=v(=|/))([-a-zA-Z0-9_]+)|(?<=youtu.be/)([-a-zA-Z0-9_]+)" 
                      options:NSRegularExpressionCaseInsensitive 
                      error:&error]; 
    NSRange rangeOfFirstMatch = [regex rangeOfFirstMatchInString:youtubeURL options:NSMatchingReportProgress range:NSMakeRange(0, [youtubeURL length])]; 
    if(rangeOfFirstMatch.location != NSNotFound) { 
     NSString *substringForFirstMatch = [youtubeURL substringWithRange:rangeOfFirstMatch]; 

     return substringForFirstMatch; 
    } 

    return nil; 
} 
1

夫特2版本@Alex答案

func getYoutubeVideoId(youtubeLink:String) -> String?{ 
    var youtubeId:String? = nil 
    let pattern: String = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    do { 
     let regex = try NSRegularExpression(pattern: pattern, options: .CaseInsensitive) 
     if let regexMatch = regex.firstMatchInString(youtubeLink, options: NSMatchingOptions(rawValue: 0), range: NSRange(location: 0, length: youtubeLink.characters.count)) { 
      youtubeId = (youtubeLink as NSString).substringWithRange(regexMatch.range) 
     } 
    } 
    catch let error as NSError{ 
     print("Error while extracting youtube id \(error.debugDescription)") 
    } 

    return youtubeId 
} 
0

夫特3版本@Alex答案

func extractYoutubeIdFromLink(link: String) -> String? { 
    let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    guard let regExp = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) else { 
     return nil 
    } 
    let nsLink = link as NSString 
    let options = NSRegularExpression.MatchingOptions(rawValue: 0) 
    let range = NSRange(location: 0,length: nsLink.length) 
    let matches = regExp.matches(in: link as String, options:options, range:range) 
    if let firstMatch = matches.first { 
     debugPrint(firstMatch) 
     return nsLink.substring(with: firstMatch.range) 
    } 
    return nil 
} 
1

更新夫特4:

static func extractYoutubeVideoId(from url: String) -> String? { 
    let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    guard let range = url.range(of: pattern, options: .regularExpression) else { return nil } 
    return String(url[range]) 
} 

老答案:的
有點更快的方式@亞歷克斯的斯威夫特3回答W/O使用的NSString的:
我們可以強制嘗試正則表達式,因爲我們知道它是有效的。

static func extractYoutubeVideoId(from url: String) -> String? { 
    let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    let regex = try! NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) 
    let range = NSRange(location: 0, length: url.utf16.count) 
    guard let firstMatch = regex.firstMatch(in: url, options: .init(rawValue: 0), range: range) else { return nil } 
    let start = String.UTF16Index(firstMatch.range.location) 
    let end = String.UTF16Index(firstMatch.range.location + firstMatch.range.length) 
    return String(url.utf16[start..<end]) 
} 

或者,如果你仍然想的NSString:

static func extractYoutubeVideoId(from url: String) -> String? { 
    let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)" 
    let regex = try! NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) 
    let range = NSRange(location: 0, length: (url as NSString).length) 
    guard let firstMatch = regex.firstMatch(in: url, options: .init(rawValue: 0), range: range) else { return nil } 
    return (url as NSString).substring(with: firstMatch.range) 
}