用Python將音頻內(nèi)容轉(zhuǎn)換為文本格式
當(dāng)對(duì)一個(gè)或多個(gè)人的談話(huà)進(jìn)行記錄時(shí),采用一種高度準(zhǔn)確和自動(dòng)化的方式將口語(yǔ)提取為文本非常有用。轉(zhuǎn)換成文字后,便可以將其用于進(jìn)一步分析或用作其他功能。
在本教程中,我們將使用稱(chēng)為AssemblyAI(https://www.assemblyai.com/)的高精度語(yǔ)音轉(zhuǎn)文本W(wǎng)eb API從MP3錄音中提取文本(也支持許多其他格式)。
在本教程中,音頻文件示例下載地址請(qǐng)掃描本文下方二維碼添加Python小助手獲取,下面是音頻輸出如下所示的高精度文本轉(zhuǎn)錄內(nèi)容:
- An object relational mapper is a code library that automates the transfer of
 - data stored in relational, databases into objects that are more commonly used
 - in application code or EMS are useful because they provide a high level
 - abstraction upon a relational database that allows developers to write Python
 - code instead of sequel to create read update and delete, data and schemas in
 - their database. Developers can use the programming language. They are
 - comfortable with to work with a database instead of writing SQL...
 
教程要求
在本教程中,我們將使用以下依賴(lài)項(xiàng),稍后將安裝它們。請(qǐng)確保您的環(huán)境中還安裝了Python 3,最好安裝3.6或更高版本:
我們將使用以下依賴(lài)關(guān)系來(lái)完成本教程:
- requests 2.24.0 來(lái)向AssemblyAI語(yǔ)音文本API發(fā)出HTTP請(qǐng)求
 - 一個(gè) AssemblyAI 帳戶(hù),您可以在此處(https://app.assemblyai.com/login/)注冊(cè)免費(fèi)的API訪(fǎng)問(wèn)密鑰
 
本文所有代碼下載地址請(qǐng)掃描本文下方二維碼添加Python小助手獲取。
搭建開(kāi)發(fā)環(huán)境
轉(zhuǎn)到保存Python虛擬環(huán)境的目錄。我將我的目錄保存在用戶(hù)主目錄下的venvs子目錄中。使用以下命令為此項(xiàng)目創(chuàng)建一個(gè)新的virtualenv。
- python3 -m venv ~/venvs/pytranscribe
 
用 shell 命令激活 virtualenv:
- source ~/venvs/pytranscribe/bin/activate
 
執(zhí)行上述命令后,命令提示符將發(fā)生更改,因此virtualenv的名稱(chēng)將以原始命令提示符格式開(kāi)頭,如果您的提示符只是$,則其外觀如下所示:
- (pytranscribe) $
 
請(qǐng)記住,您必須在每個(gè) virtualenv 中使用依賴(lài)項(xiàng)的新終端窗口中激活您的 virtualenv 。
現(xiàn)在,我們可以將請(qǐng)求包安裝到已激活但為空的 virtualenv 中。
- pip install requests==2.24.0
 
查找類(lèi)似于以下內(nèi)容的輸出,以確認(rèn)從PyPI正確安裝了相應(yīng)的軟件包。
- (pytranscribe) $ pip install requests==2.24.0
 - Collecting requests==2.24.0
 - Using cached https://files.pythonhosted.org/packages/45/1e/0c169c6a5381e241ba7404532c16a21d86ab872c9bed8bdcd4c423954103/requests-2.24.0-py2.py3-none-any.whl
 - Collecting certifi>=2017.4.17 (from requests==2.24.0)
 - Using cached https://files.pythonhosted.org/packages/5e/c4/6c4fe722df5343c33226f0b4e0bb042e4dc13483228b4718baf286f86d87/certifi-2020.6.20-py2.py3-none-any.whl
 - Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests==2.24.0)
 - Using cached https://files.pythonhosted.org/packages/9f/f0/a391d1463ebb1b233795cabfc0ef38d3db4442339de68f847026199e69d7/urllib3-1.25.10-py2.py3-none-any.whl
 - Collecting chardet<4,>=3.0.2 (from requests==2.24.0)
 - Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
 - Collecting idna<3,>=2.5 (from requests==2.24.0)
 - Using cached https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl
 - Installing collected packages: certifi, urllib3, chardet, idna, requests
 - Successfully installed certifi-2020.6.20 chardet-3.0.4 idna-2.10 requests-2.24.0 urllib3-1.25.10
 
我們已經(jīng)安裝了所有必需的依賴(lài)項(xiàng),因此我們可以開(kāi)始對(duì)應(yīng)用程序進(jìn)行編碼。
上傳、啟動(dòng)和轉(zhuǎn)錄音頻
我們已完成開(kāi)始構(gòu)建應(yīng)用程序所需的一切,該應(yīng)用程序會(huì)將音頻轉(zhuǎn)換為文本。我們將在三個(gè)文件中構(gòu)建此應(yīng)用程序:
1、upload_audio_file.py:將您的音頻文件上傳到AssemblyAI服務(wù)上的安全位置,以便可以進(jìn)行處理。如果您的音頻文件已經(jīng)可以通過(guò)公共URL訪(fǎng)問(wèn),則無(wú)需執(zhí)行此步驟,只需按照此快速入門(mén)(https://docs.assemblyai.com/overview/getting-started)
2、initial_transcription.py:告訴API要轉(zhuǎn)錄并立即啟動(dòng)的文件
3、get_transcription.py:如果仍在處理轉(zhuǎn)錄,則顯示轉(zhuǎn)錄狀態(tài),或者在處理完成后顯示轉(zhuǎn)錄結(jié)果
創(chuàng)建一個(gè)名為pytranscribe的新目錄,以在我們編寫(xiě)文件時(shí)存儲(chǔ)這些文件。然后轉(zhuǎn)到新的項(xiàng)目目錄。
- mkdir pytranscibe
 - cd pytranscribe
 
我們還需要將 AssemblyAI API 密鑰導(dǎo)出為環(huán)境變量。注冊(cè) AssemblyAI 帳戶(hù)并登錄 AssemblyAI 儀表板,然后復(fù)制“您的API token”,如以下屏幕截圖所示:
- export ASSEMBLYAI_KEY=your-api-key-here
 
請(qǐng)注意,必須每個(gè)命令行窗口中使用 export 命令以保證此密鑰可訪(fǎng)問(wèn)。如果您沒(méi)有在運(yùn)行腳本的環(huán)境中將標(biāo)記導(dǎo)出為 ASSEMBLYAI_KEY,則我們正在編寫(xiě)的腳本將無(wú)法訪(fǎng)問(wèn)API。
現(xiàn)在我們已經(jīng)創(chuàng)建了項(xiàng)目目錄并將API密鑰設(shè)置為環(huán)境變量,讓我們繼續(xù)編寫(xiě)第一個(gè)文件的代碼,該文件會(huì)將音頻文件上傳到AssemblyAI服務(wù)。
上傳音頻文件并進(jìn)行轉(zhuǎn)錄
創(chuàng)建一個(gè)名為upload_audio_file.py的新文件,并將以下代碼放入其中:
- import argparse
 - import os
 - import requests
 - API_URL = "https://api.assemblyai.com/v2/"
 - def upload_file_to_api(filename):
 - """Checks for a valid file and then uploads it to AssemblyAI
 - so it can be saved to a secure URL that only that service can access.
 - When the upload is complete we can then initiate the transcription
 - API call.
 - Returns the API JSON if successful, or None if file does not exist.
 - """
 - if not os.path.exists(filename):
 - return None
 - def read_file(filename, chunk_size=5242880):
 - with open(filename, 'rb') as _file:
 - while True:
 - data = _file.read(chunk_size)
 - if not data:
 - break
 - yield data
 - headers = {'authorization': os.getenv("ASSEMBLYAI_KEY")}
 - response = requests.post("".join([API_URL, "upload"]), headersheaders=headers,
 - data=read_file(filename))
 - return response.json()
 
上面的代碼導(dǎo)入了argparse,os和request軟件包,以便我們可以在此腳本中使用它們。API_URL是一個(gè)常量,具有AssemblyAI服務(wù)的基本URL。我們使用單個(gè)參數(shù)定義upload_file_to_api函數(shù),filename應(yīng)該是一個(gè)字符串,其中包含文件及其文件名的絕對(duì)路徑。
在函數(shù)中,我們檢查文件是否存在,然后使用Request的分塊傳輸編碼將大文件流式傳輸?shù)紸ssemblyAI API。
os模塊的getenv函數(shù)讀取使用帶有g(shù)etenv的export命令在命令行上設(shè)置的API。確保在運(yùn)行此腳本的終端中使用該導(dǎo)出命令,否則ASSEMBLYAI_KEY值將為空白。如有疑問(wèn),請(qǐng)使用echo $ ASSEMBLY_AI查看該值是否與您的API密鑰匹配。
要使用upload_file_to_api函數(shù),請(qǐng)將以下代碼行添加到upload_audio_file.py文件中,以便我們可以正確地將此代碼作為使用python命令調(diào)用的腳本執(zhí)行:
- if __name__ == "__main__":
 - parser = argparse.ArgumentParser()
 - parser.add_argument("filename")
 - args = parser.parse_args()
 - upload_filename = args.filename
 - response_json = upload_file_to_api(upload_filename)
 - if not response_json:
 - print("file does not exist")
 - else:
 - print("File uploaded to URL: {}".format(response_json['upload_url']))
 
上面的代碼創(chuàng)建了一個(gè)ArgumentParser對(duì)象,它允許應(yīng)用程序從命令行獲取單個(gè)參數(shù)來(lái)指定我們要訪(fǎng)問(wèn)的對(duì)象,讀取并上傳到AssmeblyAI服務(wù)的文件。
如果文件不存在,腳本將顯示一條消息,提示找不到該文件。在路徑中,我們確實(shí)找到了正確的文件,然后使用upload_file_to_api函數(shù)中的代碼上傳了文件。
通過(guò)使用python命令在命令行上運(yùn)行完整的upload_audio_file.py腳本,以執(zhí)行該腳本。將FULL_PATH_TO_FILE替換為您要上傳的文件的絕對(duì)路徑,例如/Users/matt/devel/audio.mp3。
- python upload_audio_file.py FULL_PATH_TO_FILE
 
假設(shè)在您指定的位置找到文件,當(dāng)腳本完成文件的上傳后,它將打印一條帶有唯一URL的消息:
- File uploaded to URL: https://cdn.assemblyai.com/upload/463ce27f-0922-4ea9-9ce4-3353d84b5638
 
該URL不是公開(kāi)的,只能由AssemblyAI服務(wù)使用,因此除您及其轉(zhuǎn)錄的API外,其他任何人都無(wú)法訪(fǎng)問(wèn)您的文件及其內(nèi)容。
重要的部分是URL的最后一部分,在此示例中為463ce27f-0922-4ea9-9ce4-3353d84b5638。保存該唯一標(biāo)識(shí)符,因?yàn)槲覀冃枰獙⑵鋫鬟f給下一個(gè)啟動(dòng)轉(zhuǎn)錄服務(wù)的腳本。
啟動(dòng)轉(zhuǎn)錄
接下來(lái),我們將編寫(xiě)一些代碼來(lái)開(kāi)始轉(zhuǎn)錄。創(chuàng)建一個(gè)名為initial_transcription.py的新文件。將以下代碼添加到新文件中。
- import argparse
 - import os
 - import requests
 - API_URL = "https://api.assemblyai.com/v2/"
 - CDN_URL = "https://cdn.assemblyai.com/"
 - def initiate_transcription(file_id):
 - """Sends a request to the API to transcribe a specific
 - file that was previously uploaded to the API. This will
 - not immediately return the transcription because it takes
 - a moment for the service to analyze and perform the
 - transcription, so there is a different function to retrieve
 - the results.
 - """
 - endpoint = "".join([API_URL, "transcript"])
 - json = {"audio_url": "".join([CDN_URL, "upload/{}".format(file_id)])}
 - headers = {
 - "authorization": os.getenv("ASSEMBLYAI_KEY"),
 - "content-type": "application/json"
 - }
 - response = requests.post(endpoint, jsonjson=json, headersheaders=headers)
 - return response.json()
 
