小白必看:Python设置ip代理访问网页安全吗?

使用代理ip访问网站的时候,本机会先把数据发送到代理服务器,代理服务器跟网站之间是加密的,所以浏览器会显示当前通信已加密,然后代理服务器再把我们获取的内容发到本机上。

但是在这段过程之中,本机跟代理服务器之间的通信是不加密的,使用了代理服务器之后,都会把需要访问的网站的域名或者其他信息发到代理服务器,但是这样,网站的安全性还有保证吗?

其实就算不用代理服务器,浏览器也会先去发送查询域名所对应的IP的DNS请求,还会在得到IP后发送TCP连接请求到需要访问的网站,通过网络数据包监视一样可以知道我们在访问什么网站。

有些代理服务器使用了SSL劫持的手段。这些代理服务器产品无一例外需要安装对应的客户端,一旦安装了这些客户端,就能控制客户端电脑,进而安装代理服务器自己的证书,进行SSL劫持。有些产品不需安装客户端,但客户端浏览器访问所有的HTTPS网站时都通不过浏览器的SSL证书验证,浏览器都会给出证书无效的警告。

如果我们原先不能访问某个网站,使用了代理服务器可以放问这个网站了,这个网站是http协议的,这样访问这个网站所有的行为在受监控的网络环境内都会被记录,因为代理服务器不改变HTTP协议的安全性,所以所有明文传输的内容都能被监控系统记录下来。

所以,我们使用代理服务器提交重要信息(比如用户名和口令)时,都要选择HTTPS协议,在输入用户名和口令时需要注意浏览器的地址栏是不是以https开头的。

如何优雅地解析命令行选项

如何优雅地解析命令行选项

 

如何优雅地解析命令行选项
随着我们编程经验的增长,对命令行的熟悉程度日渐加深,想来很多人会渐渐地体会到使用命令行带来的高效率。

自然而然地,我们自己写的很多程序(或者干脆就是脚本),也希望能够像原生命令和其他程序一样,通过运行时输入的参数就可以设定、改变程序的行为;而不必一层层找到相应的配置文件,然后还要定位到相应内容、修改、保存、退出……

想想就很麻烦好吗

01
1. 手动解析
所以让我们开始解析命令行参数吧~

在以前关于模块的文章中我们提到过sys.args这个变量,其中保存的就是调用当前脚本时传入的命令行参数。

我们先观察一下这个变量:

# test_sys.py
import sys

print(sys.argv)
通过命令行调用:

$ python test_sys.py -d today -t now –author justdopython –country China –auto
得到如下输出结果:

[‘test_sys.py’, ‘-d’, ‘today’, ‘-t’, ‘now’, ‘–author’, ‘justdopython’, ‘–country’, ‘China’, ‘–auto’]
可见,sys.argv其实就是将命令行参数按空格切分,得到的一个字符串列表。此外,命令行参数的*个就是当前运行的脚本名称。

我们如果想要提取出各个参数及其对应的值,首先得区分出命令行的长参数和短参数,它们分别由“–”和“-”开头作为标识。所以我们也以此作为判断长短参数的条件:

import sys

for command_arg in sys.argv[1:]:
if command_arg.startswith(‘–‘):
print(“%s 为长参数” % command_arg)
elif command_arg.startswith(‘-‘):
print(“%s 为短参数” % command_arg)
测试结果:

$ python manually_parse_argv.py -d today -t now –author justdopython –country China –auto

-d 为短参数
-t 为短参数
–author 为长参数
–country 为长参数
–auto 为长参数
紧接着,我们需要在解析出长短参数这一步的基础上,再解析出对应的参数值:

# manually_parse_argv.py
import sys

# 由于sys.argv的*个变量是当前脚本名称,因此略过
for index, command_arg in enumerate(sys.argv[1:]):
if command_arg.startswith(‘–‘):
try:
value = sys.argv[1:][index+1]
if not value.startswith(‘-‘):
print(“%s 为长参数,参数值为 %s” % (command_arg, value))
continue
except IndexError:
pass

print(“%s 为长参数,无参数值” % command_arg)

elif command_arg.startswith(‘-‘):
try:
value = sys.argv[1:][index+1]
if not value.startswith(‘-‘):
print(“%s 为短参数,参数值为 %s” % (command_arg, value))
continue
except IndexError:
pass

