2012/01/22

[python] 鳳凰網站自動抓視頻

檢視html 原始碼,是看不出任何mp4或flv的link。
經過Firefox CacheViewer以及 Firefox Video Download Helper調查,原理大致如下:
它是先餵給 flash player 一組 guid 號碼。 flash player 依此guid下載一份xml檔案,在xml 檔案中有mp4或flv 的 link。

[python]用eyeD3 + xml.dom.minidom 給mp3 自動標名,支援iTunes

毛豆工作室下載下來的「開卷八分鐘」的mp3們,沒有 idv3 的 title,沒有lyrics,把它們放入iTunes再下載到 iPhone的話,這樣不方便。

毛豆工作室有xml 可以下載,先用 xml.dom.minidom 分析取出 fileName + title + description ,之後用 eyeD3 (python 模組) 將mp3 加tag。

總共有一千多個mp3,很難確保第一次就寫程式就OK,保險點是第一步驟的結果先暫存在 txt 檔,之後再讀 txt檔用 eyeD3。或是先對一兩個mp3試驗,之後再擴展之。再者,從xml中抽取出的description裡面有時夾雜不想要的廣告文字或html控制碼,先暫存輸出到txt檔,各別修改之。這樣子做至少比在程式一開始寫時就全盤考慮來得簡單、有效率的多了。現在的text editor的 find/replace都很強大,有regular expression功能,做起來不比用python程式遜色。

這個xml 結構是有兩百多個item,每個item描述每個mp3,每個mp3有 title、description、link等 nodes。
dom1 = xml.dom.minidom.parse('8minsreading_2010.xml')
items = dom1.getElementsByTagName('item')
for item in items:
    title = item.getElementsByTagName('title')[0].\
            firstChild.nodeValue
    link = item.getElementsByTagName('link')[0].\
            firstChild.nodeValue
    fileName = link.split('/')[-1].split('.')[0]
    description = item.getElementsByTagName('description')\
            [0].firstChild.nodeValue


Unicode 簡体中文的問題:第一個問題是python在print到 stdio的時候,簡中會變成亂碼(文字化け),就算用了
sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
偶爾某些印出還是亂碼,所以不要太在意console output,還是先印出到暫存的 utf-8 text 檔。

第二個問題是 iTunes 用的IDv3 是 eyeD3.ID3_V2_3 ,是UCS-2 little endian,也就是前綴要加上
tag.encoding='\x01'
不保險的話,要對tag的每個frame.encoding都設好
for frame in tag.frames: frame.encoding='\x01'

為了要讓下載到iPhone的mp3,在聆聽時點一下螢幕會出現歌詞。這個mp3必須要加一個tag 叫「UNSYNCED LYRICS」。從foobar2000上看到tag名是 UNSYNCED LYRICS,但其實對eyeD3只要一個簡單的addLyrics即可:
tag.addLyrics(description)

以下python code全文,是邊做邊改邊試的,只是為了一次性工作而做的。你可以取出copy paste對你有幫助的code片段,比如 xml 的讀取,eyeD3 tag的寫入。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os, sys, codecs, glob, re
import xml.dom.minidom
from xml.dom.minidom import parse
import eyeD3

sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)

def read_xml():
    xmlFileName = '8minsreading_2010.xml'
    dom1 = parse(xmlFileName)

    txtFileName = xmlFileName.split('.')[0] + '.txt'
    fout = codecs.open(txtFileName, mode='w', \
            encoding='utf-8')

    items = dom1.getElementsByTagName('item')
    for item in items:
        title = item.getElementsByTagName('title')[0].\
                firstChild.nodeValue
        link = item.getElementsByTagName('link')[0].\
                firstChild.nodeValue
        fileName = link.split('/')[-1].split('.')[0]
        description = item.getElementsByTagName(\
                'description')[0].firstChild.nodeValue
        description = description.split(' ')[0]
        description = description.strip()
        fout.write(fileName + '\t' + title + '\t' + \
                description + '\n')

    fout.close()

def sum_all():
    pat = re.compile(r'\d\d\d\d\d\d\t')
    table = {}
    txtFiles = ['8minsreading_2011.txt',
            '8minsreading_2010.txt',
            '8minsreading_2009.txt',
            '8minsreading_2008.txt',
            '8minsreading_2007.txt']
    for txtFile in txtFiles:
        fin = codecs.open(txtFile, mode='r',
                encoding='utf-8')
        for line in fin:
            line = line.strip()
            if not line:
                continue
            if pat.match(line):
                comps = line.split('\t')
                fileTitle = comps[0]
                title = comps[1]
                description = ''.join(comps[2:])
                table[fileTitle] = (fileTitle, title,
                        description)
            else:
                (fileTitle, title, description) = \
                        table[fileTitle]
                description += line
                table[fileTitle] = (fileTitle, title,
                        description)
        fin.close()

    fout = codecs.open('all.txt', mode='w',
            encoding='utf-8')
    keys = table.keys()
    keys.sort()
    for k in keys:
        fileTitle, title, description = table[k]
        fout.write( fileTitle+'\t'+ title +'\t'+
                description +'\n' )
    fout.close()

table = {}
fin = codecs.open('all.txt', mode='r', encoding='utf-8')
for line in fin:
    comps = line.split('\t')
    fileTitle, title = comps[0], comps[1]
    description = ''.join(comps[2:])
    table[fileTitle] = (title, description)
fin.close()

mp3Files = glob.glob('*.mp3')
for mp3File in mp3Files:
    fileTitle = mp3File.split('.')[0]
    title, description = table[fileTitle]
    tag = eyeD3.Tag()
    tag.link(mp3File)
    tag.setVersion(eyeD3.ID3_V2_3)
    tag.encoding='\x01'
    tag.setTitle(fileTitle+title )
    tag.setAlbum(u'開卷八分鐘')
    tag.setArtist(u'梁文道')
    tag.addLyrics(description)
    for frame in tag.frames: frame.encoding='\x01'
    tag.update()