我們具有與先前腳本相同的導(dǎo)入,并添加了一個(gè)新常量CDN_URL,該常量與AssemblyAI存儲(chǔ)上傳的音頻文件的單獨(dú)URL匹配。
initiate_transcription函數(shù)本質(zhì)上只是向AssemblyAI API設(shè)置了一個(gè)HTTP請(qǐng)求,以傳入的特定URL對(duì)音頻文件啟動(dòng)轉(zhuǎn)錄過(guò)程。這就是為什么file_id傳遞很重要的原因:完成音頻文件的URL 我們告訴AssemblyAI進(jìn)行檢索。
通過(guò)附加此代碼來(lái)完成文件,以便可以從命令行輕松地使用參數(shù)調(diào)用它。
- if __name__ == "__main__":
 - parser = argparse.ArgumentParser()
 - parser.add_argument("file_id")
 - args = parser.parse_args()
 - file_id = args.file_id
 - response_json = initiate_transcription(file_id)
 - print(response_json)
 
通過(guò)在initiate_transcription文件上運(yùn)行python命令來(lái)啟動(dòng)腳本,并傳入您在上一步中保存的唯一文件標(biāo)識(shí)符。
- # the FILE_IDENTIFIER is returned in the previous step and will
 - # look something like this: 463ce27f-0922-4ea9-9ce4-3353d84b5638
 - python initiate_transcription.py FILE_IDENTIFIER
 