print(“%s 为短参数,无参数值” % command_arg)
再测试一下:

$ python manually_parse_argv.py -d today -t now –author justdopython –country China –auto

-d 为短参数,参数值为 today
-t 为短参数,参数值为 now
–author 为长参数,参数值为 justdopython
–country 为长参数,参数值为 China
–auto 为长参数,无参数值
看起来还不错。

但是再看看我们的代码……真正的逻辑还没开始,反倒是为了解析命令行参数已经写了几十行代码。这一点都不pythonic——这还不包括一些其他关于异常情况的处理。

更何况是要在每个类似的程序中加入这么一段程序了。

2. getopt模块帮您忙
Python的好处就在于,生态过于丰富,几乎你要用到的每个功能,都已经有人为你写好了现成的模块以供调用。

衣来伸手饭来张口的日子除了能在梦中想想,在用Python写程序的时候也不是不可以奢望。

比如命令行参数解析,就有一个名为getopt的模块,既能够准确区分长短命令行参数,也能够恰当地提取命令行参数的值。

咱们先来看看:

# test_getopt.py
import sys
import getopt

opts, args = getopt.getopt(sys.argv[1:], ‘d:t:’, [“author=”, “country=”, “auto”])

print(opts)
print(args)
打印结果:

$ python test_getopt.py -d today -t now –author justdopython –country China –auto
[(‘-d’, ‘today’), (‘-t’, ‘now’), (‘–author’, ‘justdopython’), (‘–country’, ‘China’), (‘–auto’, ”)]
[]
下面我们来分别解释一下相关参数的含义。

getopt模块中的getopt函数用于解析命令行参数。

该函数接受三个参数:args,shortopts和longopts,分别代表“命令行参数”,“要接收的短选项”和“要接收的长选项”。

其中args和longopts均为字符串组成的列表,而shortopts则为一个字符串。

同样地,由于sys.argv的*个值为当前脚本名称,所以多数情况下我们会选择向args参数传入sys.argv[1:]的值。

而shortopts这个参数接受的字符串则表示需要解析哪些短选项,字符串中每个字母均表示一个短选项:

import sys
import getopt

opts, args = getopt.getopt(sys.argv[1:], ‘dt’)

print(opts)
print(args)
输出结果:

$ python test_getopt.py -d -t
[(‘-d’, ”), (‘-t’, ”)]
[]
当然,如果输入的参数少于预期,也不会导致解析失败:

$ python test_getopt.py -t
[(‘-t’, ”)]
[]
但要是给出了预期之外的参数,就会导致模块抛错:

$ python test_getopt.py -d -t -k
Traceback (most recent call last):
File “test_getopt.py”, line 11, in <module>
opts, args = getopt.getopt(sys.argv[1:], ‘dt’)

raise GetoptError(_(‘option -%s not recognized’) % opt, opt)
getopt.GetoptError: option -k not recognized
这样的处理逻辑也符合我们使用命令的体验,可以简单地理解为“宁缺毋滥”。

如果短参数相应的字母后带了一个冒号:,则意味着这个参数需要指定一个参数值。getopt会将该参数对应的下一个命令行参数作为参数值(而不论下一个参数是什么形式):

import sys
import getopt

opts, args = getopt.getopt(sys.argv[1:], ‘d:t’)

print(opts)
print(args)

# $ python test_getopt.py -d -t
# [(‘-d’, ‘-t’)]
# []
此外,一旦getopt在预期接收到长短选项的位置没有找到以“–”或“-”开头的字符串,就会终止解析过程,剩下的未解析字符串均放在返回元组的第二项中返回。

$ python test_getopt.py -d d_value o –pattern -t
[(‘-d’, ‘d_value’)]
[‘o’, ‘–pattern’, ‘-t’]
类似地,longopts参数表示需要解析的长参数。

列表中的每一个字符串代表一个长参数:

import sys
import getopt

opts, args = getopt.getopt(sys.argv[1:], ”, [“author”, “country”])

print(opts)
print(args)

# $ python test_getopt.py –author –country
# [(‘–author’, ”), (‘–country’, ”)]
# []
要解析带有参数值的长参数,还应在每个长参数后附带一个等于号(=),以标识该参数需要带值:

