【问题标题】:How to convert YouTube API duration to seconds?如何将 YouTube API 持续时间转换为秒?
【发布时间】:2013-05-20 11:49:11
【问题描述】:

为了感兴趣,我想将视频时长从 YouTube ISO 8601 转换为秒。为了将来证明我的解决方案,我选择了a really long video 对其进行测试。

API 在其持续时间内提供此功能 - "duration": "P1W2DT6H21M32S"

我尝试按照stackoverflow.com/questions/969285 中的建议使用dateutil 解析此持续时间。

import dateutil.parser
duration = = dateutil.parser.parse('P1W2DT6H21M32S')

这会引发异常

TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int'

我错过了什么?

【问题讨论】:

    标签: python youtube-api


    【解决方案1】:

    扩展 StanleyZheng 的答案...不需要 _js_parseInt 函数。

    import re
    
    def YTDurationToSeconds(duration):
        match = re.match('PT((\d+)H)?((\d+)M)?((\d+)S)?', duration).groups()
        hours = int(match[1]) if match[1] else 0
        minutes = int(match[3]) if match[3] else 0
        seconds = int(match[5]) if match[5] else 0
        return hours * 3600 + minutes * 60 + seconds
    
    # example output 
    YTDurationToSeconds('PT15M33S')
    # 933
    

    【讨论】:

      【解决方案2】:

      Python 的内置 dateutil 模块仅支持解析 ISO 8601 日期,不支持解析 ISO 8601 持续时间。为此,您可以使用“isodate”库(在 pypi 中 https://pypi.python.org/pypi/isodate -- 通过 pip 或 easy_install 安装)。该库完全支持 ISO 8601 持续时间,将它们转换为 datetime.timedelta 对象。所以一旦你导入了这个库,它就像这样简单:

      import isodate
      dur = isodate.parse_duration('P1W2DT6H21M32S')
      print(dur.total_seconds())
      

      【讨论】:

      • 哇,考虑到我编写了自己的解析器,这让一切看起来如此简单:) 谢谢!
      • @MorganWilde python 的一大优点是你通常可以找到一个现有的解决方案,如果不在标准库中,那么在 pypi 上。如果解决方案已经存在(任何语言,而不仅仅是 python),尽量避免实现任何东西是一个好习惯。
      【解决方案3】:

      9000's answer 上扩展,显然 Youtube 的格式是使用周而不是月,这意味着可以轻松计算总秒数。
      这里没有使用命名组,因为我最初需要它来使用 PySpark。

      from operator import mul
      from itertools import accumulate
      import re
      from typing import Pattern, List
      
      SECONDS_PER_SECOND: int = 1
      SECONDS_PER_MINUTE: int = 60
      MINUTES_PER_HOUR: int = 60
      HOURS_PER_DAY: int = 24
      DAYS_PER_WEEK: int = 7
      WEEKS_PER_YEAR: int = 52
      
      ISO8601_PATTERN: Pattern = re.compile(
          r"P(?:(\d+)Y)?(?:(\d+)W)?(?:(\d+)D)?"
          r"T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
      )
      
      def extract_total_seconds_from_ISO8601(iso8601_duration: str) -> int:
          """Compute duration in seconds from a Youtube ISO8601 duration format. """
          MULTIPLIERS: List[int] = (
              SECONDS_PER_SECOND, SECONDS_PER_MINUTE, MINUTES_PER_HOUR,
              HOURS_PER_DAY, DAYS_PER_WEEK, WEEKS_PER_YEAR
          )
          groups: List[int] = [int(g) if g is not None else 0 for g in
                    ISO8601_PATTERN.match(iso8601_duration).groups()]
      
          return sum(g * multiplier for g, multiplier in
                     zip(reversed(groups), accumulate(MULTIPLIERS, mul)))
      

      【讨论】:

        【解决方案4】:

        这是我的答案,它采用了9000 的正则表达式解决方案(感谢您 - 对正则表达式的惊人掌握!)并完成了原始海报的 YouTube 用例的工作,即将小时、分钟和秒转换为秒。我使用了.groups() 而不是.groupdict(),然后是几个精心构建的列表推导式。

        import re
        
        def yt_time(duration="P1W2DT6H21M32S"):
            """
            Converts YouTube duration (ISO 8061)
            into Seconds
        
            see http://en.wikipedia.org/wiki/ISO_8601#Durations
            """
            ISO_8601 = re.compile(
                'P'   # designates a period
                '(?:(?P<years>\d+)Y)?'   # years
                '(?:(?P<months>\d+)M)?'  # months
                '(?:(?P<weeks>\d+)W)?'   # weeks
                '(?:(?P<days>\d+)D)?'    # days
                '(?:T' # time part must begin with a T
                '(?:(?P<hours>\d+)H)?'   # hours
                '(?:(?P<minutes>\d+)M)?' # minutes
                '(?:(?P<seconds>\d+)S)?' # seconds
                ')?')   # end of time part
            # Convert regex matches into a short list of time units
            units = list(ISO_8601.match(duration).groups()[-3:])
            # Put list in ascending order & remove 'None' types
            units = list(reversed([int(x) if x != None else 0 for x in units]))
            # Do the maths
            return sum([x*60**units.index(x) for x in units])
        

        很抱歉没有发布更高的帖子 - 这里还是新的,没有足够的声望点来添加 cmets。

        【讨论】:

          【解决方案5】:

          这通过一次解析输入字符串 1 个字符来工作,如果字符是数字,它只是将它(字符串添加,而不是数学添加)添加到正在解析的当前值。 如果它是“wdhms”之一,则将当前值分配给适当的变量(周、日、小时、分钟、秒),然后将值重置以准备取下一个值。 最后,它将 5 个解析值的秒数相加。

          def ytDurationToSeconds(duration): #eg P1W2DT6H21M32S
              week = 0
              day  = 0
              hour = 0
              min  = 0
              sec  = 0
          
              duration = duration.lower()
          
              value = ''
              for c in duration:
                  if c.isdigit():
                      value += c
                      continue
          
                  elif c == 'p':
                      pass
                  elif c == 't':
                      pass
                  elif c == 'w':
                      week = int(value) * 604800
                  elif c == 'd':
                      day = int(value)  * 86400
                  elif c == 'h':
                      hour = int(value) * 3600
                  elif c == 'm':
                      min = int(value)  * 60
                  elif c == 's':
                      sec = int(value)
          
                  value = ''
          
              return week + day + hour + min + sec
          

          【讨论】:

            【解决方案6】:

            适用于 python 2.7+。来自JavaScript one-liner for Youtube v3 question here

            import re
            
            def YTDurationToSeconds(duration):
              match = re.match('PT(\d+H)?(\d+M)?(\d+S)?', duration).groups()
              hours = _js_parseInt(match[0]) if match[0] else 0
              minutes = _js_parseInt(match[1]) if match[1] else 0
              seconds = _js_parseInt(match[2]) if match[2] else 0
              return hours * 3600 + minutes * 60 + seconds
            
            # js-like parseInt
            # https://gist.github.com/douglasmiranda/2174255
            def _js_parseInt(string):
                return int(''.join([x for x in string if x.isdigit()]))
            
            # example output 
            YTDurationToSeconds(u'PT15M33S')
            # 933
            

            处理 iso8061 持续时间格式以使 Youtube 使用时间长达数小时

            【讨论】:

              【解决方案7】:

              视频不是 1 周 2 天 6 小时 21 分 32 秒长吗?

              Youtube 显示为 222 小时 21 分 17 秒; 1 * 7 * 24 + 2 * 24 + 6 = 222。不过,我不知道 17 秒与 32 秒的差异来自哪里;也可以是舍入误差。

              在我看来,为此编写解析器并不难。不幸的是,dateutil 似乎没有解析间隔,只解析日期时间点。

              更新:

              我看到有一个包可以解决这个问题,但作为正则表达式强大、简洁和难以理解的语法的一个例子,这里有一个解析器:

              import re
              
              # see http://en.wikipedia.org/wiki/ISO_8601#Durations
              ISO_8601_period_rx = re.compile(
                  'P'   # designates a period
                  '(?:(?P<years>\d+)Y)?'   # years
                  '(?:(?P<months>\d+)M)?'  # months
                  '(?:(?P<weeks>\d+)W)?'   # weeks
                  '(?:(?P<days>\d+)D)?'    # days
                  '(?:T' # time part must begin with a T
                  '(?:(?P<hours>\d+)H)?'   # hourss
                  '(?:(?P<minutes>\d+)M)?' # minutes
                  '(?:(?P<seconds>\d+)S)?' # seconds
                  ')?'   # end of time part
              )
              
              
              from pprint import pprint
              pprint(ISO_8601_period_rx.match('P1W2DT6H21M32S').groupdict())
              
              # {'days': '2',
              #  'hours': '6',
              #  'minutes': '21',
              #  'months': None,
              #  'seconds': '32',
              #  'weeks': '1',
              #  'years': None}
              

              我故意不在此处从这些数据中计算确切的秒数。它看起来微不足道(见上文),但实际上并非如此。例如,从 1 月 1 日起 2 个月的距离是 58 天 (30+28) 或 59 (30+29),具体取决于年份,而从 3 月 1 日起始终是 61 天。适当的日历实施应该考虑到所有这些;对于 Youtube 剪辑长度计算,它必须是过多的。

              【讨论】:

              • 这似乎是迄今为止最好的解决方案:)
              • 看看我的解决方案有多糟糕...... :D 简洁肯定让我无法理解......
              • @MorganWilde:好吧,在更新的答案中查看我的。我并不总是编写有限自动机,但当我这样做时,我会尝试使用一种众所周知的特定于领域的语言:)
              • 可爱!这就是我要说的 :) 那是一个很长的正则表达式......一个想法 - 如果 YT 以它的持续时间格式定义周,这并不意味着 monthsyears 不会提供,因为您认为这些会相互排斥?
              • 我刚刚意识到你没有实现秒数的转换,就像那里多出了 5 行 :)
              【解决方案8】:

              所以这就是我想出的 - 一个自定义解析器来解释时间:

              def durationToSeconds(duration):
                  """
                  duration - ISO 8601 time format
                  examples :
                      'P1W2DT6H21M32S' - 1 week, 2 days, 6 hours, 21 mins, 32 secs,
                      'PT7M15S' - 7 mins, 15 secs
                  """
                  split   = duration.split('T')
                  period  = split[0]
                  time    = split[1]
                  timeD   = {}
              
                  # days & weeks
                  if len(period) > 1:
                      timeD['days']  = int(period[-2:-1])
                  if len(period) > 3:
                      timeD['weeks'] = int(period[:-3].replace('P', ''))
              
                  # hours, minutes & seconds
                  if len(time.split('H')) > 1:
                      timeD['hours'] = int(time.split('H')[0])
                      time = time.split('H')[1]
                  if len(time.split('M')) > 1:
                      timeD['minutes'] = int(time.split('M')[0])
                      time = time.split('M')[1]    
                  if len(time.split('S')) > 1:
                      timeD['seconds'] = int(time.split('S')[0])
              
                  # convert to seconds
                  timeS = timeD.get('weeks', 0)   * (7*24*60*60) + \
                          timeD.get('days', 0)    * (24*60*60) + \
                          timeD.get('hours', 0)   * (60*60) + \
                          timeD.get('minutes', 0) * (60) + \
                          timeD.get('seconds', 0)
              
                  return timeS
              

              现在它可能是超级不酷等等,但它有效,所以我分享是因为我关心你们。

              【讨论】:

                猜你喜欢
                • 2016-11-05
                • 2013-10-04
                • 1970-01-01
                • 2020-05-03
                • 1970-01-01
                • 1970-01-01
                • 2021-08-05
                • 1970-01-01
                • 2014-04-04
                相关资源
                最近更新 更多