API將發(fā)回該腳本打印到命令行的JSON響應(yīng)。
- {'audio_end_at': None, 'acoustic_model': 'assemblyai_default', 'text': None,
 - 'audio_url': 'https://cdn.assemblyai.com/upload/463ce27f-0922-4ea9-9ce4-3353d84b5638',
 - 'speed_boost': False, 'language_model': 'assemblyai_default', 'redact_pii': False,
 - 'confidence': None, 'webhook_status_code': None,
 - 'id': 'gkuu2krb1-8c7f-4fe3-bb69-6b14a2cac067', 'status': 'queued', 'boost_param': None,
 - 'words': None, 'format_text': True, 'webhook_url': None, 'punctuate': True,
 - 'utterances': None, 'audio_duration': None, 'auto_highlights': False,
 - 'word_boost': [], 'dual_channel': None, 'audio_start_from': None}
 
記下JSON響應(yīng)中id鍵的值。這是我們需要用來(lái)檢索轉(zhuǎn)錄結(jié)果的轉(zhuǎn)錄標(biāo)識(shí)符。在此示例中,它是gkuu2krb1-8c7f-4fe3-bb69-6b14a2cac067。復(fù)制轉(zhuǎn)錄標(biāo)識(shí)符到您自己的響應(yīng)中,因?yàn)樵谙乱徊街形覀儗⑿枰鼇?lái)檢查轉(zhuǎn)錄過(guò)程何時(shí)完成。
檢索轉(zhuǎn)錄結(jié)果
我們已經(jīng)上傳并開(kāi)始了轉(zhuǎn)錄過(guò)程,因此,準(zhǔn)備就緒后,我們將盡快獲得結(jié)果。
返回結(jié)果所需的時(shí)間取決于文件的大小,因此下一個(gè)腳本將向HTTP發(fā)送一個(gè)HTTP請(qǐng)求,并報(bào)告轉(zhuǎn)錄狀態(tài),或者在完成后打印輸出。
創(chuàng)建一個(gè)名為 get_transcription.py 的第三個(gè)Python文件,并將以下代碼放入其中。
- import argparse
 - import os
 - import requests
 - API_URL = "https://api.assemblyai.com/v2/"
 - def get_transcription(transcription_id):
 - """Requests the transcription from the API and returns the JSON
 - response."""
 - endpoint = "".join([API_URL, "transcript/{}".format(transcription_id)])
 - headers = {"authorization": os.getenv('ASSEMBLYAI_KEY')}
 - response = requests.get(endpoint, headersheaders=headers)
 - return response.json()
 - if __name__ == "__main__":
 - parser = argparse.ArgumentParser()
 - parser.add_argument("transcription_id")
 - args = parser.parse_args()
 - transcription_id = args.transcription_id
 - response_json = get_transcription(transcription_id)
 - if response_json['status'] == "completed":
 - for word in response_json['words']:
 - print(word['text'], end=" ")
 - else:
 - print("current status of transcription request: {}".format(
 - response_json['status']))
 