import sys
import getopt

opts, args = getopt.getopt(sys.argv[1:], ”, [“author=”, “country”])

print(opts)
print(args)

# $ python test_getopt.py –author justdopython –country
# [(‘–author’, ‘justdopython’), (‘–country’, ”)]
# []
所以*终就得到了我们一开始的解析结果:

import sys
import getopt

opts, args = getopt.getopt(sys.argv[1:], ‘d:t:’, [“author=”, “country=”, “auto”])

print(opts)
print(args)

# $ python test_getopt.py -d today -t now –author justdopython –country China –auto
# [(‘-d’, ‘today’), (‘-t’, ‘now’), (‘–author’, ‘justdopython’), (‘–country’, ‘China’), (‘–auto’, ”)]
# []
解析完成后,我们再从opts中提取相应的值即可。

懒人福音
getopt除了替我们节省了编写命令行参数解析代码的时间和精力,另一方面还可以让你在输入命令行参数时少打几个字母——当然,严谨来讲,我们并不建议此类行为。慎用,慎用!

getopt对长参数的解析支持前缀匹配,只要输入的参数能够与某个指定参数唯一匹配,同样能够完成预期解析。

$ python test_getopt.py -d today -t now –auth justdopython –coun China –auto
[(‘-d’, ‘today’), (‘-t’, ‘now’), (‘–author’, ‘justdopython’), (‘–country’, ‘China’), (‘–auto’, ”)]
[]
可以看到,author和country两个参数我们都只输入了一部分,但是getopt依然进行了正确的解析。

总结
本文讲解了使用Python解析命令行参数的两种方式,一种是略显笨重的手动解析,即自己编写程序自定义解析;另一种则是调用现成、且更加健壮的getopt模块来完成解析。

从此以后,我们终于可以摆脱繁琐的配置文件,用一种优雅简洁的方式来修改程序的行为了。

设置代理后ip地址不变,是不是透明代理ip呢?

%title插图%num

简单点说,代理ip根据安全和质量程度,被分为透明代理、普通代理跟高匿代理,使用透明代理被访服务器可以获取本机的真实ip。

使用普通代理虽然被访服务器无法获取本机真实ip,但是依旧知道本机使用了代理ip,只有高匿代理,既可以隐藏本机真实ip地址,而且被访服务器也不会知道本机使用了代理ip。虽然现在网上有很多服务商说自己是代理ip,但是其实不然。

有的时候也会出现一种情况,就是使用代理ip设置好之后,通过浏览器一查询,发现还是能够看出来本机的ip地址,所以有时候就会让大家觉得这个是透明代理。但是实际上不全是这样,有的时候是因为没有设置好,所以代理ip并没有发挥作用。

一般来说,设置好代理ip之后,基本上就是可用跟不可用两种情况,想要更确切的知道代理ip的匿名程度,可以参考一下别的参数,因为匿名程度不同,所以服务器的配置以及HTTP_X_FORWARDED_FOR参数也有所不同

透明代理:HTTP_X_FORWARDED_FOR = Your IP
普匿代理:HTTP_X_FORWARDED_FOR = proxy
IP 高匿代理:HTTP_X_FORWARDED_FOR = Random IP address
想要分辨代理ip的匿名程度,可以查看一下参数的详情

 

当我print时,Python做了什么

当我print时,Python做了什么

 

写了这么久的程序,不知道大家有没有思考过,Python到底在干嘛呢?

或者换句话说,当我们执行Python代码的时候,是怎么实现的呢?

众所周知,Python是一门解释型的语言

——所谓“解释型”,当然是区别于以C语言为代表的编译型语言。编译型语言需要将整个程序文件全部转换为可以直接由机器执行的二进制文件;而解释型语言则是由相应的解释器一行一行“解释”并执行代码描述的行为。

正是因此,对于新接触的人来说,Python这样的解释性语言很多时候需要执行到相应的语句,才会发现一些显然的错误。

话说回来,Python的解释器是怎么样来“解释”Python代码的呢?

实际上,类似于Java的执行机制,Python也拥有自己的虚拟机。而这个虚拟机实际上执行的也是一种“字节码”。

在Python程序的执行中依然存在一个“编译”的过程:将Python代码编译为字节码。

