教程
信息差
资源
软件工具
技术笔记
AIGC
视频
Search
1
使用AI实现高精度钢琴曲转谱Piano Transcription简明使用教程
37,794 阅读
2
使用ESP8266Wifi模块制作Wifi杀手
37,467 阅读
3
unravel 让图片唱歌详细教程 Real Time Image Animation 项目
27,386 阅读
4
佳能相机刷Magic Lantern魔灯固件
23,501 阅读
5
战地3 正版账号免费分享!
16,213 阅读
教程
信息差
资源
软件工具
技术笔记
AIGC
视频
Search
标签搜索
python
前端
环境搭建
空镜素材
Ubuntu
markdown
神器
黑苹果
编码
技巧
Git
数据库
开发
下载工具
Youtube
CDN
PDF
OST
电影原声带
音乐
易小灯塔
累计撰写
176
篇文章
累计收到
44
条评论
首页
栏目
教程
信息差
资源
软件工具
技术笔记
AIGC
视频
页面
搜索到
30
篇与
的结果
2020-11-20
使用AI实现高精度钢琴曲转谱Piano Transcription简明使用教程
简介此项目可将钢琴录音(mp3, wav等格式)转录成MIDI文件, 识别的精度非常之高, 扒谱党的福利。相对于人工扒谱效率高了不知道多少倍, AI扒谱的时代已经到来。前段时间字节跳动发布了全球最大的古典钢琴MIDI数据集 GiantMIDI-Piano, GiantMIDI-Piano 的特点是使用钢琴转谱技术,通过计算机将音频文件自动转为 MIDI 文件,并通过该技术转谱了大规模的 MIDI 数据集。研究者万余首钢琴作品+一千多小时训练,开发并开源了一套高精度钢琴转谱系统piano_transcription,将所有音频转谱成 MIDI 文件,进而构建了 GiantMIDI-Piano 数据库。该转谱系统的特点包括:能够将任意声部数目、任意复杂度,甚至双钢琴、多钢琴的钢琴音频转谱为 MIDI 文件。实现了任意时间精度的音符检测,突破了之前算法 32 毫秒识别精度的限制。对每个音符实现了 128 个粒度的力度识别。同时包含了钢琴音符和钢琴踏板的识别。在 MAESTRO 评测数据集上取得 96.72% 的 F1 值,超越了 Google 系统的 94.80%。预训练模型的代码以 Apache 2.0 协议开源。详细介绍见https://zhuanlan.zhihu.com/p/269218623GiantMIDI-Pianohttps://github.com/bytedance/GiantMIDI-Pianopiano_transcriptionhttps://github.com/bytedance/piano_transcription一网友在此基础制作的修改版https://zhuanlan.zhihu.com/p/270999354在此基础上, 我再稍微加工了下, 可批量转置, 使得更易使用下面是一些转置后制作的一些视频2011年Zetacola《赤色要塞》的钢琴演奏音质修复+特效钢琴版使用方法只需三步即可下载资源安装环境使用下载下面的资源包, 解压链接: https://pan.baidu.com/s/1aqnlgfFCjB0KIlPEB8RcRg 提取码: xubj资源包内包括piano_transcription项目,项目所使用的已训练完好模型,python安装包和ffmpeg安装环境以64位的windows版本为例, Mac和linux的步骤也是一样的, 只需安装对应系统的软件版本安装下列环境01 Python 3.7使用到了f-string, 需要Python3.6以上版本才支持, 这里选择安装Python3.7.9版本02 项目所依赖的库03 ffmpeg用于读取媒体文件, 使用资源包里的版本即可04 cuda限NVIDIA显卡, 使用cuda转置速度更快, 没有可以不装, 使用CPU转置05 pytorch需要看Python版本和cuda版本, 选择相应的版本下载01 安装 Python 3.7双击附件里边的Python 3.7.9安装即可在cmd里输入python出现下面提示及安装成功02 项目所依赖的库在项目目录根下按住shift在空白处点击powershell打开输入下面命令, 升级pip版本(我装有多个python版本, 所以图片显示命令不一样, 新手直接输入下面命令即可)python -m pip install --upgrade pip 安装依赖环境pip install -r .\requirements.txt 03 安装ffmpeg需要把ffmpeg的bin目录添加到环境变量右键点击此电脑--属性 , 点击高级系统设置, 高级里边的环境变量, 双击Path, 选择新建,将ffmpeg下的bin目录路径复制的里边,如 F:\钢琴转谱资源包\ffmpeg\bin(将路径改为你所解压的ffmpeg的bin目录路径)然后点击确定在cmd里输入ffmpeg 有显示ffmpeg version 4.3.1字样的即添加好了环境变量04 安装cuda (使用CPU跳过此步骤)CUDA 是 NVIDIA 的一种并行计算平台和编程模型。使用显卡转置比用CPU快很多cuda版本和驱动支持有关,显卡驱动会影响支持cuda的版本 , 为避免不必要的错误, 先去官网更新驱动到最新版本,这里以win10 1909版本的系统为例win7等其他系统去NVIDIA控制面板查看支持cuda的最高版本, 选择相应的cuda版本驱动下载地址https://www.nvidia.cn/geforce/drivers/安装显卡最新驱动, 再去下载cuda工具包11.1安装(有3G大)下载地址https://developer.nvidia.com/zh-cn/cuda-downloads在安装界面选择自定义, 如图只勾选cuda当中的那几项, 其他不必勾选, 选了有可能安装失败安装完成后显示05 安装pytorch到官网下载自己系统合适的版本, 如果使用CPU选择cuda版本选择nonehttps://pytorch.org/这里选择windows系统python3.7 使用pip安装cuda11为例, 按下图选择好版本后下面会有对应的安装命令在cmd命令行里输入安装pytorch, 文件挺大有差不多2G, 如果网络不好安装失败, 请看下边的离线包安装pip install torch===1.7.0+cu110 torchvision===0.8.1+cu110 torchaudio===0.7.0 -f https://download.pytorch.org/whl/torch_stable.html 文件比较大差不多2G, 如果网络不好可以下载离线安装包https://download.pytorch.org/whl/cu110/torch-1.7.0%2Bcu110-cp37-cp37m-win_amd64.whl下载好后再目录下按住shift在空白处右键点击, 选择powershell打开命令行, 输入下面安装命令 pip install .\torch-1.7.0+cu110-cp37-cp37m-win_amd64.whl 使用把MP3,或wav文件放入input文件夹, 可以放多个音频文件在项目piano_transcription按住shift在空白处右键点击, 选择powershell打开命令行, 输入以下命令, 等待程序跑完即可 python .\start.py 使用GPU进行转置还是挺快的, 大概10秒~1分多钟一首跑完后可在output文件夹得到转置好的mid文件, 使用播放器播放即可, 可使用Pianoteq的音源生成高音质的mp3, 如果效果不是很好, 可以使用midi编辑软件进行进一步的修改。midi文件还可以通过一些软件转成琴谱如果使用CPU转置可以修改start.py文件把第19行里边的cuda改为cpu即可
2020年11月20日
37,794 阅读
6 评论
114 点赞
2020-02-12
Python性能分析工具Line_profiler
介绍profile和line_profiler两个模块都是性能分析工具。有时候需要找到代码中运行速度较慢处或瓶颈,可以通过这两模块实现,而不再使用time计时。line_profiler模块可以记录每行代码的运行时间和耗时百分比。安装输入以下命令安装即可pip install line_profiler使用方法1 :生成一个Line_profiler类(推荐)简单版本from line_profiler import LineProfiler def do_stuff(numbers): s = sum(numbers) l = [numbers[i] / 43 for i in range(len(numbers))] m = ['hello' + str(numbers[i]) for i in range(len(numbers))] if __name__ == '__main__': number = [1,2,3,4,5,6] p = LineProfiler() p_wrap = p(do_stuff) p_wrap(number) p.print_stats() # 控制台打印相关信息 p.dump_stats('saveName.lprof') # 当前项目根目录下保存文件 输出结果:"D:\Program Files\Anaconda3\envs\tensorflow2.3\python.exe" "C:/Users/admin/Desktop/xxxx/temp.py" Timer unit: 1e-07 s Total time: 1.08e-05 s File: C:/Users/admin/Desktop/GPflowMPC_cui _contract - profile/temp.py Function: do_stuff at line 193 Line # Hits Time Per Hit % Time Line Contents ============================================================== 193 def do_stuff(numbers): 194 1 21.0 21.0 19.4 s = sum(numbers) 195 1 45.0 45.0 41.7 l = [numbers[i] / 43 for i in range(len(numbers))] 196 1 42.0 42.0 38.9 m = ['hello' + str(numbers[i]) for i in range(len(numbers))]多函数调用当你需要调用不止一个函数的时候,就可以使用add_function函数去添加额外需要监控的函数from line_profiler import LineProfiler def second_function(): # just for test i = 5 pass def do_stuff(numbers): s = sum(numbers) l = [numbers[i] / 43 for i in range(len(numbers))] m = ['hello' + str(numbers[i]) for i in range(len(numbers))] for i in range(5): second_function() if __name__ == '__main__': number = [1,2,3,4,5,6] p = LineProfiler() p.add_function(second_function) p_wrap = p(do_stuff) p_wrap(number) p.print_stats() p.dump_stats('saveName.lprof')输出结果"D:\Program Files\Anaconda3\envs\tensorflow2.3\python.exe" "C:/Users/admin/Desktop/xxxx/temp.py" Timer unit: 1e-07 s Total time: 2.4e-06 s File: C:/Users/admin/Desktop/GPflowMPC_cui _contract - profile/temp.py Function: second_function at line 193 Line # Hits Time Per Hit % Time Line Contents ============================================================== 193 def second_function(): 194 # just for test 195 5 14.0 2.8 58.3 i = 5 196 5 10.0 2.0 41.7 pass Total time: 2.44e-05 s File: C:/Users/admin/Desktop/GPflowMPC_cui _contract - profile/temp.py Function: do_stuff at line 198 Line # Hits Time Per Hit % Time Line Contents ============================================================== 198 def do_stuff(numbers): 199 1 22.0 22.0 9.0 s = sum(numbers) 200 1 48.0 48.0 19.7 l = [numbers[i] / 43 for i in range(len(numbers))] 201 1 45.0 45.0 18.4 m = ['hello' + str(numbers[i]) for i in range(len(numbers))] 202 6 32.0 5.3 13.1 for i in range(5): 203 5 97.0 19.4 39.8 second_function()方法2:使用装饰器@profile在需要检测的函数上面添加@profile装饰符号, (此时调包是调用profile 而不是line_profiler)。在命令行中使用kernprof -l -v test.py启动,结束会在窗口打印逐行信息以及生成一个lprof文件。 与方法1 相似。问题在于每次不在使用profile 查看性能时,需要将函数上的装饰类注释掉读取lprof 文件: 进入当前目录后,在命令行中使用python -m line_profiler saveName.lprof # saveName.lprof 读取指定文件资料来源 https://blog.csdn.net/weixin_44613728/article/details/120411325
2020年02月12日
916 阅读
0 评论
0 点赞
2018-07-19
Python gevent协程发http请求
为什么要用协程,Python在执行并发任务时有:多线程用锁,很容易造成数据不稳定,无法使用发挥多核CPU的能力多进程耗资源Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。安装gevent安装: pip install greenlet#-*- coding: utf-8 -*- import urllib.request # url = "http://r.qzone.qq.com/cgi-bin/user/cgi_personal_card?uin=******" url = "http://gc.ditu.aliyun.com/geocoding?a="+urllib.request.quote("广州市") HEADER = {'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','User-Agent':'Mozilla/5.0 (Windows NT 6.1; rv:29.0) Gecko/20100101 Firefox/29.0'} # 自定义请求的方法,get post def f(url): import urllib import urllib.request data=urllib.request.urlopen(url).read() z_data=data.decode('UTF-8') print(z_data) # 我自己封装了gevent 的方法,重载了run from gevent import Greenlet class MyGreenlet(Greenlet): def __init__(self, func): Greenlet.__init__(self) self.func = func def _run(self): # gevent.sleep(self.n) self.func count = 3 green_let = [] for i in range(0, count): green_let.append(MyGreenlet(f(url))) for j in range(0, count): green_let[j].start() for k in range(0, count): green_let[k].join() 运行结果C:\Python3\python.exe D:/app/dgm/field_study.py {"lon":120.58531,"level":2,"address":"","cityName":"","alevel":4,"lat":31.29888} {"lon":120.58531,"level":2,"address":"","cityName":"","alevel":4,"lat":31.29888} {"lon":120.58531,"level":2,"address":"","cityName":"","alevel":4,"lat":31.29888}
2018年07月19日
1,308 阅读
0 评论
0 点赞
2018-07-14
F-Strings:超级好用的Python格式字符串!!
主要内容从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快!今天就开始使用f-string(后文称为F字符串) !首先, 我们要聊以下在F字符串出现之前我们怎么实现格式化字符的。旧时代的格式化字符串在Python 3.6之前,有两种将Python表达式嵌入到字符串文本中进行格式化的主要方法:%-formatting和str.format()。您即将看到如何使用它们以及它们的局限性。1: %-formatting这是Python格式化的OG(original generation),伴随着python语言的诞生。可以在Python文档中阅读更多内容。请记住,文档不建议使用%格式,其中包含以下注释:“The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly).Using the newer formatted string literals or the str.format() interface helps avoid these errors. These alternatives also provide more powerful, flexible and extensible approaches to formatting text.”怎样使用 %-formatting字符串对象具有使用%运算符的内置操作,可以使用它来格式化字符串。name = "Eric" "Hello, %s." % name # 输出 'Hello, Eric.' 为了插入多个变量,必须使用这些变量的元组。以name = "Eric" age = 74 "Hello, %s. You are %s." % (name, age) # 输出 'Hello, Eric. You are 74.' 为什么 %-formatting不好用上面的代码示例足够易读。但是,一旦你开始使用几个参数和更长的字符串,将很快变得不太容易阅读。first_name = "Eric" last_name = "Idle" age = 74 profession = "comedian" affiliation = "Monty Python" "Hello, %s %s. You are %s. You are a %s. You were a member of %s." %</div> (first_name, last_name, age, profession, affiliation) # 输出 'Hello, Eric Idle. You are 74. You are a comedian. You were a member of Monty Python.' 这种格式不是很好,因为它是冗长的,会导致错误,比如不能正确显示元组或字典。2: str.format()这种更新的工作方式是在Python 2.6中引入的。怎样使用Use str.format()str.format()是对%-formatting的改进。它使用正常的函数调用语法,并且可以通过对要转换为字符串的对象的__format __()方法进行扩展。使用str.format(),替换字段用大括号标记:"Hello, {}. You are {}.".format(name, age) # 输出 'Hello, Eric. You are 74.' 通过引用其索引来以任何顺序引用变量:"Hello, {1}. You are {0}-{0}.".format(age, name) # 输出 'Hello, Eric. You are 74-74.' 但是,如果插入变量名称,则会获得额外的能够传递对象的权限,然后在大括号之间引用参数和方法:person = {'name': 'Eric', 'age': 74} "Hello, {name}. You are {age}.".format(name=person['name'], age=person['age']) # 输出 'Hello, Eric. You are 74.' 也可以使用**来用字典来完成这个巧妙的技巧:"Hello, {name}. You are {age}.".format(**person) # 输出 'Hello, Eric. You are 74.' 与f-string相比,str.format()绝对是一个升级版本,但它并非总是好的。为什么 str.format() 并不好使用str.format()的代码比使用%-formatting的代码更易读,但当处理多个参数和更长的字符串时,str.format()仍然可能非常冗长。first_name = "Eric" last_name = "Idle" age = 74 profession = "comedian" affiliation = "Monty Python" print(("Hello, {first_name} {last_name}. You are {age}. " + "You are a {profession}. You were a member of {affiliation}.") </div> .format(first_name=first_name, last_name=last_name, age=age, </div> profession=profession, affiliation=affiliation)) # 输出 Hello, Eric Idle. You are 74. You are a comedian. You were a member of Monty Python. 如果想要传递给字典中的.format()的变量,那么你可以用.format(** some_dict)解压缩它,并通过字符串中的键引用这些值,但是必须有更好的的方法f-Strings:一种改进Python格式字符串的新方法F字符串在这里可以节省很多的时间。他们确实使格式化更容易。他们自Python 3.6开始加入标准库。也称为“格式化字符串文字”,F字符串是开头有一个f的字符串文字,以及包含表达式的大括号将被其值替换。表达式在运行时进行渲染,然后使用__format__协议进行格式化。与以下是f-strings可以让编程更轻松的一些方法。简单例子语法与str.format()使用的语法类似,但较少细节啰嗦。name = "Eric" age = 74 f"Hello, {name}. You are {age}." # 输出 'Hello, Eric. You are 74.' 使用大写字母F也是有效的:F"Hello, {name}. You are {age}." # 输出 'Hello, Eric. You are 74.' 任意表达式由于f字符串是在运行时进行渲染的,因此可以将任何有效的Python表达式放入其中。可以做一些非常简单的事情,就像这样:f"{2 * 37}" # 输出 '74' 可以调用函数f"{name.lower()} is funny." # 输出 'eric is funny.' 甚至可以使用带有f字符串的类创建对象。class Comedian: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def __str__(self): return f"{self.first_name} {self.last_name} is {self.age}." def __repr__(self): return f"{self.first_name} {self.last_name} is {self.age}. Surprise!" new_comedian = Comedian("Eric", "Idle", "74") f"{new_comedian}" 'Eric Idle is 74.' __str __()和__repr __()方法处理对象如何呈现为字符串,因此您需要确保在类定义中包含至少一个这些方法。如果必须选择一个,请使用__repr __(),因为它可以代替__str __()。__str __()返回的字符串是对象的非正式字符串表示,应该可读。__repr __()返回的字符串是官方表示,应该是明确的。调用str()和repr()比直接使用__str __()和__repr __()更好。默认情况下,f字符串将使用__str __(),但如果包含转换标志!r,则可以确保它们使用__repr __():f"{new_comedian}" 'Eric Idle is 74.' f"{new_comedian!r}" 'Eric Idle is 74. Surprise!' 多行f-string多行字符串:message = (f"Hi {name}. " f"You are a {profession}. " f"You were in {affiliation}.") message # 输出 'Hi Eric. You are a comedian. You were in Monty Python.' 没必要将f放在多行字符串的每一行的前面。以下代码也能运行:message = (f"Hi {name}. " "You are a {profession}. " "You were in {affiliation}.") message # 输出 'Hi Eric. You are a {profession}. You were in {affiliation}.' 但是如果使用"""这将会发生什么:message = f""" Hi {name}. You are a {profession}. You were in {affiliation}. """ message # 输出 '\n Hi Eric. \n You are a comedian. \n You were in Monty Python.\n ' 性能f字符串中的f也可以代表“速度快”。f-字符串比%-formatting和str.format()都快。f-字符串是运行时渲染的表达式,而不是常量值。以下是文档摘录:“F-strings provide a way to embed expressions inside string literals, using a minimal syntax. It should be noted that an f-string is really an expression evaluated at run time, not a constant value. In Python source code, an f-string is a literal string, prefixed with f, which contains expressions inside braces. The expressions are replaced with their values.” (Source)在运行时,大括号内的表达式将在其自己的作用域中进行求值,然后将其与其余字符串组合在一起。以下是速度比较:%%timeit name = "Eric" age = 74 '%s is %s.' % (name, age) 202 ns ± 2.05 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %%timeit name = "Eric" age = 74 '{} is {}.'.format(name, age) 244 ns ± 5.52 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %%timeit name = "Eric" age = 74 '{name} is {age}.' 14.4 ns ± 0.0121 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each) 可以看到, 速度最快的就是f字符串.Python f-Strings:Pesky细节为什么F字符串很好,开始使用它们但请记住一些细节。引号您可以在表达式中使用各种类型的引号。只要确保在表达式中使用的f-字符串外部没有使用相同类型的引号即可。以下写法都是正确的:f"{'Eric Idle'}" 'Eric Idle' f'{"Eric Idle"}' 'Eric Idle' f"""Eric Idle""" 'Eric Idle' f'''Eric Idle''' 'Eric Idle' f"The \"comedian<span class="string">" is {name}, aged {age}." 'The "comedian" is Eric, aged 74.' 字典说在使用字典的时候。如果要为字典的键使用单引号,请记住确保对包含键的f字符串使用双引号。以下代码是有效的:comedian = {'name': 'Eric Idle', 'age': 74} f"The comedian is {comedian['name']}, aged {comedian['age']}." # 输出 'The comedian is Eric Idle, aged 74.' 但是,以下代码就是一个语法错误:f'The comedian is {comedian['name']}, aged {comedian['age']}.' # 输出 File "<ipython-input-40-cd7d8a3db23b>", line 1 f'The comedian is {comedian['name']}, aged {comedian['age']}.' ^ SyntaxError: invalid syntax 字典键周围使用与在f字符串外部使用相同类型的引号,则第一个字典键开头的引号将被解释为字符串的结尾。大括号为了使字符串出现大括号,您必须使用双大括号:f"{{74}}" '{74}' 但是,如果使用三个以上的大括号,则可以获得更多大括号:f"{{{{74}}}}" '{{74}}' 反斜杠正如之前所看到的,可以在f字符串的字符串部分使用反斜杠转义符。但是,不能使用反斜杠在f字符串的表达式部分中进行转义:f"{<span class="string">"Eric Idle\"}" # 输出 File "<ipython-input-43-35cb9fe0ccc1>", line 1 f"{\"Eric Idle\"}" ^ SyntaxError: f-string expression part cannot include a backslash lambda表达式如果需要使用lambda表达式,解析f-字符串的方式会稍微复杂一些。如果!, :或}不在括号,大括号,括号或字符串中,则它将被解释为表达式的结尾。由于lambda使用:,这可能会导致一些问题:f"{lambda x: x * 37 (2)}" # 输出 File "<fstring>", line 1 (lambda x) ^ SyntaxError: unexpected EOF while parsing 可以通过将您的lambda嵌套在圆括号中来解决此问题:f"{(lambda x: x * 37) (2)}" # 输出 '74' 结束语虽然仍然可以使用格式化字符串的较旧方式,但使用F字符串时可以更简洁,更易读,更方便,既快速又不易出错。
2018年07月14日
1,903 阅读
2 评论
6 点赞
2018-06-04
学习到了几个比较舒服的代码风格
学习到了几个比较舒服的代码风格 看了别人的写的代码, 学习到了几个比较舒服的代码风格自定义Log替换print使用自定义log()def log(*args, **kwargs): '''替代print''' print('log', *args, **kwargs) log() 写入到本地logimport time def log(*args, **kwargs): format = '%Y/%m/%d %H:%M:%S' value = time.localtime(int(time.time())) dt = time.strftime(format, value) with open('log.txt', 'a', encoding='utf-8') as f: print(dt, *args, file=f, **kwargs) 字典函数函数存到字典里, 方便以后调用func_list = { 'func1' = func1(), 'func2' = func2(), } func_list.get('func1') 字典传参if __name__ == '__main__': config = dict( debug=True, host='127.0.0.1', port=8000, # 这样写参数方便注释 ) app.run(**config) 多模块导入from flask import ( Flask, request, render_template, redirect, ) JavaScript自定义日志var log = function(){ console.log.apply(console, arguments) } log('代码')
2018年06月04日
925 阅读
0 评论
0 点赞
2018-05-03
Django项目部署简单记录
前几天买了阿里云的Linux服务器ubuntu16.04,一路搭建环境和部署过程搞了2天多,可以说是神坑无数,最有意思的网上的很多博客是叫踩坑记录,结果照着这些方式去搞出现了很多奇怪的问题,估计也和版本不同有关。这里为了避免少走一些弯路,我就来记录一下整个过程(这里都是默认root用户远程登录下进行操作,项目名为DjangoTest)。Python环境的配置因为Python环境都是Linux系统上面自带的,我这里项目用的设Python3.6,安装的过程就不多说了,这里基本没什么问题,安装好后只需把安装目录链接到系统自带的python3(系统自带的是Python3.5)即可。本来这里应该没什么问题的,但刚这个项目需要请求https的链接,因为在本地的时候会这里是不会出问题的,但是一旦放到服务器端,坑也就来了。请求时直接返回这么一句异常SSLError("Can`t connect to HTTTPS URL because the SSL module is not available")) 可以看到用到是python中的ssl模块不存在,直接去安装好相应的openssl模块后,还是抛出这个异常。于是不停的去Google和百度,过程中也是按照类似的答案,结果还是没能解决这个问题。最后我就卸载了Python3.6, 直接用3.5的环境去运行,这个问题才得以解决。 具体应该是因为系统默认的是加载已存在的ssl模块,用3.6版本的虽然也依赖了,但是并不会识别,发现不是来自默认Python环境中自带的,就无法加载。这里感觉也是能配置的,但事实就是我不知道多少次的卸载重装,按照各种方式去安装和依赖,还是出现同样的问题,好在3.6版本的项目在3.5上还是基本兼容的,虽然暴力,但不失为一种办法。(如果哪位有更好的解决办法,欢迎给我留言)这里完成后就是Python虚拟环境容器的安装和环境变量的配置$ pip install virtualenvwrapper 如果报本地不存在,就先更新下源$ apt-get update 安装完成后直接启动服务/etc/init.d/mysql start /etc/init.d/mysql stop /etc/init.d/mysql restart 然后直接输入mysql进入数据库后$ use mysql 更改mysql密码update user set Password = PASSWORD('你的密码') where User ='root'; 然后设置外部ip访问权限GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '你的密码' WITH GRANT OPTION; FLUSH PRIVILEGES; 这里完成后 重启一下 ,然后还需要修改一下配置文件中绑定的地址vim /etc/mysql/mariadb.conf.d/50-server.cnf 这样就能完成数据库的远程连接了。 注意: 如果Django项目中用到Mysql的话,需要在服务器端的Python虚拟环境下安装一下$ pip install mysqlclient 可能会安装出错,因为缺少一些依赖包,安装一下$ apt-get install libmysqlclient-dev 然后重新安装mysqlclient,这样配置就完成了。uwsgi的安装和配置uwsgi也是一种web服务器,这里就不多介绍了,直接开始安装$ pip install uwsgi 可以先来测试一下$ uwsgi --http 0:80 --module DjangoTest.wsgi 然后使用配置文件的方式来启动django项目,可以直接在项目的目录下新建一个uwsgi.ini文件# mysite_uwsgi.ini file [uwsgi] # Django-related settings # 项目根目录的路径 chdir = /你的路径/DjangoTest # Django's wsgi file module = DjangoTest.wsgi # the virtualenv (full path) # process-related settings # master master = true # maximum number of worker processes processes = 10 # the socket (use the full path to be safe socket = :8000 # ... with appropriate permissions - may be needed # chmod-socket = 664 # clear environment on exit vacuum = true # 虚拟环境的目录 pythonpath = /你的虚拟环境的路径/lib/python3.6/site-packages/ 配置文件写好后,其实我们是可以直接用uwsgi就能完成部署的,使用命令$ uwsgi -i uwsgi.ini 如果端口号被占用,就直接使用命令查看占用情况$ lsof -i :8000 然后使用命令,销毁这些进程$ killall -9 uwsgi 但是为了提高扩展性,我们继续下面的步骤Nginx的安装与配置nginx是一个强大的中间件,我们直接把它作为对外的服务接口。 开始安装$ apt-get install nginx 然后看看启动、停止和重启命令/etc/init.d/nginx start /etc/init.d/nginx stop /etc/init.d/nginx reload 这里注意,其它配置文件不要乱改!如果安装完成不能正常启动的话可以重启服务器看看。没问题的话,我们可以在项目目录下新建一个nginx.conf文件,然后按照下面的方式配置# the upstream component nginx needs to connect to upstream django { #django项目的端口号 和uwsgi里面的端口号保存一致 server 0.0.0.0:8000; # for a web port socket (we'll use this first) } # configuration of the server server { # nginx服务的端口号 不用修改 listen 80; # the domain name it will serve for # 这里可以填写你的ip地址或者域名 server_name www.test.com; charset utf-8; # max upload size client_max_body_size 75M; # adjust to taste # Django media location /media { alias /你的路径/DjangoTest/media; # 指向django的media目录 } location /static { alias /你的路径/DjangoTest/static; # 指向django的static目录 } # Finally, send all non-media requests to the Django server. location / { uwsgi_pass django; include uwsgi_params; # the uwsgi_params file you installed } } 配置写好后,就需要把它链接到Nginx的启动配置文件中,$ ln -s /你的目录/DjangoTest/nginx.conf /etc/nginx/conf.d/ 配置完成后,再去加载一下uwsgi文件。总结配置好后,直接输入IP地址或者解析到该IP的域名就能成功访问到Django项目了,这样你也会发现,通过nginx我们可以轻松地配置和部署多个环境不同的站点,只需要把配置文件写好后放入nginx的启动文件中。 最后来整理一下整个访问的过程1.使用nginx作为外部接口,直接处理发送过来的请求,如果是静态请求,就直接根据配置的静态文件目录,返回相应的静态资源。 2.如果是动态请求,就通过配置文件将请求转移到uwsgi,uwsgi又会将接受到的请求传递给wsgi(Django项目默认通过wsgi启动服务的),wsgi就会根据请求来调用Django项目中的逻辑来处理请求。 3.处理完成后,又将结果返回给uwsgi,然后uwsgi又继续将结果返回给nginx 4.最后Nginx将处理后的结果返回给用户
2018年05月03日
6,328 阅读
0 评论
0 点赞
2018-04-05
Django 网络请求超时问题
服务器架构: gunicorn、 Django、nginx最近在工作中遇到一个奇怪的问题,某段逻辑偶尔会出现异常,却偏偏看不到error log。查看代码发现里面有一段网络请求的逻辑,resp = urlopen(url) 这里忽略一个重要的参数 - timeout, urlopen将默认读取 socket defaulttimout。 如果socket也没有设置的话,请求将一直等待,直到出错断开。>> socket.getdefaulttimeout() >> None 看起来就是一个疏忽,诡异的地方在于一直看不到请求报错!!没有报错就看不到错误日志,没有错误日志就无法自动报警,导致了这个问题一直没有被暴露。继续研究日志,在很后面的地方发现一个异常,刚好与发起网络请求的log相差30s。[2017-01-04 12:00:54 +0000] [1260] [CRITICAL] WORKER TIMEOUT (pid:19348) [2017-01-04 12:00:54 +0000] [19348] [INFO] Worker exiting (pid: 19348) [2017-01-04 12:00:54 +0000] [19417] [INFO] Booting worker with pid: 19417 是的,gunicorn worker 超时重启了。原因: gunicorn worker默认 Timeout 为30秒,当网络堵塞超过30秒时,worker将自动重启,这样永远也看不到网络超时的Error解决方法:1、修改gunicorn 默认超时时间 (添加到启动参数,按需修改)--timeout 60 2、非异步网络请求必须添加timeout参数,且不能大于等于gunicorn worker超时时间总结:要关注每个view工作时间,耗时长的任务建议做成异步任务(如使用Celery)
2018年04月05日
2,830 阅读
0 评论
0 点赞
2018-03-15
Python Django 商城开发简单小结
用python django框架,参与开发了一个电商物流的项目,简单说就是从用户(店主)在各个电商平台拉订单,然后智能选仓,发送订单,由仓库出货。订单后处理功能包括:各电商平台拉取订单,检查订单(去重,留言分析,货到付款标识识别,地址合法性检查),拆单/并单,选仓发货。 辅助功能包括:允许开展营销活动(满元赠,满件赠)。允许开启消费者理智期(延迟处理订单)。全国范围智能选仓进行备货,业内叫分仓。项目采用python django框架,涉及到下面几个技术要点: 1、以外键(Foreign Key)为关联,进行前向,后向联合查表,组合成一个QuerySet对象输出。 def get(self, user_id, order_id): # filter方法,这是最常用的一个方法,用来取出数据库中的表项,是一个list。 order_details = OrderDetail.objects.\ filter(order_id=order_id, is_deleted=False). \ select_related('sku') # select_related是后向查表,以'sku'为外键的另一张子表,一并查出来。 order_history = OrderHistory.objects.filter(order_id=order_id) # prefetch_related是前向查表,一并查出父表,并且可以迭加使用。 order = Order.objects.\ prefetch_related(Prefetch('order_detail', queryset=order_details, to_attr='order_details')).\ prefetch_related(Prefetch('order_history', queryset=order_history)).\ select_related('store').\ get(user_id=user_id, id=order_id) return order #返回的order是一个django QuerySet对象。是组合了多张表构成的一个对象。 2、QuerySet对象可以多次条件过滤,从而支持多条件查询操作。warehouse_list是一个QuerySet对象,可以经过几个不同条件的过滤和排除,从而得到符合多条件的对象。if 'province' in dict_arg: warehouse_list = warehouse_list.filter(province__in=dict_arg['province']) if 'city' in dict_arg: warehouse_list = warehouse_list.filter(city__in=dict_arg['city']) if 'not_approved' in dict_arg: warehouse_list = warehouse_list.exclude(approved=dict_arg['not_approved']) # province__in,数据库列名加上__in的意思是一个条件判断,判断province是否在=后的列表中。这是django强大之处。 3、prefetch_related另一种经典用法,关联表列名conditions = { "warehouseservice__code": SERVICE_ITEMS[0][0], } # 'warehouseservice_set'这是warehouse的一个父表的QuerySet对象。实际上django在查表时,一并把关联表全部查出来了的。 # 'warehouseservice__price'和"warehouseservice__code"是父表中的两个列名:price和code。 warehouse_list = warehouse_list.prefetch_related('warehouseservice_set') .filter(**conditions).order_by('warehouseservice__price') 4、django排序输出# 用order_by方法,可以对多个列名进行排序,先排第一个,第一个相同的情况下,按第二个来排。默认是升序,前面加-是降序。 warehouse_list = warehouse_list.order_by('-stars_level', 'cost_per_order') 5、http url中的query取参方法# 前端用GET或POST方法提交时,都可以在http url中添加query参数,就是?type=1这种参数。 request.GET 类似于一个字典,更好的办法是用 request.GET.get('a', 0) 当没有传递 a 的时候默认 a 为 0 一个同事写了一个get_param_by_request(params, param_name, default_val=None, _type=None)方法来达到这个目的, 看来似乎没必要。 6、url正则表达式写法 django框架给出了一个urlpatterns = [ url(r'^create$', livechat_view.create, name='create_channel'), # 发送申请看仓消息 url(r'^requests$', livechat_view.requests, name='create_or_list_requests'), # idx是频道号,func是动作名。这两个参数都通过url给进来。这是一个技巧。 url(r'^(?P<idx>\w+)/(?P<func>\w+)$', livechat_view.handle, name='pause_resume_delete'), ] django的匹配url时,是从上到下来顺序执行的,如果我们把最后一行提到最前面,那创建频道和发送看仓消息就得不到正确的匹配。7、前后台参数传递,用code而不是值,前端要展示的值,由后端查出来给前端。OPERATION_CODE = ( (10, '创建订单', '接收订单'), (11, '订单审核成功', '订单通过系统审核'), (30, '仓库接单', '仓库成功接受发货任务'), (31, '发货完成', '仓库出库完成'), ) 这个编码值,传递起来简单准确,数据量也少。当一个代码的描述值发生变化时,code仍然不用变。8、基于ModelSerializer做序列化,返回json对象给前端。class OrderHistorySerializer(ModelSerializer): order_id = CharField(source='order.id') class Meta: model = OrderHistory fields = [ 'id', 'created_at', 'updated_at', 'order_id', 'is_system', 'operator_id', 'copy_operator_name', 'action', 'comment', ] 把要返回的字段放进去,就可以得到json化的dict返回,比较方便快捷。
2018年03月15日
1,431 阅读
0 评论
0 点赞
2018-03-07
Celery异步任务的使用
我们在做网站后端程序开发时,会碰到这样的需求:用户需要在我们的网站填写注册信息,我们发给用户一封注册激活邮件到用户邮箱,如果由于各种原因,这封邮件发送所需时间较长,那么客户端将会等待很久,造成不好的用户体验.我们将耗时任务放到后台异步执行。不会影响用户其他操作。除了注册功能,例如上传,图形处理等等耗时的任务,都可以按照这种思路来解决。 如何实现异步执行任务呢?我们可使用celery. celery除了刚才所涉及到的异步执行任务之外,还可以实现定时处理某些任务 Celery是一个功能完备即插即用的任务队列。elery的特点是: 简单,易于使用和维护,有丰富的文档。 高效,单个celery进程每分钟可以处理数百万个任务。 灵活,celery中几乎每个部分都可以自定义扩展。 一个celery系统可以包含很多的worker和broker,可增强横向扩展性和高可用性能。安装使用$ pip install -U Celery Celery需要一种解决消息的发送和接受的方式,我们把这种用来存储消息的的中间装置叫做message broker, 也可叫做消息中间人。Redis也是一款功能完备的broker安装Redis$ sudo apt-get install redis 使用celery第一件要做的最为重要的事情是需要先创建一个Celery实例,我们一般叫做celery应用,或者更简单直接叫做一个app。app应用是我们使用celery所有功能的入口,比如创建任务,管理任务等,在使用celery的时候,app必须能够被其他的模块导入。创建应用创建tasks.py模块, 其内容为:from celery import Celery # 我们这里案例使用redis作为broker app = Celery('demo', broker='redis://:332572@127.0.0.1/1') # 创建任务函数 @app.task def my_task(): print("任务函数正在执行....") Celery第一个参数是给其设定一个名字, 第二参数我们设定一个中间人broker, 在这里我们使用Redis作为中间人。my_task函数是我们编写的一个任务函数, 通过加上装饰器app.task, 将其注册到broker的队列中。现在我们在创建一个worker, 等待处理队列中的任务.打开终端,cd到tasks.py同级目录中,执行命令:$ celery -A tasks worker --loglevel=info 或者 $ celery -A tasks worker -l init 调用任务任务加入到broker队列中,以便刚才我们创建的celery workder服务器能够从队列中取出任务并执行。如何将任务函数加入到队列中,可使用delay()。进入python终端, 执行如下代码:from tasks import my_task my_task.delay() 通过worker的控制台,可以看到我们的任务被worker处理。调用一个任务函数,将会返回一个AsyncResult对象,这个对象可以用来检查任务的状态或者获得任务的返回值。存储结果想跟踪任务的状态,Celery需要将结果保存到某个地方。有几种保存的方案可选:SQLAlchemy、Django ORM、Memcached、 Redis、RPC (RabbitMQ/AMQP)。例子我们仍然使用Redis作为存储结果的方案,任务结果存储配置我们通过Celery的backend参数来设定。我们将tasks模块修改如下:from celery import Celery # 我们这里案例使用redis作为broker app = Celery('demo', backend='redis://:332572@127.0.0.1:6379/2', broker='redis://:332572@127.0.0.1:6379/1') # 创建任务函数 @app.task def my_task(a, b): print("任务函数正在执行....") return a + b 给Celery增加了backend参数,指定redis作为结果存储,并将任务函数修改为两个参数,并且有返回值。Celery使用简单,配置也非常简单。Celery有很多配置选项能够使得celery能够符合我们的需要,但是默认的几项配置已经足够应付大多数应用场景了。配置信息可以直接在app中设置,或者通过专有的配置模块来配置。直接通过app来配置from celery import Celery app = Celery('demo') # 增加配置 app.conf.update( result_backend='redis://:332572@127.0.0.1:6379/2', broker_url='redis://:332572@127.0.0.1:6379/1', ) 专有配置文件对于比较大的项目,我们建议配置信息作为一个单独的模块。我们可以通过调用app的函数来告诉Celery使用我们的配置模块。配置模块的名字我们取名为celeryconfig, 这个名字不是固定的,我们可以任意取名,建议这么做。我们必须保证配置模块能够被导入。 配置模块的名字我们取名为celeryconfig, 这个名字不是固定的,我们可以任意取名,建议这么做。我们必须保证配置模块能够被导入。下面我们在tasks.py模块 同级目录下创建配置模块celeryconfig.py:result_backend = 'redis://:332572@127.0.0.1:6379/2' broker_url = 'redis://:332572@127.0.0.1:6379/1' tasks.py文件修改为:from celery import Celery import celeryconfig # 我们这里案例使用redis作为broker app = Celery('demo') # 从单独的配置模块中加载配置 app.config_from_object('celeryconfig') 我的项目目录:TestCelery/ ├── proj │ ├── celeryconfig.py │ ├── celery.py │ ├── init.py │ └── tasks.py └── test.py celery.py内容如下:from celery import Celery # 创建celery实例 app = Celery('demo') app.config_from_object('proj.celeryconfig') # 自动搜索任务 app.autodiscover_tasks(['proj']) celeryconfig.p模块内容如下:from kombu import Exchange, Queue BROKER_URL = 'redis://:332572@127.0.0.1:6379/1' CELERY_RESULT_BACKEND = 'redis://:332572@127.0.0.1:6379/2' tasks.py模块内容如下:from proj.celery import app as celery_app # 创建任务函数 @celery_app.task def my_task1(): print("任务函数(my_task1)正在执行....") @celery_app.task def my_task2(): print("任务函数(my_task2)正在执行....") @celery_app.task def my_task3(): print("任务函数(my_task3)正在执行....") 启动worker:$ celery -A proj worker -l info 键入ctrl+c可关闭worker.调用任务,可使用delay()方法:my_task.delay(2, 2) 也可以使用apply_async()方法,该方法可让我们设置一些任务执行的参数,例如,任务多久之后才执行,任务被发送到那个队列中等等.my_task.apply_async((2, 2), queue='my_queue', countdown=10) 任务my_task将会被发送到my_queue队列中,并且在发送10秒之后执行。如果我们直接执行任务函数,将会直接执行此函数在当前进程中,并不会向broker发送任何消息。无论是delay()还是apply_async()方式都会返回AsyncResult对象,方便跟踪任务执行状态.
2018年03月07日
2,901 阅读
0 评论
0 点赞
2018-02-10
搭建Python开发环境时遇到问题小记
搭建Python开发环境时遇到问题小记 1.安装Pythram的社区版后,发现打不开,出现没安装Java的提示弹窗,于是安装JAVA,一路安装完成后,有出现JVM弹窗,于是上度娘翻译单词求解释和查找解决方法。有说时环境变量的配置问题,找教程改了JAVA_HOME,path,classpath的设置,还是不行。2.吃点东西休息一下后,将之前安装的Python,Pytharm,Java全部卸了。3.按网上找的教程,重新设置了JAVA_HOME,path,classpath。4.跟着先安装Java,再安装Python3X,之后安装Pytharm,安装完成后直接打开Pytharm,能打开了,设置了主题和字体,突然主题变了,关了重新打开,再次出现之前的错误弹窗。5.想了一会,感觉是环境变量设置那边的事情,找了另外一个教程重新设置。6.全部卸了,又重新安装,能打开了,设置完成关闭后,再打开,没问题,搭建OK。
2018年02月10日
806 阅读
0 评论
0 点赞
2017-10-13
Django框架踩过的坑 02
Django框架踩过的坑 021:TypeError: unhashable type: ‘dict’遇到这种问题是因为使用的字典的键是不可哈希的,能够哈希的是一些不可变类型的int,string之类的,而list,dict就不行了。2:append方法和extend等列表方法是没有返回值的,是没有返回值的值,如果直接返回结果只能None,在实际操作过程中吃了两次亏了。3:django很久没有用了,结果用的时候手忙脚乱不知所措,究其本质还是自己了解地不够透彻,既然老是在同一个地方查资料,还不如自己写个方便自己查看:首先是url的配置,在主urls文件中导入需要的包from django.conf.urls import url,include备用,接着处理app中的urls文件(这个是自己手动构建的),同样导入一些调料包:from django.conf.urls import url,当然还有就是需要处理的视图函数所在的路径,然后开始下锅:在app的urls.py中写urlpatterns=[url(''),function_name,],写好之后返回管家婆主urls文件中,告诉它urlpatterns=[url(r'^app_name'),include('one_app.urls'),]这就相当于告诉主人:url以我的app_name开始的路由就指向我的私人路由。4:写接口的时候在路由后面加一个斜杠?然后写上在视图函数中接收参数的参数名也就是这个里面的param_name(而且需要加等号以及后面的内容,无论是不是字符串都不能加引号,很多情况下都是json格式的说)request.GET.get('param_name')。
2017年10月13日
751 阅读
0 评论
0 点赞
2017-10-12
Django框架踩过的坑 01
在admin.py中写fieldsetsfieldsets = [ ('base', {'field': ['btitle']}), ('super', {'field': ['bpub_date']}), ] 元组间没有加,,导致 无法启动Django项目,报错如下:Traceback (most recent call last): File "manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line utility.execute() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 312, in execute django.setup() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/__init__.py", line 18, in setup apps.populate(settings.INSTALLED_APPS) File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/apps/registry.py", line 115, in populate app_config.ready() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/contrib/admin/apps.py", line 22, in ready self.module.autodiscover() File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/contrib/admin/__init__.py", line 24, in autodiscover autodiscover_modules('admin', register_to=site) File "/home/python/.virtualenvs/h1/local/lib/python2.7/site-packages/django/utils/module_loading.py", line 74, in autodiscover_modules import_module('%s.%s' % (app_config.name, module_to_search)) File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module __import__(name) File "/home/python/Desktop/django/test1/booktest/admin.py", line 7, in <module> class BookInfoAdmin(admin.ModelAdmin): File "/home/python/Desktop/django/test1/booktest/admin.py", line 14, in BookInfoAdmin ('super', {'field': ['bpub_date']}) TypeError: 'tuple' object is not callable
2017年10月12日
1,129 阅读
0 评论
0 点赞
2017-08-20
Python的虚拟环境virtualenv
Python的虚拟环境可以使一个Python程序拥有独立的库library和解释器interpreter,而不用与其他Python程序共享统一个library和interpreter。虚拟环境的好处是避免了不同Python程序间的互相影响(共同使用global library 和 interpreter),例如程序A需要某个库的1.0版本,而程序B需要同样这个库的2.0版本,如果程序B执行则A就不能执行了。安装virtualenv:$ sudo pip install virtualenv $ sudo pip install virtualenvwrapper 创建目录用来存放虚拟环境mkdir ~/.virtualenvs 打开~/.bashrc文件,并添加如下:export WORKON_HOME=$HOME/.virtualenvs source ~/.local/bin/virtualenvwrapper.sh 更新配置文件$ source ~/.bashrc 创建虚拟环境$ virtualenv -p python3 虚拟环境名称 查看虚拟环境的命令 :$ workon 两次tab键 使用虚拟环境的命令$ workon 虚拟环境名称 退出虚拟环境$$ deactivate 删除虚拟环境$ rmvirtualenv 虚拟环境名 或者 $ rm -r 虚拟环境名
2017年08月20日
1,294 阅读
0 评论
0 点赞
2017-08-12
Python miniWeb服务器实现
返回固定数据示例代码:import socket def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 2. 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = "are u ok?" response = responseHeader + responseBody clientSocket.send(response.encode("utf-8")) # 3. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 返回固定页面示例代码:import socket def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 2. 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" # 读取固定页面数据 with open('./html/index.html', 'rb') as file: responeBody = file.read() clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responeBody) #先发body # 3. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 解析用户的请求示例代码:import socket import re def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 解析用户的请求示例代码:import socket import re def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 支持默认index页面示例代码:import socket import re def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 serviceClient(clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 多任务协程版示例代码:import socket import re import gevent from gevent import monkey monkey.patch_all() def serviceClient(clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 1. 创建套接字 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 port = 8888 tcpSocket.bind(("", port)) print("服务器绑定端口为:", port) # 3. 设置为监听套接字 tcpSocket.listen(128) while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 # 创建这个对象之后,gevent就知道了当遇到耗费时间的时候可以切换到这个对象指定的函数中运行 gevent.spawn(serviceClient, clientSocket) # 6. 关闭套接字 tcpSocket.close() if __name__ == "__main__": main() 面向对象版示例代码:import socket import re import gevent from gevent import monkey monkey.patch_all() class WebServer(object): def __init__(self, port): # 1. 创建套接字 self.tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 self.tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 self.port = port self.tcpSocket.bind(("", self.port)) print("服务器绑定端口为:", self.port) # 3. 设置为监听套接字 self.tcpSocket.listen(128) def StartUp(self): while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = self.tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 # 创建这个对象之后,gevent就知道了当遇到耗费时间的时候可以切换到这个对象指定的函数中运行 gevent.spawn(self.serviceClient, clientSocket) # 6. 关闭套接字 self.tcpSocket.close() def serviceClient(self, clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): # 创建服务器 server = WebServer(8888) # 启动服务器 server.StartUp() if __name__ == "__main__": main() 命令行参数版示例代码:import socket import re import gevent import sys from gevent import monkey monkey.patch_all() class WebServer(object): def __init__(self, port): # 1. 创建套接字 self.tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字端口复用 self.tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定本地信息 self.port = port self.tcpSocket.bind(("", self.port)) print("服务器绑定端口为:", self.port) # 3. 设置为监听套接字 self.tcpSocket.listen(128) def StartUp(self): while True: # 4. 等待新的客户端链接 clientSocket, clientAddr = self.tcpSocket.accept() print(clientAddr, "连接成功") # 5. 为这个客户端服务 # 创建这个对象之后,gevent就知道了当遇到耗费时间的时候可以切换到这个对象指定的函数中运行 gevent.spawn(self.serviceClient, clientSocket) # 6. 关闭套接字 self.tcpSocket.close() def serviceClient(self, clientSocket): """为客户端服务""" # 1. 接收客户端发送过来的http的请求数据 request = clientSocket.recv(1024).decode("utf-8") print(">"*40, "\n", request) # 以行为单位切割,并返回一个列表 requestLines = request.splitlines() print("====>", requestLines) # GET /index.html HTTP/1.1 requestFirstLine = requestLines[0] # 通过正则表达式提取相应的文件名 fileName = re.match(r"[^/]+(/[^ ]*)", requestFirstLine).group(1) # 如果后面没有路径,默认是index.html if fileName == "/": fileName = "/index.html" print("-----请求的文件名是:%s" % fileName) # 3. 根据文件名去读取这个文件的内容,如果有就发送,如果没有就告诉浏览器 这个资源不存在404 try: with open("./html" + fileName, "rb") as f: content = f.read() except Exception as ret: # 意味着没有这个请求的资源 404 # 返回http的应答数据 responseHeader = "HTTP/1.1 404 NOT FOUND\r\n" responseHeader += "\r\n" responseBody = "------file not found-------".encode("utf-8") else: # 意味着 有这个对应的资源 200 # 返回http的应答数据 responseHeader = "HTTP/1.1 200 OK\r\n" responseHeader += "\r\n" responseBody = content # 返回http的应答数据 clientSocket.send(responseHeader.encode("utf-8")) #先发头 clientSocket.send(responseBody) #先发body # 4. 关闭套接字 clientSocket.close() def main(): if len(sys.argv) < 2: print('您需要指定监听端口!') return # 获取用户指定的绑定端口 port = int(sys.argv[1]) # 创建服务器 server = WebServer(port) # 启动服务器 server.StartUp() if __name__ == "__main__": main()
2017年08月12日
1,647 阅读
0 评论
0 点赞
2017-08-11
Pyhton socket套接字之UDP编程
Pyhton的socket套接字之UDP编程是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的。创建socket 在 Python 中 使用socket 模块的函数 socket 就可以完成:import socket socket.socket(AddressFamily, Type) 函数 socket.socket 创建一个 socket,该函数带有两个参数: AddressFamily:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议) 示例代码:import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #创建udp的套接字 #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #创建tcp的套接字 # ...这里是使用套接字的功能(省略)... # 不用的时候,关闭套接字 s.close() 套接字使用流程与文件的使用流程很类似: 创建套接字 使用套接字收/发数据 关闭套接字 UDP 不提供可靠性的传输,它只是把应用程序传给 IP 层的数据报发送出去,但是并不能保证它们能到达目的地。由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。对于 UDP发送方流程, 有点类似于写信过程: 找个邮政工作人员(socket()) 信封上写上地址同时里面装上信件内容并且投递(sendto() ) 还可以继续写信,或者,接收对方的回信(recvfrom() ) 打完收工(close() ) 示例代码:import socket #1、创建套接字 udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #2、发送数据 sendData = input("请输入要发送的数据:") udp.sendto(sendData.encode("utf-8"), ("172.16.1.52", 8080)) #3、接收数据 recvData = udp.recvfrom(1024) print("recvData = ", recvData) #4、关闭套接字 udp.close() UDP 发送方注意点 本地IP、本地端口(我是谁) 目的IP、目的端口(发给谁) 在发送方的代码中,我们只设置了目的IP、目的端口 发送方的本地 ip、本地 port 是我们调用 sendto 的时候系统底层自动给客户端分配的。分配端口的方式为随机分配,即每次运行系统给的 port 不一样。 python3编码转换字符串通过编码成为字节码,字节码通过解码成为字符串:str -> bytes: encode编码 bytes -> str: decode解码 示例如下:>>> text = '我是文本' >>> text '我是文本' >>> print(text) 我是文本 >>> bytesText = text.encode() >>> bytesText b'\xe6\x88\x91\xe6\x98\xaf\xe6\x96\x87\xe6\x9c\xac' >>> print(bytesText) b'\xe6\x88\x91\xe6\x98\xaf\xe6\x96\x87\xe6\x9c\xac' >>> type(text) <class 'str'> >>> type(bytesText) <class 'bytes'> >>> textDecode = bytesText.decode() >>> textDecode '我是文本' >>> print(textDecode) 我是文本 其中decode()与encode()方法可以接受参数,其声明分别为:bytes.decode(encoding="utf-8", errors="strict") str.encode(encoding="utf-8", errors="strict") 其中的encoding是指在解码编码过程中使用的编码(此处指“编码方案”是名词),errors是指错误的处理方案。详细的可以参照官方文档: str.encode() bytes.decode() 元组拆包>>> addr = ("127.0.0.1", 8888) >>> ip, port = addr >>> type(ip) <class 'str'> >>> type(port) <class 'int'> >>> print(ip) 127.0.0.1 >>> print(port) 8888 示例代码:recvData, recvAddr = udp.recvfrom(1024) print(recvAddr, " >>>>>>>>>>>>> ", recvData.decode("utf-8")) 接收端接收数据的条件UDP网络程序想要收取数据需什么条件? 确定的 ip 地址 确定的端口(port) 这正如,我要收到别人寄过来的信,我必须告诉别人我的地址(ip),同时告诉别人我我的公寓信箱号(端口)。接收端使用 bind() 函数,来完成地址结构与 socket 套接字的绑定,这样 ip、port 就固定了,发送端在 sendto 函数中指定接收端的 ip、port,就可以发送数据了。2.3.4.2 示例代码对于 UDP 服务器编程流程, 有点类似于收信过程: 找个邮政工作人员( socket() ) 确定信箱的位置:地址+信箱号(bind() ) 等待对方的来信( recvfrom() ) 还可以回信(sendto() ),或者,继续等待对方的来信…… 打完收工(close() )
2017年08月11日
1,032 阅读
0 评论
0 点赞
1
2