你只知道with,那with该with who呢?

你只知道with,那with该with who呢?

 

在长期的编程实践中,我们必然已经有过使用下面这段代码的经验:

with open(“test.txt”, “r”, encoding=”utf-8″) as f:
s = f.readlines()
有的人知道这么写的原因;但也有很多人不知道,只是单纯地“别人都这么写,我也应该这么写”。

同时,很多知道原因的人也只是知其然而不知其所以然:with语句可以替我们自动关闭打开的文件对象。但是这是通过什么机制办到的呢?

1. with和异常处理
我们知道,如果不使用with语句的话,正常地读写一个文件应该经过这些过程:打开文件、操作文件、关闭文件。表达为Python代码如下:

f = open(“test.txt”, “r”, encoding=”utf-8″)
s = f.readlines()
f.close()
在正常情况下,这样写看起来也没啥问题。

接下来我们就人为制造一点“意外”:把打开文件对象时指定的模式由“r”改为“w”。

f = open(“test.txt”, “w”, encoding=”utf-8″)
s = f.readlines()
f.close()
此时,当程序执行到第2行读取文件内容时,就会抛出错误:

Traceback (most recent call last):
File “test_with.py”, line 2, in <module>
s = f.readlines()
io.UnsupportedOperation: not readable
然后……一个可怕的情况就发生了。

Python产生未处理的异常从而退出了,导致第2行之后的代码尚未执行,因此**f.close()**也就再也没有机会执行。一个孤魂野鬼般打开的文件对象就这样一个人漂泊在内存的汪洋大海中,没有人知道他是谁、他从哪儿来、他要去哪儿。

就这样,每当抛出一次异常,就会产生这么一个流浪对象。久而久之,内存的汪洋大海也就顺理成章被改造成了流浪者的乐土,其他人想来压根儿没门儿。

追根究底,我们发现导致这个问题的关键在于“打开-操作-关闭”文件这个流水操作中,存在抛出异常的可能。

所以我们想到了使用Python为我们提供的大杀器,来对付这些异常:try-catch。

用异常处理改造一下前面的代码:

try:
f = open(“test.txt”, “a”, encoding=”utf-8″)
s = f.readlines()
except:
print(“出现异常”)
finally:
f.close()
这样一来,通过附加的finally语句,无论文件操作是否抛出异常,都能够保证打开的文件被关闭。从而避免了不断占用资源导致资源泄露的问题。

实际上,with语句正是为我们提供了一种try-catch-finally的封装。

编程时,看似只是随随便便的一个with,其实已经暗地里确保了类似于上面代码的异常处理机制。

2. 上下文管理器
with要生效,需要作用于一个上下文管理器——

打住,到底什么是上下文管理器呢?

长话短说,就是实现了__enter__和__exit__方法的对象。

在进入一个运行时上下文前,会先加载这两个方法以备使用。进入这个运行时上下文时,调用__enter__方法;退出该上下文前,则会调用__exit__方法。

这里的“运行时上下文”,可以简单地理解为一个提供了某些特殊配置的代码作用域。

当我们使用with open(“test.txt”, “r”, encoding=”utf-8″) as f这句代码时,Python首先对open(“test.txt”, “r”, encoding=”utf-8″)求值,得到一个上下文管理器。

这里有一点特殊的是,Python中文件对象本身就是一个上下文管理器,因此我们可以使用open函数作为求值的表达式。

随后调用__enter__方法,返回的对象绑定到我们指定的标识符f上。文件对象的__enter__返回文件对象自身,因此这句代码就是将打开的“test.txt”文件对象绑定到了标识符f上。

紧跟着执行with语句块中的内容。

*后调用__exit__,退出with语句块。

根据上面的内容,我们也可以自行构造一个上下文管理器(注意,两个特征方法的参数要与协议一致):

class testContextManager:
def __enter__(self):
print(“进入运行时上下文,调用__enter__方法”)

def __exit__(self, exc_type, exc_value, traceback):
print(“退出运行时上下文,调用__exit__方法”)

with testContextManager() as o:
pass
输出结果:

进入运行时上下文,调用__enter__方法
退出运行时上下文,调用__exit__方法
with语句之所以能够替代繁琐的异常处理语句,正是由于上下文管理器遵循协议实现了__enter__和__exit__方法,而with语句又确保了发生异常时能够执行完__exit__方法,再退出相关运行时上下文。

在这个方法中,我们就可以完成一些必要的清理工作。

总结
本文我们讲解了with语句的内部逻辑,尝试实现了一个自定义的上下文管理器。相信大家对于with的作用方式有了更深刻的领会。

with语句不仅仅可以用于读写文件,还可以用于锁的自动获取和释放、全局状态的保存和恢复等。更多的实用方式留待大家探索。