上面的代碼與其他腳本具有相同的 imports 對(duì)象。在這個(gè)新的get_transcription函數(shù)中,我們只需使用我們的API密鑰和上一步中的轉(zhuǎn)錄標(biāo)識(shí)符(而不是文件標(biāo)識(shí)符)調(diào)用AssemblyAI API。我們檢索JSON響應(yīng)并將其返回。
在main函數(shù)中,我們處理作為命令行參數(shù)傳入的轉(zhuǎn)錄標(biāo)識(shí)符,并將其傳遞給get_transcription函數(shù)。如果來(lái)自get_transcription函數(shù)的響應(yīng)JSON包含completed狀態(tài),則我們將打印轉(zhuǎn)錄結(jié)果。否則,請(qǐng)?jiān)赾ompleted之前打印當(dāng)前狀態(tài)如queued或processing。
使用命令行和上一節(jié)中的轉(zhuǎn)錄標(biāo)識(shí)符調(diào)用腳本:
- python get_transcription.py TRANSCRIPTION_ID
 
如果該服務(wù)尚未開(kāi)始處理腳本,則它將返回queued,如下所示:
- current status of transcription request: queued
 
當(dāng)服務(wù)當(dāng)前正在處理音頻文件時(shí),它將返回processing:
- current status of transcription request: processing
 
