程序是事件驱动的,写博客是什么驱动的?事件? 时间?no,我承认我很懒,甚至不愿意记录总结。哪是什么驱动的? 对! 问题驱动的。遇到了问题解决了问题突然想起来搬到blog上,让遇到相同问题的可以参考下。
问题所在
我用的Electronic WeChat,当朋友发送文字、图片、甚至视频的时候都可以打开(Audio无法收听,因为电脑无外设音响/喇叭)。但发来的语音就扯蛋了,只能通过手机听,甚至不能转发。
但是发现可以通过开发者工具看到http语音文件的请求地址。
直接复制url在浏览器中打开没有任何响应?难道是Referer与Cookie有判断。果然!只有在登陆的浏览器中可以,不信你可以直接在浏览器中打开web微信尝试操作,Electronic WeChat 只有一个tab。open link in new tab 其实就跳转到浏览器了。然而开发者工具带了一个骚操作,那就是“Copy Response”
copy response的数据为base64内容:
data:audio/mp3;base64,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
所以将base64转换为对应的mp3格式不就可以播放了吗?.......
base64
Python的base64函数是内置函数,是一种用64个字符来表示任意二进制数据的方法。用记事本打开exe
、jpg
、pdf
这些文件时,我们都会看到一大堆乱码,因为二进制文件包含很多无法显示和打印的字符,所以,如果要让记事本这样的文本处理软件能处理二进制数据,就需要一个二进制到字符串的转换方法。Base64是一种最常见的二进制编码方法。
廖雪峰老师对base64的介绍
base64对文件的编码解码
对文件的编码就是读取文件内容,encode为base64,对文件的解码就是将base64字符串转换为文件二进制。
- 编码使用
base64.b64encode()
- 解码使用
base64.b64decode()
读取文件我们可以用with open as 也可以直接open,区别是with智能关闭文件,open要手动close文件。参考:
上代码:
def ToBase64(file, txt):
with open(file, 'rb') as fileObj:
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
fout = open(txt, 'w')
fout.write(base64_data.decode())
fout.close()
def ToFile(txt, file):
with open(txt, 'r') as fileObj:
base64_data = fileObj.read()
ori_image_data = base64.b64decode(base64_data)
fout = open(file, 'wb')
fout.write(ori_image_data)
fout.close()
ToBase64("./desk.jpg",'desk_base64.txt') # 文件转换为base64
ToFile("./desk_base64.txt",'desk_cp_by_base64.jpg') # base64编码转换为二进制文件
pyqt的GUI外衣
想转换命令行就如上,但是操作不方便,可以给base64的代码穿一个GUI外衣,使用pyqt方便的不得了!
# -*- coding: UTF8 -*-
import io, shutil, sys, os
from PyQt5.QtWidgets import *
from PyQt5 import QtGui
import base64
class MainWidgetUI(QDialog):
def __init__(self):
super().__init__()
self.setFixedSize(250, 200) # PyQT禁止调整窗口大小
self.setWindowTitle('文件base64编码互转')
self.setWindowIcon(QtGui.QIcon("favicon.ico"))
Layout = QVBoxLayout()
self.pushButton1 = QPushButton("To base64")
self.pushButton2 = QPushButton("To file")
Layout.addWidget(self.pushButton1) # addWidget 添加一个挂件
Layout.addSpacing(100) # 添加一个100px的空间距离 且不带弹性
Layout.addWidget(self.pushButton2)
self.setLayout(Layout) # setLayout设置 QVBoxLayout()垂直 与QHBoxLayout() 水平布局, 查看布局请移步CURL-api.py
# pushButton1点击按钮
self.pushButton1.clicked.connect(self.Tobase64)
# pushButton2点击按钮
self.pushButton2.clicked.connect(self.Tofile)
def Tobase64(self):
fileDict = self.selectFile()
if fileDict:
GlobalToBase64(fileDict['file'], fileDict['filepath'] + "/" + fileDict['shotname'] + ".txt")
QMessageBox.information(self, '提示!', "转换成功", QMessageBox.Yes)
def Tofile(self):
fileDict = self.selectFile()
if fileDict:
# 简单判断文件是否自带Mime 文件扩展 data:audio/mp3;base64,
fobj = open(fileDict['file'])
contents = fobj.read()
extensions = contents.split(',')[0].rsplit('/')[1].split(';')
if len(extensions) == 2: # ['mp3', 'base64'] 格式为两个
# 生成临时文件保存base64
tmpFile = fileDict['filepath'] + "/" + fileDict['shotname'] + ".tmp"
tmpFileObj = open(tmpFile, 'w')
tmpFileObj.write(contents.split(',')[1])
tmpFileObj.close()
GlobalToFile(tmpFile, fileDict['filepath'] + "/" + fileDict['shotname'] + "." + extensions[0])
QMessageBox.information(self, '提示!', "转换成功", QMessageBox.Yes)
os.remove(tmpFile) # 删除临时文件
else: # 无mime
mine, okPressed = QInputDialog.getText(self, "文件格式类型", "请输入文件格式类型:", QLineEdit.Normal,
"mp3") # 获取输入对话框内容
if okPressed and mine: # 选择确认且输入文本
GlobalToFile(fileDict['file'], fileDict['filepath'] + "/" + fileDict['shotname'] + "." + mine)
QMessageBox.information(self, '提示!', "转换成功", QMessageBox.Yes)
else:
return False
def selectFile(self):
'''
选择文件
'''
# getOpenFileName 只能选择一个 getOpenFileNames 可多个选择
file = QFileDialog.getOpenFileName(self, "请选择文件", '', "*.*")
if file[0] == '':
QMessageBox.warning(self, '错误提示!', "请选择文件", QMessageBox.Yes)
else:
(filepath, filename) = os.path.split(file[0]) # 获取文件路径,文件名
(shotname, extension) = os.path.splitext(filename) # 获取文件名称,文件后缀
return {"file": file[0], "filepath": filepath, "shotname": shotname}
def GlobalToBase64(file, txt):
with open(file, 'rb') as fileObj:
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
fout = open(txt, 'w')
fout.write(base64_data.decode())
fout.close()
def GlobalToFile(txt, file):
with open(txt, 'r') as fileObj:
base64_data = fileObj.read()
ori_image_data = base64.b64decode(base64_data)
fout = open(file, 'wb')
fout.write(ori_image_data)
fout.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
main_widget = MainWidgetUI()
main_widget.show()
sys.exit(app.exec_())
效果:
这样就可以把微信发送的语音文件保存下来了。猜猜上面那个 “data:audio/mp3;base64,..”说的什么玩意。