参考
[官方文档:8.5. `with` 语句](https://docs.python.org/zh-cn/3/reference/compound_stmts.html#the-with-statement)

[官方文档:3.3.9. with 语句上下文管理器](https://docs.python.org/zh-cn/3/reference/datamodel.html#with-statement-context-managers)

[官方文档:上下文管理器类型](https://docs.python.org/zh-cn/3/library/stdtypes.html#typecontextmanager)

Python 小技之 Office 文件转 PDF

Python 小技之 Office 文件转 PDF

 

在平时的工作中,难免需要一些 小Tip 来解决工作中遇到的问题,今天的文章给大家安利一个方便快捷的小技巧,将 Office(doc/docx/ppt/pptx/xls/xlsx)文件批量或者单一文件转换为 PDF 文件。不过在做具体操作之前需要在 PC 安装好 Office,再利用 Python 的 win32com 包来实现 Office 文件的转换操作。

安装 win32com
在实战之前,需要安装 Python 的 win32com,详细安装步骤如下:

使用 pip 命令安装

pip install pywin32

如果我们遇到安装错误,可以通过python -m pip install –upgrade pip更新云端的方式再进行安装即可:

python -m pip install –upgrade pip

下载离线安装包安装
如果 pip 命令未安装成功的话还可以下载离线包安装,方法步骤如下:首先在官网选择对应的 Python 版本下载离线包:https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/ 下载好后傻瓜式安装好即可。

文件转换逻辑
详细代码如下:

class PDFConverter:
def __init__(self, pathname, export=’.’):
self._handle_postfix = [‘doc’, ‘docx’, ‘ppt’, ‘pptx’, ‘xls’, ‘xlsx’] # 支持转换的文件类型
self._filename_list = list()  #列出文件
self._export_folder = os.path.join(os.path.abspath(‘.’), ‘file_server/pdfconver’)
if not os.path.exists(self._export_folder):
os.mkdir(self._export_folder)
self._enumerate_filename(pathname)

def _enumerate_filename(self, pathname):
”’
读取所有文件名
”’
full_pathname = os.path.abspath(pathname)
if os.path.isfile(full_pathname):
if self._is_legal_postfix(full_pathname):
self._filename_list.append(full_pathname)
else:
raise TypeError(‘文件 {} 后缀名不合法!仅支持如下文件类型:{}。’.format(pathname, ‘、’.join(self._handle_postfix)))
elif os.path.isdir(full_pathname):
for relpath, _, files in os.walk(full_pathname):
for name in files:
filename = os.path.join(full_pathname, relpath, name)
if self._is_legal_postfix(filename):
self._filename_list.append(os.path.join(filename))
else:
raise TypeError(‘文件/文件夹 {} 不存在或不合法!’.format(pathname))

def _is_legal_postfix(self, filename):
return filename.split(‘.’)[-1].lower() in self._handle_postfix and not os.path.basename(filename).startswith(
‘~’)

def run_conver(self):
print(‘需要转换的文件数是:’, len(self._filename_list))
for filename in self._filename_list:
postfix = filename.split(‘.’)[-1].lower()
funcCall = getattr(self, postfix)
print(‘原文件:’, filename)
funcCall(filename)
print(‘转换完成!’)
doc/docx 转换为 PDF
doc/docx 转换为 PDF 部分代码如下所示:

def doc(self, filename):
name = os.path.basename(filename).split(‘.’)[0] + ‘.pdf’
exportfile = os.path.join(self._export_folder, name)
print(‘保存 PDF 文件:’, exportfile)
gencache.EnsureModule(‘{00020905-0000-0000-C000-000000000046}’, 0, 8, 4)
pythoncom.CoInitialize()
w = Dispatch(“Word.Application”)
pythoncom.CoInitialize()  # 加上防止 CoInitialize 未加载
doc = w.Documents.Open(filename)
doc.ExportAsFixedFormat(exportfile, constants.wdExportFormatPDF,
Item=constants.wdExportDocumentWithMarkup,
CreateBookmarks=constants.wdExportCreateHeadingBookmarks)
w.Quit(constants.wdDoNotSaveChanges)
def docx(self, filename):
self.doc(filename)

ppt/pptx 转换为 PDF
ppt/pptx 转换为 PDF 部分代码如下:

def ppt(self, filename):
name = os.path.basename(filename).split(‘.’)[0] + ‘.pdf’
exportfile = os.path.join(self._export_folder, name)
gencache.EnsureModule(‘{00020905-0000-0000-C000-000000000046}’, 0, 8, 4)
pythoncom.CoInitialize()
p = Dispatch(“PowerPoint.Application”)
pythoncom.CoInitialize()
ppt = p.Presentations.Open(filename, False, False, False)
ppt.ExportAsFixedFormat(exportfile, 2, PrintRange=None)
print(‘保存 PDF 文件:’, exportfile)
p.Quit()

def pptx(self, filename):
self.ppt(filename)

xls/xlsx 转换为 PDF
def xls(self, filename):
name = os.path.basename(filename).split(‘.’)[0] + ‘.pdf’
exportfile = os.path.join(self._export_folder, name)
pythoncom.CoInitialize()
xlApp = DispatchEx(“Excel.Application”)
pythoncom.CoInitialize()
xlApp.Visible = False
xlApp.DisplayAlerts = 0
books = xlApp.Workbooks.Open(filename, False)
books.ExportAsFixedFormat(0, exportfile)
books.Close(False)
print(‘保存 PDF 文件:’, exportfile)
xlApp.Quit()

def xlsx(self, filename):
self.xls(filename)

执行转换逻辑
if __name__ == “__main__”:
# 支持文件夹批量导入
#folder = ‘tmp’
#pathname = os.path.join(os.path.abspath(‘.’), folder)
# 也支持单个文件的转换
pathname = “G:/python_study/test.doc”
pdfConverter = PDFConverter(pathname)
pdfConverter.run_conver()

还在为多张Excel汇总统计发愁?Python 秒处理真香!

还在为多张Excel汇总统计发愁?Python 秒处理真香!

 

为什么越来越多的非程序员白领都开始学习 Python ?他们可能并不是想要学习 Python 去爬取一些网站从而获得酷酷的成就感,而是工作中遇到好多数据分析处理的问题,用 Python 就可以简单高效地解决。本文就通过一个实际的例子来给大家展示一下 Python 是如何应用于实际工作中高效解决复杂问题的。

背景
小明就职于一家户外运动专营公司,他们公司旗下有好多个品牌,并且涉及到很多细分的行业。小明在这家公司任数据分析师,平时都是通过 Excel 来做数据分析的。今天老板丢给他一个任务:下班前筛选出集团公司旗下*近一年销售额前五名的品牌以及销售额。

对于 Excel 大佬来说,这不就是分分钟的事吗?小明并没有放在眼里,直到市场部的同事将原始的数据文件发给他,他才意识到事情并没有那么简单:

%title插图%num

表格文件(数据来源于网络)
这并不是想象中的排序取前五就行了。这总共有90个文件,按常规的思路来看,他要么将所有文件的内容复制到一张表中进行分类汇总,要么将每张表格进行分类汇总,然后再**终结果进行分类汇总。

想想这工作量,再想想截止时间,小明挠了挠头,感觉到要渐渐头秃。

思路分析
这种体力活,写程序解决是*轻松的啦。小明这时候想到了他的程序员好朋友小段,于是他把这个问题抛给了小段。

小段缕了下他那所剩无几的头发,说:so easy,只需要找潘大师即可。

小明说:你搞不定吗?还要找其他人!

小段苦笑说:不不不,潘大师是 Python 里面一个处理数据的库,叫 Pandas ,俗称 潘大师。

小明说:我不管什么大师不大师,就说需要多久搞定。

小段说:给我几分钟写程序,再跑几秒钟就好了!

小明发过去了膜拜大佬的表情。

小段略微思考了下,整理了一下程序思路:

计算每张表每一行的销售额,用“访客数 * 转化率 * 客单价”就行。

将每张表格根据品牌汇总销售额。

将所有表格的结果汇总成一张总表

在总表中根据品牌汇总销售额并排序

编码
第零步,读取 Excel :

import pandas as pd

df = pd.read_excel(“./tables/” + name)
*步,计算每张表格内的销售额:

df[‘销售额’] = df[‘访客数’] * df[‘转化率’] * df[‘客单价’]

第二步,将每张表格根据品牌汇总销售额:

df_sum = df.groupby(‘品牌’)[‘销售额’].sum().reset_index()
第三步,将所有表格的结果汇总成一张总表:

result = pd.DataFrame()
result = pd.concat([result, df_sum])
第四步,在总表中根据品牌汇总销售额并排序:

final = result.groupby(‘品牌’)[‘销售额’].sum().reset_index().sort_values(‘销售额’, ascending=False)
*后,我们来看看完整的程序:

import pandas as pd
import os

result = pd.DataFrame()

for name in os.listdir(“./tables”):
try:
df = pd.read_excel(“./tables/” + name)
df[‘销售额’] = df[‘访客数’] * df[‘转化率’] * df[‘客单价’]
df_sum = df.groupby(‘品牌’)[‘销售额’].sum().reset_index()
result = pd.concat([result, df_sum])
except:
print(name)
pass

final = result.groupby(‘品牌’)[‘销售额’].sum().reset_index().sort_values(‘销售额’, ascending=False)
pd.set_option(‘display.float_format’, lambda x: ‘%.2f’ % x)
print(final.head())
*后的结果是这样的:

品牌           销售额
15   品牌-5 1078060923.62
8   品牌-17 1064495314.96
4   品牌-13 1038560274.21
3   品牌-12 1026115153.00
13   品牌-3 1006908609.07
可以看到*终的前五已经出来了,整个程序运行起来还是很快的。

几分钟之后,小段就把结果给小明发过去了,小明感动得内牛满面,直呼改天请吃饭,拜师学艺!

Android自动化测试工具合集

安卓app自动化测试工具之一 – PerfectoMobile

该工具的官方网址:PerfectoMobile.com

背景:美国/以色列公司,该工具已有6年历史。

突出特点:测试脚本可以跨平台(Android/iOS/Blackberry…)执行,号称拥有市面上所有智能机。

脚本编辑器有两种方式:一、纯Web的脚本制作界面;二、近年新开发的QTP插件;

脚本语言:Web端的是基于关键字的脚本设计器“ScriptOnce”;如果用QTP插件,则是VBScript。

Web端是鼠标拖拽的方式制作脚本;QTP插件是否可以支持录制就不清楚了。

价格:Web端对于设备的使用是按小时收费。QTP插件的费用还不清楚。相信不会比QTP贵吧~

安卓app自动化测试工具之二 – TestDroid

该工具的官方网址:TestDroid.com

背景:芬兰公司,近两年刚起步,去年年底开始做云平台。

突出特点:测试脚本可以录制,并转成Robotium/MonkeyRunner脚本。

脚本编辑器其实就是Eclipse插件。

可以用录制的方式产生脚本,并生成Robotium or MonkeyRunner的脚本语言。但这个前提是一定要有被测应用的源代码。官方文档虽然说不用源码也能测,只是抓不到R-Class级别的对象。但笔者试了一下没有源码的apk,好像文本框的顺序还无法辨认。

价格:USD99/Month,买够一年还可以打5折。云端价格暂未公开。

安卓app自动化测试工具之三 – DroidPilot

该工具的官方网址:DroidPilot.cn

背景:深圳公司,今年刚起步。

突出特点:抓取对象能力较强;工具仿制QTP,易于测试人员上手。

自己写的脚本编辑器,仿QTP使用VBScript语言。

使用脚本设计器,通过抓取的对象设计脚本,然后把设计好的脚本转换成VBScript进行深加工。据开发团队声称,测试工程师在制作脚本的时候录制的效率不一定有制作的效率高,且也不一定灵活。不过他们表明会在后续版本开发录制功能。

有类似QTP的检查点语句Checkpoint; 也可以写条件判断语句对比属性值。

价格未定,目前开放试用下载,试用期限不够的话还可以跟他们谈。

安卓app自动化测试工具之四 – LessPainful

该工具的官方网址:lesspainful.com

背景:丹麦公司,这两年刚起步。

突出特点:支持iOS & Android;只需提供被测apk和脚本到他们的网站即可测试;脚本很特别。

测试工程师就像写测试用例那样写脚本,都不需要录制功能了。

价格,按月收费。

安卓app自动化测试工具之五 – DeviceAnywhere

该工具的官方网址:deviceanywhere.com

背景:美国公司,做了好几年了。

突出特点:号称支持所有平台;与测试管理工具整合。

这家公司提供的是一整套解决方案。不单有测试管理工具,设备监控工具,甚至还有移动应用开发工具。

价格很贵。

安卓app自动化测试工具之六 – JamoSolutions

该工具的官方网址:jamosolutions.com

背景:比利时公司,做了好几年了。

突出特点:提供QTP、Eclipse、Visual Studio插件;可以跨平台iOS/Android/Blackberry。

通过插件形式工作的,脚本编辑器和脚本语言视乎开发工具(QTP、Eclipse、Visual Studio)而定。

价格不明,估计不会比开发工具贵。

安卓app自动化测试工具之七 – bsquare – TestQuest CountDown

该工具的官方网址:bsquare.com

背景:美国公司,做了好几年了。

突出特点:跨平台;与测试管理工具整合。

整合这家公司自身的Test Designer/Test Manager/Test Runner之类的工具。

价格不清楚,听说有点贵。

安卓app自动化测试工具之八 – ZAP-fiX

该工具的官方网址:zap-fix.com

背景:美国公司,做了好几年了。

突出特点:QTP插件;跨平台。

可以跨Android/iOS测试。

价格不详,肯定不会比QTP卖的贵。

安卓app自动化测试工具之九 – eggPlant

该工具的官方网址:testplant.com

背景:美国公司,做了好几年了。

突出特点:跨平台;整合测试管理工具。

可以跨Android/iOS/Blackberry/Windows Phone等。

价格不详。由于可以与Rational Quality Manager整合,所以估计不会比Rational的工具卖的贵吧。

安卓app自动化测试工具之十 – Testin

该工具的官方网址:testin.cn

背景:北京公司,近两年刚起步。

突出特点:跨平台。

只能录制脚本,无法编辑。

可以跨Android/iOS,但是好像脚本要分开录制。

价格不详。应该不贵。

安卓app自动化测试工具之十一 – ExperiTest – SeeTestMobile

该工具的官方网址:experitest.com

背景:美国公司,近两年刚起步。

突出特点:可录制;跨平台。

支持不同工具的Plug-in,脚本语言视乎工具而定。

可以跨Android/iOS/Blackberry/Windows Phone。

价格:SeeTestMobile – $2499USD/Year。

安卓app自动化测试工具之十二 – AndroidTester

该工具的官方网址:androidtester.net

背景:上海公司,近两年刚起步。

突出特点:可录制。

独立编辑器,Python脚本语言。

只支持Android。

价格不详,应该不贵。

安卓app自动化测试工具之十三 – SmartRobot

该工具的官方网址:dongzhousoft.com

背景:北京公司,近两年刚起步。

突出特点:可录制,与测试管理平台整合。

独立编辑器,可生成Robotium或MonkeyRunner脚本语言。

只支持Android。

价格不详,应该不贵。

安卓app自动化测试工具之十四 – Others

除了上述介绍的商业工具,Android自动化测试其实还有很多开源工具,大家可以陆续学习,这里尝试列举一些:

1. Robotium – robotium.org – 地球人都知道。

2. MonkeyRunner – 自己上网搜吧~

3. WindRiver – windriver.com – 这家厂其实是做芯片的,但是他们也有一个自动化测试框架,好像是不卖的。

4. Robolectric – 这其实是个单元测试框架。

5. Sikuli – sikuli.org – 这家专门做图像比较的。

*后加上一个

手游测试工具-TestBird

官方网址:http://www.testbird.com

终端OS:支持 Android OS和iOS

游戏平台:Cocos2d-x, unity3d

背景:Testbird总裁李伟是原华为GSM研发总裁,公司管理团队(CEO/CTO/COO)均在华为工作10年以上,核心技术团队有10年以上测试经验,精通测试管理、设计和评估、工具和平台开发等模块工作。

突出特点:专注于手游测试服务,是全球*手游自动化云测试平台,以高效手游测试流程和200多款手机游戏测试用例完成超过1万份游戏测试报告,为全球200多家手机游戏厂家和渠道提供手游测试服务。

Python入门:大数据采集不推荐用免费的网络代理ip,为什么?

%title插图%num

为了更方便地更改ip地址,我们选择使用http代理。使用在线http代理,可以帮助网络推广,让客户知道我们的品牌;使用爬虫,可以帮助收集客户需求,为客户提供更强大的商品服务等等。

为了消除ip地址的这种限制,*好选择使用代理IP来执行交换IP操作,以便我们的工作能够正常进行。

但是在选择http代理的时候,我们将尽可能少的使用IP代理。下面是一些常用的http代理基本功能。

因为免费代理的用户很多,毕竟是免费的,所以很多人都会选择,这就导致了免费代理可以使用的IP资源非常少,在更换IP的操作上浪费了大量的时间,降低了工作效率。

而且你所选择的免费代理也有可能与他人同时使用,如果他人的账号出现一些违规行为,那么你的账号就会被连累,IP和账号被封的风险也会大大增加。

此外,免费代理均为透明代理,其主要特点之一就是IP无法隐藏,使其访问信息暴露,不利于个人信息安全。

因此,综合这么多情况,在使用代理ip时,必须选择高匿名、高性能的付费代理ip哦。

爬虫采集、HTTP代理相关问题可以+威“zhimaip01”咨询哦

Office 文件转 PDF 之服务实战

Office 文件转 PDF 之服务实战

 

小编之前写了一篇关于 Office 文件转 PDF 的实战文章,详见Python 小技之 Office 文件转 PDF但是在平时的工作中,咱们需要通过接口的形式来调用具体的转换逻辑,同时开可以将文件转换写成服务的形式,将服务开启后传入参数或者地址即可直接调用逻辑转换,今天的文章主要讲解如何将文件转换写成服务;

一起拭目以待吧!!!

文件服务器
Office 文件转 PDF 涉及到文件的传输,所以考虑用文件服务器来进行文件的传递,客户端如果有文件服务器的话, 同样在服务端也需要写一个文件服务器来返回转换完成的文件,以供客户端使用,文件服务器实现思路如下:

前端文件上传下载页面
<body>
<form action=”download” method=”GET”,enctype=”multipart/form-data”>
要下载的文件: <input type=”text” value=”请上传文件” name=”filename” />
<input type=”submit” value=”download”>
</form>

<form action=”/file_server/cgi-bin/upload.py” method=”POST” enctype=”multipart/form-data”>
要上传的文件: <input type=”file” name=”filename” />
<input type=”submit” value=”upload”>
</form>
上传文件
上传文件 upload.py 部分代码如下:

import cgi, os

form = cgi.FieldStorage()

item = form[“filename”]

if item.filename:
fn = os.path.basename(item.filename)
open(“/home/sxhlinux/data/” + fn, “wb”).write(item.file.read())
msg = “File” + fn + ‘ upload successfully !’
else:
msg = ‘no file is uploaded ‘

print(“””\
Content-type: text/html\n
<html>
<head>
<meta charset=”utf-8”>
<title>Hello world</title>
</head>
<body>
<h2>名称: %s</h2>
</body>
<html>
“”” % (msg,))
下载文件
下载文件 download.py 部分代码如下:

form = cgi.FieldStorage()

filename = form.getvalue(‘filename’)

dir_path = “F:/WorkSpace/FilesToPDF/file_server/tmp”

target_path = dir_path + str(filename)

if os.path.exists(target_path) == True:
print(“Content-Type: application/octet-stream”)
print(‘Content-Disposition: attachment; filename = “%s”‘ % filename)

print(target_path)

sys.stdout.flush()
fo = open(target_path, “rb”)
sys.stdout.buffer.write(fo.read())
fo.close()
else:
print(“””\
Content-type: text/html\n
<html>
<head>
<meta charset=”utf-8″>
<title>File server</title>
</head>
<body>
<h1> %s doesn’t exist in the server:
files in the server list below: </h1>””” % filename)

for line in os.popen(filename):
name = line.strip().split(‘ ‘, 8)
type(name)
if len(name) == 9:
print(“””
<form action=”/cgi-bin/download.py” method=”get”>%s
<input type=”submit” name=”filename” value=”%s”>
</form>””” % (line, name[8]))
文件服务
文件服务入口 server.py 部分代码如下:

if __name__ == ‘__main__’:
try:
handler = CGIHTTPRequestHandler
handler.cgi_directories = [‘/cgi-bin’, ‘/htbin’]
#port = int(sys.argv[1])
port = 8001
print(‘port is %d’ % port)
server = HTTPServer((”, port), handler)
print(‘Welcome to my website !’)
server.serve_forever()

except KeyboardInterrupt:
print(‘^C received, shutting down server’)
server.socket.close()
以上服务启动后即可使用。

转换逻辑接口
转换接口使用了 Python Flask框架,在文件转换逻辑里面需要引入具体的转换逻辑,接口逻辑实现代码如下:

from flask import Flask, render_template,request
import requests
import config
from requests import get
import files2pdf  # 引入转换逻辑
import os, shutil
from flask import Flask, render_template, url_for, send_from_directory,json,make_response,jsonify

app = Flask(__name__, static_folder=’/static’)
app.config.from_object(config)
app.config[“SECRET_KEY”] = “123456”

@app.route(‘/’)
def index():
return “Welcome to You,Please visit the url:http://IP:5000/upload_file?filePath=xxx.pptx”

#pathname = ”
@app.route(‘/upload_file’, methods=[‘GET’, ‘POST’])
def upload_file():
if request.method == ‘POST’ or request.method == ‘GET’:
file_url = request.args.get(‘filePath’, ”)
req = get(file_url)
if req.status_code == 404:
print(“no file”)

return
# 取正确的转换文件名称
filename = file_url.split(‘/’)[-1]
name = filename.split(‘?’)[1]
ppt_name = name.split(‘=’)[-1]
print(ppt_name)

with open(ppt_name, “wb”) as file:
# get request
# response = get(url)
# write to file
file.write(req.content)

# 判断要转换的文件是否存在
if os.path.exists(ppt_name) and not os.path.exists(file_path + ‘/’ + ppt_name):
shutil.move(ppt_name, file_path)
elif os.path.exists(ppt_name) and os.path.exists(file_path + ‘/’ + ppt_name):
print(‘file alreadly exists’)
os.remove(ppt_name)
print(“already deleted exists file”)

pdfConverter = files2pdf.PDFConverter(file_path + ‘/’ + ppt_name)  # 调用文件转换逻辑
# print(“transform complete:”+pdfConverter)
pdfConverter.run_conver()

# file transform finshed –> Get file’s name
re_name = ppt_name.replace(‘.pptx’, ‘.pdf’)
print(‘New name is:’ + re_name)
# /static/%E6%B0%B4%E9%92%A2%E9%9B%86%E5%9B%A2%E6%99%BA%E6%85%A7%E5%85%9A%E5%BB%BA%E5%9F%B9%E8%AE%AD%E8%B5%84%E6%96%990420%282%29.pdf
re_url = ‘IP:8001’  # 文件服务地址
# urlfor = url_for(‘static’, filename=re_name)
return re_url + url_for(‘static’, filename=re_name)
#return  render_template(‘result_link.html’)
if __name__ == ‘__main__’:
app.run(host=’0.0.0.0′, port=5000)

调用
先启动 server.py 后再启动 flaskdemo.py 传入参数即可调用逻辑。也可以用 postman 调用,调用实例如下:

%title插图%num

import 到底在干啥

学了半天,import 到底在干啥?

 

Python凭什么就那么好用呢?

毫无疑问,大量现成又好用的内置/第三方库功不可没。

那我们是怎么使用它们的呢?

噢,对了~是用的import xxx这个语句。

之所以会有此一问,也是之前有一次使用PyCharm进行开发时(又)踩了个坑……

废话少说,先讲问题
像下面这样一个项目结构:

Projetc_example
|– A
|– alpha.py
|– beta.py
|– B
|– theta.py
|– main
|– main.py
假设要在main.py中导入theta.py:

# main/main.py
from B import theta

显然会导致我们所不希望的问题,即Python不知道要到哪里去找这个名为B的模块(包是一种特殊的模块):

Traceback (most recent call last):
File “main/main.py”, line 1, in <module>
from B import theta
ModuleNotFoundError: No module named ‘B’
可是这就奇了怪了,为啥同样的代码,在PyCharm里运行就是好的了呢?

import的查找路径
于是我们不辞艰辛,上下求索,原来在Python中,import语句实际上封装了一系列过程。

1. 查找是否已导入同名模块
首先,Python会按照import xxx中指定的包名,到sys.modules中查找当前环境中是否已经存在相应的包——不要奇怪为什么都没有导入sys这个模块就有sys.modules了。

sys是Python内置模块,也就是亲儿子,导入只是意思一下,让我们这样的外人在导入的环境中也可以使用相关接口而已,实际上相应的数据对Python而言从始至终都是透明的。

我们可以导入sys查看一下这个对象的具体内容(节省篇幅,做省略处理):

>>> import sys
>>> sys.modules
{‘sys’: <module ‘sys’ (built-in)>, ‘builtins’: <module ‘builtins’ (built-in)>, …’re’: <module ‘re’ from ‘E:\\Anaconda\\Anaconda\\lib\\re.py’>, …}
这些就都是Python一开始就已经加载好的模块,也就是安装好Python之后,只要一运行环境中就已经就绪的模块——只是作为外人的我们还不能直接拿过来用,得跟Python报备一声:“欸,我要拿您儿子来用了嗨~”

很容易可以发现,sys.modules中列出来的已加载模块中存在明显的不同,前面的很多模块显得很干净,而后面的很多模块都带有from yyy’的字样,并且这个yyy看起来还像是一个路径。

这就关系到我们接下来要讲的步骤了。

2. 在特定路径下查找对应模块
前面我们讲到了,当我们导入某个模块时,Python先会去查询sys.modules,看其中是否存在同名模块,查到了那当然皆大欢喜,Python直接把这个模块给我们用就好了,毕竟儿子那么多,借出去赚点外快也是好事儿不是?

可问题在于:那要是没找到呢?

这显然是一个很现实的问题。毕竟资源是有限的,Python不可能把你可能用到的所有模块全都一股脑给加载起来,否则这样男上加男加男加男……谁也顶不住啊不是(大雾

于是乎就有人给Python出了个主意:那你等到要用的时候,再去找他说他是你儿子呗

Python:妙哇~

有了这个思路,Python就指定了几家特定的酒楼,说:“凡是去消费的各位,都可以给我当儿子。”

就这样,一些本来不是Python亲儿子的人,出于各种原因聚集到了这几家酒楼,以雇佣兵的身份随时准备临时称为Python的儿子。

这可就比周文王开局就收100个义子优雅多了,养家糊口的压力也就没那么大了(Python:什么?我的亲儿子都不止100个?你说什么?听不见啊——

回到正经的画风来——

实际上,在Python中,sys.path维护的就是这样一个py交易的结果~~(诶?好像莫名发现了什么),其中保存的内容就是这几家“指定酒楼”,也就是当Python遇到不认识的儿子~~模块时,就会去实地查找的路径。

我们也可以打印出来看看具体内容:

>>> sys.path
[”, ‘E:\\Anaconda\\Anaconda\\python37.zip’, ‘E:\\Anaconda\\Anaconda\\DLLs’, ‘E:\\Anaconda\\Anaconda\\lib’, ‘E:\\Anaconda\\Anaconda’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin’]
大体上就是安装环境时配置的一些包所在路径,其中*个元素代表当前所执行脚本所在的路径。

也正是因此,我们可以在同一个目录下,大大方方地调用其他模块。

3. 将模块与名字绑定
找到相应的非亲生模块还没完,加载了包还得为它分配一个指定的名字,我们才能在脚本中使用这个模块。

当然多数时候我们感知不到这个过程,因为我们就是一个import走天下:

import sys
import os
import requests
这个时候我们指定的模块名,实际上也是指定的稍后用来调用相应模块的对象名称。

换个更明显的:

import requests as req
如果这个时候只使用了第二种方式来导入requests这个模块,那么很显然在之后的程序流程中,我们都不能使用requests这个名字来调用它而应当使用req。

这就是Python导入过程中的名称绑定,本质上与正常的赋值没有太大区别,加载好了一个对象之后,然后为这个对象赋一个指定的变量名。

当然即使是已经加载好的模块,我们也可以利用这个名称绑定的机制为它们取别名,比如:

>>> import sys
>>> import sys as sy
>>> sys.path
[”, ‘E:\\Anaconda\\Anaconda\\python37.zip’, ‘E:\\Anaconda\\Anaconda\\DLLs’, ‘E:\\Anaconda\\Anaconda\\lib’, ‘E:\\Anaconda\\Anaconda’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin’]
>>> sy.path
[”, ‘E:\\Anaconda\\Anaconda\\python37.zip’, ‘E:\\Anaconda\\Anaconda\\DLLs’, ‘E:\\Anaconda\\Anaconda\\lib’, ‘E:\\Anaconda\\Anaconda’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib’, ‘E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin’]
>>> sys == sy
True
问题解决
好了,上面就是对Python导入机制的大致介绍,但是说了半天,我们的问题还没有解决:在项目中如何简洁地跨模块导入其他模块?

在使用PyCharm的时候倒是一切顺遂,因为PyCharm会自动将项目的根目录加入到导入的搜索路径,也就是说像下面这样的项目结构,在任意模块中都可以很自然地通过import A导入模块A,用import B导入模块B。

Projetc_example
|– A
|– alpha.py
|– beta.py
|– B
|– theta.py
|– main
|– main.py
但是在非IDE环境中呢?或者说就是原生的Python环境中呢?

很自然地我们就会想到:那就手动把项目根目录加入到sys.path中去嘛。说起来也跟PyCharm做的事没差呀

可以,贫道看你很有悟性,不如跟我去学修仙吧

所以我们就通过sys和os两个模块七搞八搞(这两个模块以前有过介绍,不再赘述)——

噔噔噔噔——好使了

# Peoject_example/A/alpha.py
print(“name: ” + __name__)
print(“file: ” + __file__)

def al():
print(“Importing alpha succeeded.”)
main.py中则加入一个逻辑,在sys.path中增加一个项目根目录:

import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

import A.alpha

A.alpha.al()

# name: A.alpha
# file: *\Project_example\A\alpha.py
# Importing alpha succeeded.
大功告成,风紧扯呼~

 

自学Python:掌握Socks5代理协议意义何在?

SOCKS5的概念?
一种使用TCP/IP协议进行通前端和服务器之间发挥中介作用,使内部网络的前端机器能够访问互联网的服务器,使通信更加安全。

通过将前端请求转发到真正的目标服务器,SOCKS5服务器模拟了前端行为。

在这种情况下,前端和SOCKS5通过TCP/IP协议进行通信,前端向SOCKS5服务器发送请求,而SOCKS5服务器向实际的服务器发送请求。

SOCKS5服务器在向真实服务器发送通信请求时,对请求包本身没有任何变化。SOCKS5服务器接到真正的服务器反应后,也直接转发到前端。

所以SOCKS5协议是代理协议,可以适用于各种基于TCP/IP的应用层协议,几乎是全能的。虽然不能了解自己转发的数据内部结构,但可以实现对通信包的忠实转发,完成协议需要完成的功能。

与SOCKS5协议不同,HTTP代理商通过HTTP协议实现,HTTP代理服务器软件了解通信包的内部结构,在转发过程中还需要修改和转换通信包的程序。与HTTP代理协议不同,SOCKS5实际上是一个传输层代理协议。

假如每一个特定的应用层协议必须设计出相应的代理协议表示,那么可以想象,一个特定的代理服务器不可能支持这么多新协议。可以说,SOCKS5的出现缓解了不同协议对代理协议的特殊需求。

Python 教你如何给图像分类

Python 教你如何给图像分类

 

在日常生活中总是有给图像分类的场景,比如垃圾分类、不同场景的图像分类等;今天的文章主要是基于图像识别场景进行模型构建。图像识别是通过 Python深度学习来进行模型训练,再使用模型对上传的电子表单进行自动审核与比对后反馈相应的结果。主要是利用 Python Torchvision 来构造模型,Torchvision 服务于Pytorch 深度学习框架,主要是用来生成图片、视频数据集以及训练模型。

模型构建
构建模型为了直观,需要使用 Jupyter notebook 进行模型的构建,Jupyter notebook 的安装及使用详见公众号历史文章 一文吃透 Jupyter Notebook,进入 JupyterNotebook 页面后即可进行编辑。详细页面如下:

导入所需包
图像识别需要用到深度学习相关模块,所以需要导入相应的包,具体导入的包如下:

%reload_ext autoreload
%autoreload 2

import torch
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision import transforms as tfs
from torchvision import models
from torch import nn

import matplotlib.pyplot as plt
%matplotlib inline

import os
os.environ[“KMP_DUPLICATE_LIB_OK”]=”TRUE”

是否使用 GPU
模型的训练主要方式是基于 GPU 或者 CPU 训练,在没有 GPU 的条件下就在 CPU 下进行训练,模型的训练需要花费一定的时间,训练时长根据训练集的数据和硬件性能而定,训练结果精确性根据数据的多少和准确性而且,深度学习需要大量的素材才能判断出精确的结果,所以需要申明使用 CPU 进行训练:

# 是否使用GPU
use_gpu = False
数据增强
将拿到的数据进行训练集的数据预处理并设置训练分层数,再将拿到的图片进行水平翻转后对图片进行剪裁, 剪裁后将图片进行随机翻转,增强随机对比度以及图片颜色变化

# 数据增强
train_transform = tfs.Compose([
# 训练集的数据预处理
tfs.Resize([224, 224]),
tfs.RandomHorizontalFlip(),
tfs.RandomCrop(128),
tfs.ToTensor(),
tfs.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
])

test_transform = tfs.Compose([
tfs.Resize([224,224]),
#     tfs.RandomCrop(128),
tfs.ToTensor(),
tfs.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
])

# 每一个batch的数据集数目
batch_size = 10

数据集和验证集准备
模型训练需要准备数据集和验证集,只有足够的照片才能得到更精准的答案。训练集和验证集部分代码如下:

# 构建训练集和验证集
#
train_set = ImageFolder(‘./dataset1/train’, train_transform)
train_data = DataLoader(train_set, batch_size, shuffle=True, num_workers=0)

valid_set = ImageFolder(‘./dataset1/valid’, test_transform)
valid_data = DataLoader(valid_set, 2*batch_size, shuffle=False, num_workers=0)

train_set.class_to_idx

len(valid_data)

# 数据集准备
try:
if iter(train_data).next()[0].shape[0] == batch_size and \
iter(valid_data).next()[0].shape[0] == 2*batch_size:
print(‘Dataset is ready!’)
else:
print(‘Not success, maybe the batch size is wrong’)
except:
print(‘not success, image transform is wrong!’)
模型构建并准备模型
# 构建模型
def get_model():
model = models.resnet50(pretrained=True)
model.fc = nn.Linear(2048, 3)
return model

try:
model = get_model()
with torch.no_grad():
scorce = model(iter(train_data).next()[0])
print(scorce.shape[0], scorce.shape[1])
if scorce.shape[0] == batch_size and scorce.shape[1] == 3:
print(‘Model is ready!’)
else:
print(‘Model is failed!’)
except:
print(‘model is wrong’)

if use_gpu:
model = model.cuda()
构建模型优化器
# 构建loss函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr = 1e-4)

# 训练的epoches数目
max_epoch = 20

模型训练和训练结果可视化
数据集和训练集准备好后进行模型训练和训练结果可视化,部分代码如下:

def train(model, train_data, valid_data, max_epoch, criterion, optimizer):
freq_print = int(len(train_data) / 3)

metric_log = dict()
metric_log[‘train_loss’] = list()
metric_log[‘train_acc’] = list()
if valid_data is not None:
metric_log[‘valid_loss’] = list()
metric_log[‘valid_acc’] = list()

for e in range(max_epoch):
model.train()
running_loss = 0
running_acc = 0

for i, data in enumerate(train_data, 1):
img, label = data
if use_gpu:
img = img.cuda()
label = label.cuda()

# forward前向传播
out = model(img)

# 计算误差
loss = criterion(out, label.long())

# 反向传播,更新参数
optimizer.zero_grad()
loss.backward()
optimizer.step()

# 计算准确率
_, pred = out.max(1)
num_correct = (pred == label.long()).sum().item()
acc = num_correct/img.shape[0]

running_loss += loss.item()
running_acc +=acc

if i % freq_print == 0:
print(‘[{}]/[{}], train loss: {:.3f}, train acc: {:.3f}’ \
.format(i, len(train_data), running_loss / i, running_acc / i))

metric_log[‘train_loss’].append(running_loss / len(train_data))
metric_log[‘train_acc’].append(running_acc / len(train_data))

if valid_data is not None:
model.eval()
running_loss = 0
running_acc = 0
for data in valid_data:
img, label = data
if use_gpu:
img = img.cuda()
label = label.cuda()

# forward前向传播
out = model(img)

# 计算误差
loss = criterion(out, label.long())

# 计算准确度
_, pred = out.max(1)
num_correct = (pred==label.long()).sum().item()
acc = num_correct/img.shape[0]

running_loss += loss.item()
running_acc += acc

metric_log[‘valid_loss’].append(running_loss/len(valid_data))
metric_log[‘valid_acc’].append(running_acc/len(valid_data))
print_str = ‘epoch: {}, train loss: {:.3f}, train acc: {:.3f}, \
valid loss: {:.3f}, valid accuracy: {:.3f}’.format(
e+1, metric_log[‘train_loss’][-1], metric_log[‘train_acc’][-1],
metric_log[‘valid_loss’][-1], metric_log[‘valid_acc’][-1])
else:
print_str = ‘epoch: {}, train loss: {:.3f}, train acc: {:.3f}’.format(
e+1,
metric_log[‘train_loss’][-1],
metric_log[‘train_acc’][-1])
print(print_str)

# 可视化
nrows = 1
ncols = 2
figsize= (10, 5)
_, figs = plt.subplots(nrows, ncols, figsize=figsize)
if valid_data is not None:
figs[0].plot(metric_log[‘train_loss’], label=’train loss’)
figs[0].plot(metric_log[‘valid_loss’], label=’valid loss’)
figs[0].axes.set_xlabel(‘loss’)
figs[0].legend(loc=’best’)
figs[1].plot(metric_log[‘train_acc’], label=’train acc’)
figs[1].plot(metric_log[‘valid_acc’], label=’valid acc’)
figs[1].axes.set_xlabel(‘acc’)
figs[1].legend(loc=’best’)
else:
figs[0].plot(metric_log[‘train_loss’], label=’train loss’)
figs[0].axes.set_xlabel(‘loss’)
figs[0].legend(loc=’best’)
figs[1].plot(metric_log[‘train_acc’], label=’train acc’)
figs[1].axes.set_xlabel(‘acc’)
figs[1].legend(loc=’best’)
调参进行模型训练
# 用作调参
train(model, train_data, valid_data, max_epoch, criterion, optimizer)
保存模型
# 保存模型
torch.save(model.state_dict(), ‘./model/save_model2.pth’)

根据爬虫采集的具体应用挑选代理IP地址类型

众所周知,网络爬虫程序的工作是通过代理ip来完成的,但要使其正常运行,就必须要减少代理ip。

对于从事互联网大数据工作的人来说,网页爬虫程序并不陌生。网络大数据时代,数据信息尤为重要,互联网业务范围广泛,各种业务所需的数据和规模各不相同。当大量收集数据信息时,应该使用哪个代理ip?

在Web爬虫中大规模收集数据信息必须使用高级代理中的高质量短效代理ip,但如果要大量使用,则需要大量的测试。

优质ip的运营优势是稳定安全绿色,上线速度*快,效率高,ip的资源也非常丰富,专注于完全隐藏真实的ip。

你可以在HTTP代理ip官网上看到各种类型的代理ip,根据自己实际使用情况来选择。