該過(guò)程完成后,我們的腳本將返回轉(zhuǎn)錄文本,如您在此處看到的那樣:
- An object relational mapper is a code library that automates the transfer of
 - data stored in relational, databases into objects that are more commonly used
 - in application code or EMS are useful because they provide a high level
 - ...(output abbreviated)
 
就是這樣,我們已經(jīng)轉(zhuǎn)錄完成了!
您可能想知道如果精度不適合您的情況該怎么辦。這就是需要用到提高關(guān)鍵字或短語(yǔ)的準(zhǔn)確性方法(https://docs.assemblyai.com/guides/boosting-accuracy-for-keywords-or-phrases)和選擇與數(shù)據(jù)更匹配的模型方法(https://docs.assemblyai.com/guides/transcribing-with-a-different-acoustic-or-custom-language-model)的地方。您可以使用這兩種方法中的任一種,將記錄的準(zhǔn)確性提高到適合您情況的水平。
下一步是什么?
我們剛剛完成了一些腳本,這些腳本調(diào)用AssemblyAI API來(lái)將帶有語(yǔ)音的錄音轉(zhuǎn)錄為文本輸出。您可以查閱文檔(https://docs.assemblyai.com/overview/getting-started)來(lái)增加一些更高級(jí)功能:
- 支持不同的文件格式
 - 轉(zhuǎn)錄雙通道/立體聲錄音
 - 獲取揚(yáng)聲器標(biāo)簽(揚(yáng)聲器隔離)
 
















 
 
 









 
 
 
 