Як канвертаваць працягласць API YouTube у секундах?

Дзеля цікавасці я хачу, каб пераўтварыць відэа працягласцей YouTubes ISO 8601 у секундах. Для далейшага доказы майго рашэння, я выбраў вельмі доўгі відэа , каб праверыць яго супраць.

The API provides this for its duration - "duration": "P1W2DT6H21M32S"

Я паспрабаваў разбор гэтай працягласці з dateutil , як прапанавана ў stackoverflow.com/questions/969285 .

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

Гэта кідае выключэнне

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

Што мне не хапае?

9

6 адказы

Python убудаваная ў dateutil модуля падтрымлівае толькі разбор ISO 8601 даты, а не ISO 8601 працягласцей. Для гэтага, вы можаце выкарыстоўваць «ISODate» бібліятэку (у PyPI на https://pypi.python.org/ PyPI/ISODate - усталяваць праз Піп або easy_install). Гэтая бібліятэка мае поўную падтрымку ISO 8601 працягласцей, ператвараючы іх у datetime.timedelta аб'ектаў. Таму, як толькі вы імпартавалі бібліятэку, гэта так проста, як:

dur=isodate.parse_duration('P1W2DT6H21M32S')
print dur.total_seconds()
15
дададзена
нічога сабе, мяркуючы, што я напісаў свой уласны парсер, гэта проста робіць усё гэта здаецца так проста :) дзякуй!
дададзена аўтар Morgan Wilde, крыніца
@MorganWilde Адна з вялікіх рэчаў аб пітона з'яўляецца тое, што звычайна можна знайсці рашэньне, калі не ў стандартнай бібліятэцы, а затым на PyPI. Гэта добрая практыка, каб паспрабаваць пазбегнуць рэалізацыі нічога, калі рашэнне ўжо існуе (на любой мове, а не толькі Python).
дададзена аўтар zstewart, крыніца

Працуе на пітон 2.7+. Прынята з JavaScript адзін- укладыш для v3 пытанне Youtube .

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 выкарыстоўваецца да гадзін

4
дададзена

Ня відэа 1 тыдзень, 2 дня, 6 гадзіне 21 хвілін 32 секунд даўжынёй?

Youtube паказвае яго як 222 гадзін 21 хвілін 17 секунд; 1 * 7 * 24 + 2 * 24 + 6 = 222. я не ведаю, дзе 17 секунд супраць 32 секунд разыходжанне прыходзіць, хоць; можа таксама быць памылкі акруглення.

На мой погляд, пісаць парсер для гэтага не так ужо цяжка. На жаль, dateutil , здаецца, не разабраць інтэрвалы, толькі даты-часу кропак.

Update:

Я бачу, што ёсць пакет для гэтага, але так жа, як прыклад назваў паведамленняў ўлады, сцісласці і незразумелага сінтаксісу, вось парсер для вас:

import re

# see http://en.wikipedia.org/wiki/ISO_8601#Durations
ISO_8601_period_rx = re.compile(
    'P'   # designates a period
    '(?:(?P\d+)Y)?'   # years
    '(?:(?P\d+)M)?'  # months
    '(?:(?P\d+)W)?'   # weeks
    '(?:(?P\d+)D)?'    # days
    '(?:T' # time part must begin with a T
    '(?:(?P\d+)H)?'   # hourss
    '(?:(?P\d+)M)?' # minutes
    '(?:(?P\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}

Я наўмысна не буду разлік дакладнай колькасці секунд з гэтых дадзеных тут. Гэта выглядае трывіяльным (гл вышэй), але на самой справе гэта не так. Напрыклад, адлегласць ад 2-х месяцаў з 1 студзеня складае 58 дзён (30 + 28) або 59 (30 + 29), у залежнасці ад года, а з 1-га сакавіка ён заўсёды 61 дзён. Належная рэалізацыя календара павінна прыняць усе гэта пад увагу; для разліку даўжыні заціску Youtube, ён павінен быць празмерным.

2
дададзена
здаецца, што гэта самае лепшае рашэнне да гэтага часу :)
дададзена аўтар Morgan Wilde, крыніца
Праверце, як жахліва маё рашэнне ...: D Краткость, безумоўна, пазбягае майго дасціпнасці ...
дададзена аўтар Morgan Wilde, крыніца
Выдатны! Гэта тое, што я кажу пра :) гэта адзін доўгі рэгулярны выраз ... Адна думка - калі YT вызначае тыдня гэта фармат працягласці, ня што азначае, што <�б> месяцаў і <�б> гадоў </б > не будзе забяспечана, так як вы думаеце, што гэта будуць ўзаемавыключальнымі?
дададзена аўтар Morgan Wilde, крыніца
Я проста зразумеў, што вы не выканалі пераўтварэнне ў секунды, як 5 дадатковых лініі прама там :)
дададзена аўтар Morgan Wilde, крыніца
@MorganWilde: ну, праверыць маё ў абноўленым адказе. Я не заўсёды пішу канчатковыя аўтаматы, але калі я раблю, я стараюся выкарыстоўваць добра вядомы прадметна-арыентаваная мова :)
дададзена аўтар 9000, крыніца

Вось мой адказ, які прымае рашэнне рэгулярных выразаў 9000 'S (дзякуй - дзіўнае майстэрства рэгулярных выразаў!) І заканчвае працу для 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\d+)Y)?'   # years
        '(?:(?P\d+)M)?'  # months
        '(?:(?P\d+)W)?'   # weeks
        '(?:(?P\d+)D)?'    # days
        '(?:T' # time part must begin with a T
        '(?:(?P\d+)H)?'   # hours
        '(?:(?P\d+)M)?' # minutes
        '(?:(?P\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])

Выбачайце за не размяшчэнне вышэй - да гэтага часу тут новы і не дастаткова ачкоў рэпутацыі, каб дадаваць каментары.

2
дададзена

Гэта працуе шляхам разбору ўваходных радкі 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
1
дададзена

Дык вось што я прыдумаў - карыстацкі сінтаксічны аналізатар, каб інтэрпрэтаваць час:

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

Цяпер, верагодна, гэта супер, не крута і гэтак далей, але гэта працуе, так што я дзялю, таму што я клапачуся пра цябе людзі.

0
дададзена