并且,Python也提供了一个名为dis模块,用于查看、分析Python的字节码。

1. dis模块
举例来说,dis模块中有一个同名函数dis,可以用于将当前命名空间中的对象反汇编为字节码。

import dis

def add(add_1, add_2):
sum_value = add_1 + add_2

dis.dis(add)
执行结果为:

4 0 LOAD_FAST 0 (add_1)
2 LOAD_FAST 1 (add_2)
4 BINARY_ADD
6 STORE_FAST 2 (sum_value)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
其中,开头的数字“4”表示字节码的内容对应于脚本中第4行的内容。

随后的一列数字则表示对应指令所在的地址。纵向观察可以发现一个规律:下一条指令的地址总比上一条指令的地址大2。这是巧合吗?

显然不是的。官方文档《dis — Python 字节码反汇编器》中记录的更改显示,从Python 3.6版本开始,”每条指令使用2个字节“。所以每条指令的地址会在上一条指令地址的基础上加2。

再往后,是一列表示指令含义的单词组合,实际上就是人类可读的对应指令名称。顾名思义,LOAD_FAST就是加载某个内容/对象到某处,”FAST“很可能意味着这是一个便捷快速的命令实现。

*右边,则是对应于当前命令的操作数,即操作对象。数字同样是一个类似于地址的表示,括号中的字符串则表示相应对象在Python代码中的具体名称。

这样我们就可以大概地阅读生成的字节码了:

首先Python将函数add的*个参数add_1加载到某处,紧跟着将第二个参数add_2加载到*个参数之后。然后调用了一个名为BINARY_ADD的指令,即对之前加载的两个参数做加法。再然后则是将加法所得的和sum_value存储在了另一个位置。*后,加载了一个常量None并返回。

其实读完上面这个执行过程,我们很容易想到一种常用的数据结构——栈。

像下面这样:

01
当然这并不是本文的重点——真要探讨Python的实现机制,还得另外写几篇长文才能说得一二。

使用dis.dis函数除了可以查看当前脚本中各个对象对应的字节码,还可以直接传入一段代码对应的字符串进行反汇编:

# test_dis.py
import dis

s = “””
def add(add_1, add_2):
sum_value = add_1 + add_2
print(“Hello World!”)
import sys
“””

dis.dis(s)
汇编结果:

2 0 LOAD_CONST 0 (<code object add at 0x0000019FF66DFDB0, file “<dis>”, line 2>)
2 LOAD_CONST 1 (‘add’)
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (add)

5 8 LOAD_NAME 1 (print)
10 LOAD_CONST 2 (‘Hello World!’)
12 CALL_FUNCTION 1
14 POP_TOP

7 16 LOAD_CONST 3 (0)
18 LOAD_CONST 4 (None)
20 IMPORT_NAME 2 (sys)
22 STORE_NAME 2 (sys)
24 LOAD_CONST 4 (None)
26 RETURN_VALUE
2. compile函数
除了在程序中直接给出要反汇编的程序形成的字符串,我们还可以通过使用内置函数compile来形成相应脚本的编译对象,再使用dis.dis查看其字节码内容。

# test_compile.py
import dis

with open(“test_dis.py”, “r”, encoding=”utf-8″) as f:
s = f.read()

compile_obj = compile(s, “test_dis.py”,”exec”)

dis.dis(compile_obj)
字节码输出结果:

1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (None)
4 IMPORT_NAME 0 (dis)
6 STORE_NAME 0 (dis)

11 8 LOAD_CONST 2 (‘\ndef add(add_1, add_2):\n sum_value = add_1 + add_2\n\nprint(“Hello World!”)\n\nimport sys\n’)
10 STORE_NAME 1 (s)

13 12 LOAD_NAME 0 (dis)
14 LOAD_METHOD 0 (dis)
16 LOAD_NAME 1 (s)
18 CALL_METHOD 1
20 POP_TOP
22 LOAD_CONST 1 (None)
24 RETURN_VALUE
总结
dis模块为我们提供了一个观察Python内部机制的手段,恰当地使用dis模块,并结合其他方法,可以快速有效弄懂一些Python令人迷惑的地方。

希望大家善于利用这样一些有用的工具。

你只知道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