51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

楼主: 八戒你干嘛
打印 上一主题 下一主题

[python] 【转】Python教程

[复制链接]
  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    41#
     楼主| 发表于 2017-7-6 13:39:40 | 只看该作者
    contextlib[转]

    在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源的一个方法是使用try...finally:
    try:    f = open('/path/to/file', 'r')    f.read()finally:    if f:        f.close()
    写try...finally非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭,所以上面的代码可以简化为:
    with open('/path/to/file', 'r') as f:    f.read()
    并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。
    实现上下文管理是通过__enter__和__exit__这两个方法实现的。例如,下面的class实现了这两个方法:
    class Query(object):    def __init__(self, name):        self.name = name    def __enter__(self):        print('Begin')        return self    def __exit__(self, exc_type, exc_value, traceback):        if exc_type:            print('Error')        else:            print('End')    def query(self):        print('Query info about %s...' % self.name)
    这样我们就可以把自己写的资源对象用于with语句:
    with Query('Bob') as q:    q.query()@contextmanager
    编写__enter__和__exit__仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法,上面的代码可以改写如下:
    from contextlib import contextmanagerclass Query(object):    def __init__(self, name):        self.name = name    def query(self):        print('Query info about %s...' % self.name)@contextmanagerdef create_query(name):    print('Begin')    q = Query(name)    yield q    print('End')
    @contextmanager这个decorator接受一个generator,用yield语句把with ... as var把变量输出出去,然后,with语句就可以正常地工作了:
    with create_query('Bob') as q:    q.query()
    很多时候,我们希望在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现。例如:
    @contextmanagerdef tag(name):    print("<%s>" % name)    yield    print("</%s>" % name)with tag("h1"):    print("hello")    print("world")
    上述代码执行结果为:
    <h1>helloworld</h1>
    代码的执行顺序是:
    • with语句首先执行yield之前的语句,因此打印出<h1>;
    • yield调用会执行with语句内部的所有语句,因此打印出hello和world;
    • 最后执行yield之后的语句,打印出</h1>。
    因此,@contextmanager让我们通过编写generator来简化上下文管理。
    @closing
    如果一个对象没有实现上下文,我们就不能把它用于with语句。这个时候,可以用closing()来把该对象变为上下文对象。例如,用with语句使用urlopen():
    from contextlib import closingfrom urllib.request import urlopenwith closing(urlopen('https://www.python.org')) as page:    for line in page:        print(line)
    closing也是一个经过@contextmanager装饰的generator,这个generator编写起来其实非常简单:
    @contextmanagerdef closing(thing):    try:        yield thing    finally:        thing.close()
    它的作用就是把任意对象变为上下文对象,并支持with语句。
    @contextlib还有一些其他decorator,便于我们编写更简洁的代码。


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    42#
     楼主| 发表于 2017-7-6 13:42:13 | 只看该作者
    XML[转]
    XML虽然比JSON复杂,在Web中应用也不如以前多了,不过仍有很多地方在用,所以,有必要了解如何操作XML。
    DOM vs SAX
    操作XML有两种方法:DOM和SAX。DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。SAX是流模式,边读边解析,占用内存小,解析快,缺点是我们需要自己处理事件。
    正常情况下,优先考虑SAX,因为DOM实在太占内存。
    在Python中使用SAX解析XML非常简洁,通常我们关心的事件是start_element,end_element和char_data,准备好这3个函数,然后就可以解析xml了。
    举个例子,当SAX解析器读到一个节点时:
    <a href="/">python</a>
    会产生3个事件:
    • start_element事件,在读取<a href="/">时;
    • char_data事件,在读取python时;
    • end_element事件,在读取</a>时。

    用代码实验一下:
    from xml.parsers.expat import ParserCreateclass DefaultSaxHandler(object):    def start_element(self, name, attrs):        print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))    def end_element(self, name):        print('sax:end_element: %s' % name)    def char_data(self, text):        print('sax:char_data: %s' % text)xml = r'''<?xml version="1.0"?><ol>    <li><a href="/python">Python</a></li>    <li><a href="/ruby">Ruby</a></li></ol>'''handler = DefaultSaxHandler()parser = ParserCreate()parser.StartElementHandler = handler.start_elementparser.EndElementHandler = handler.end_elementparser.CharacterDataHandler = handler.char_dataparser.Parse(xml)
    需要注意的是读取一大段字符串时,CharacterDataHandler可能被多次调用,所以需要自己保存起来,在EndElementHandler里面再合并。
    除了解析XML外,如何生成XML呢?99%的情况下需要生成的XML结构都是非常简单的,因此,最简单也是最有效的生成XML的方法是拼接字符串:
    L = []L.append(r'<?xml version="1.0"?>')L.append(r'<root>')L.append(encode('some & data'))L.append(r'</root>')return ''.join(L)
    如果要生成复杂的XML呢?建议你不要用XML,改成JSON。
    小结
    解析XML时,注意找出自己感兴趣的节点,响应事件时,把节点数据保存起来。解析完毕后,就可以处理数据。
    练习
    请利用SAX编写程序解析Yahoo的XML格式的天气预报,获取当天和第二天的天气。
    参数w是城市代码,要查询某个城市代码,可以在网站上搜索城市,浏览器地址栏的URL就包含城市代码。
    # -*- coding:utf-8 -*-from xml.parsers.expat import ParserCreate# 测试:data = r'''<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">    <channel>        <title>Yahoo! Weather - Beijing, CN</title>        <lastBuildDate>Wed, 27 May 2015 11:00 am CST</lastBuildDate>        <yweather:location city="Beijing" region="" country="China"/>        <yweather:units temperature="C" distance="km" pressure="mb" speed="km/h"/>        <yweather:wind chill="28" direction="180" speed="14.48" />        <yweather:atmosphere humidity="53" visibility="2.61" pressure="1006.1" rising="0" />        <yweather:astronomy sunrise="4:51 am" sunset="7:32 pm"/>        <item>            <geo:lat>39.91</geo:lat>            <geo:long>116.39</geo:long>            <pubDate>Wed, 27 May 2015 11:00 am CST</pubDate>            <yweather:condition text="Haze" code="21" temp="28" date="Wed, 27 May 2015 11:00 am CST" />            <yweather:forecast day="Wed" date="27 May 2015" low="20" high="33" text="Partly Cloudy" code="30" />            <yweather:forecast day="Thu" date="28 May 2015" low="21" high="34" text="Sunny" code="32" />            <yweather:forecast day="Fri" date="29 May 2015" low="18" high="25" text="AM Showers" code="39" />            <yweather:forecast day="Sat" date="30 May 2015" low="18" high="32" text="Sunny" code="32" />            <yweather:forecast day="Sun" date="31 May 2015" low="20" high="37" text="Sunny" code="32" />        </item>    </channel></rss>'''weather = parse_weather(data)assert weather['city'] == 'Beijing', weather['city']assert weather['country'] == 'China', weather['country']assert weather['today']['text'] == 'Partly Cloudy', weather['today']['text']assert weather['today']['low'] == 20, weather['today']['low']assert weather['today']['high'] == 33, weather['today']['high']assert weather['tomorrow']['text'] == 'Sunny', weather['tomorrow']['text']assert weather['tomorrow']['low'] == 21, weather['tomorrow']['low']assert weather['tomorrow']['high'] == 34, weather['tomorrow']['high']print('Weather:', str(weather)) Run


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    43#
     楼主| 发表于 2017-7-6 13:43:45 | 只看该作者
    HTMLParser[转]

    如果我们要编写一个搜索引擎,第一步是用爬虫把目标网站的页面抓下来,第二步就是解析该HTML页面,看看里面的内容到底是新闻、图片还是视频。
    假设第一步已经完成了,第二步应该如何解析HTML呢?
    HTML本质上是XML的子集,但是HTML的语法没有XML那么严格,所以不能用标准的DOM或SAX来解析HTML。
    好在Python提供了HTMLParser来非常方便地解析HTML,只需简单几行代码:
    from html.parser import HTMLParserfrom html.entities import name2codepointclass MyHTMLParser(HTMLParser):    def handle_starttag(self, tag, attrs):        print('<%s>' % tag)    def handle_endtag(self, tag):        print('</%s>' % tag)    def handle_startendtag(self, tag, attrs):        print('<%s/>' % tag)    def handle_data(self, data):        print(data)    def handle_comment(self, data):        print('<!--', data, '-->')    def handle_entityref(self, name):        print('&%s;' % name)    def handle_charref(self, name):        print('&#%s;' % name)parser = MyHTMLParser()parser.feed('''<html><head></head><body><!-- test html parser -->    <p>Some <a href=\"#\">html</a> HTML&nbsp;tutorial...<br>END</p></body></html>''')
    feed()方法可以多次调用,也就是不一定一次把整个HTML字符串都塞进去,可以一部分一部分塞进去。
    特殊字符有两种,一种是英文表示的&nbsp;,一种是数字表示的Ӓ,这两种字符都可以通过Parser解析出来。
    小结
    利用HTMLParser,可以把网页中的文本、图像等解析出来。
    练习
    找一个网页,用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间、名称和地点。


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    44#
     楼主| 发表于 2017-7-6 13:46:21 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 14:10 编辑

    urllib[转]

    urllib提供了一系列用于操作URL的功能。
    Get
    urllib的request模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应:
    例如,对豆瓣的一个URL进行抓取,并返回响应:
    from urllib import requestwith request.urlopen('https://api.douban.com/v2/book/2129650') as f:    data = f.read()    print('Status:', f.status, f.reason)    for k, v in f.getheaders():        print('%s: %s' % (k, v))    print('Data:', data.decode('utf-8'))
    可以看到HTTP响应的头和JSON数据:
    Status: 200 OKServer: nginxDate: Tue, 26 May 2015 10:02:27 GMTContent-Type: application/json; charset=utf-8Content-Length: 2049Connection: closeExpires: Sun, 1 Jan 2006 01:00:00 GMTPragma: no-cacheCache-Control: must-revalidate, no-cache, privateX-DAE-Node: pidl1Data: {"rating":{"max":10,"numRaters":16,"average":"7.4","min":0},"subtitle":"","author":["廖雪峰编著"],"pubdate":"2007-6","tags":[{"count":20,"name":"spring","title":"spring"}...}
    如果我们要想模拟浏览器发送GET请求,就需要使用Request对象,通过往Request对象添加HTTP头,我们就可以把请求伪装成浏览器。例如,模拟iPhone 6去请求豆瓣首页:
    from urllib import requestreq = request.Request('http://www.douban.com/')req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')with request.urlopen(req) as f:    print('Status:', f.status, f.reason)    for k, v in f.getheaders():        print('%s: %s' % (k, v))    print('Data:', f.read().decode('utf-8'))
    这样豆瓣会返回适合iPhone的移动版网页:
    ...    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">    <meta name="format-detection" content="telephone=no">    <link rel="apple-touch-icon" sizes="57x57" href="http://img4.douban.com/pics/cardkit/launcher/57.png" />...Post
    如果要以POST发送一个请求,只需要把参数data以bytes形式传入。
    我们模拟一个微博登录,先读取登录的邮箱和口令,然后按照weibo.cn的登录页的格式以username=xxx&password=xxx的编码传入:
    from urllib import request, parseprint('Login to weibo.cn...')email = input('Email: ')passwd = input('Password: ')login_data = parse.urlencode([    ('username', email),    ('password', passwd),    ('entry', 'mweibo'),    ('client_id', ''),    ('savestate', '1'),    ('ec', ''),    ('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')])req = request.Request('https://passport.weibo.cn/sso/login')req.add_header('Origin', 'https://passport.weibo.cn')req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')with request.urlopen(req, data=login_data.encode('utf-8')) as f:    print('Status:', f.status, f.reason)    for k, v in f.getheaders():        print('%s: %s' % (k, v))    print('Data:', f.read().decode('utf-8'))
    如果登录成功,我们获得的响应如下:
    Status: 200 OKServer: nginx/1.2.0...Set-Cookie: SSOLoginState=1432620126; path=/; domain=weibo.cn...Data: {"retcode":20000000,"msg":"","data":{...,"uid":"1658384301"}}
    如果登录失败,我们获得的响应如下:
    ...Data: {"retcode":50011015,"msg":"\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef","data":{"username":"example@python.org","errline":536}}Handler
    如果还需要更复杂的控制,比如通过一个Proxy去访问网站,我们需要利用ProxyHandler来处理,示例代码如下:
    proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.example.com:3128/'})proxy_auth_handler = urllib.request.ProxyBasicAuthHandler()proxy_auth_handler.add_password('realm', 'host', 'username', 'password')opener = urllib.request.build_opener(proxy_handler, proxy_auth_handler)with opener.open('http://www.example.com/login.html') as f:    pass小结
    urllib提供的功能就是利用程序去执行各种HTTP请求。如果要模拟浏览器完成特定功能,需要把请求伪装成浏览器。伪装的方法是先监控浏览器发出的请求,再根据浏览器的请求头来伪装,User-Agent头就是用来标识浏览器的。
    练习
    利用urllib读取XML,将XML一节的数据由硬编码改为由urllib获取:
    from urllib import request, parsedef fetch_xml(url):# 测试print(fetch_xml('http://weather.yahooapis.com/forecastrss?u=c&w=2151330')) Run


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    45#
     楼主| 发表于 2017-7-6 14:03:54 | 只看该作者
    常用第三方模块[转]

    除了内建的模块外,Python还有大量的第三方模块。
    基本上,所有的第三方模块都会在PyPI - the Python Package Index上注册,只要找到对应的模块名字,即可用pip安装。
    本章介绍常用的第三方模块。
    PIL
    PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。
    由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。
    安装Pillow
    在命令行下直接通过pip安装:
    $ pip install pillow
    如果遇到Permission denied安装失败,请加上sudo重试。
    操作图像
    来看看最常见的图像缩放操作,只需三四行代码:
    from PIL import Image# 打开一个jpg图像文件,注意是当前路径:im = Image.open('test.jpg')# 获得图像尺寸:w, h = im.sizeprint('Original image size: %sx%s' % (w, h))# 缩放到50%:im.thumbnail((w//2, h//2))print('Resize image to: %sx%s' % (w//2, h//2))# 把缩放后的图像用jpeg格式保存:im.save('thumbnail.jpg', 'jpeg')
    其他功能如切片、旋转、滤镜、输出文字、调色板等一应俱全。
    比如,模糊效果也只需几行代码:
    from PIL import Image, ImageFilter# 打开一个jpg图像文件,注意是当前路径:im = Image.open('test.jpg')# 应用模糊滤镜:im2 = im.filter(ImageFilter.BLUR)im2.save('blur.jpg', 'jpeg')
    效果如下:
    PIL的ImageDraw提供了一系列绘图方法,让我们可以直接绘图。比如要生成字母验证码图片:
    from PIL import Image, ImageDraw, ImageFont, ImageFilterimport random# 随机字母:def rndChar():    return chr(random.randint(65, 90))# 随机颜色1:def rndColor():    return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))# 随机颜色2:def rndColor2():    return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))# 240 x 60:width = 60 * 4height = 60image = Image.new('RGB', (width, height), (255, 255, 255))# 创建Font对象:font = ImageFont.truetype('Arial.ttf', 36)# 创建Draw对象:draw = ImageDraw.Draw(image)# 填充每个像素:for x in range(width):    for y in range(height):        draw.point((x, y), fill=rndColor())# 输出文字:for t in range(4):    draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2())# 模糊:image = image.filter(ImageFilter.BLUR)image.save('code.jpg', 'jpeg')
    我们用随机颜色填充背景,再画上文字,最后对图像进行模糊,得到验证码图片如下:
    如果运行的时候报错:
    IOError: cannot open resource
    这是因为PIL无法定位到字体文件的位置,可以根据操作系统提供绝对路径,比如:
    '/Library/Fonts/Arial.ttf'
    要详细了解PIL的强大功能,请请参考Pillow官方文档。
    小结
    PIL提供了操作图像的强大功能,可以通过简单的代码完成复杂的图像处理。



    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    46#
     楼主| 发表于 2017-7-6 14:24:24 | 只看该作者
    virtualenv【转】
    在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.4。所有第三方的包都会被pip安装到Python3的site-packages目录下。
    如果我们要同时开发多个应用程序,那这些应用程序都会共用一个Python,就是安装在系统的Python 3。如果应用A需要jinja 2.7,而应用B需要jinja 2.6怎么办?
    这种情况下,每个应用可能需要各自拥有一套“独立”的Python运行环境。virtualenv就是用来为一个应用创建一套“隔离”的Python运行环境。
    首先,我们用pip安装virtualenv:
    $ pip3 install virtualenv
    然后,假定我们要开发一个新的项目,需要一套独立的Python运行环境,可以这么做:
    第一步,创建目录:
    Mac:~ michael$ mkdir myprojectMac:~ michael$ cd myproject/Mac:myproject michael$
    第二步,创建一个独立的Python运行环境,命名为venv:
    Mac:myproject michael$ virtualenv --no-site-packages venvUsing base prefix '/usr/local/.../Python.framework/Versions/3.4'New python executable in venv/bin/python3.4Also creating executable in venv/bin/pythonInstalling setuptools, pip, wheel...done.
    命令virtualenv就可以创建一个独立的Python运行环境,我们还加上了参数--no-site-packages,这样,已经安装到系统Python环境中的所有第三方包都不会复制过来,这样,我们就得到了一个不带任何第三方包的“干净”的Python运行环境。
    新建的Python环境被放到当前目录下的venv目录。有了venv这个Python环境,可以用source进入该环境:
    Mac:myproject michael$ source venv/bin/activate(venv)Mac:myproject michael$
    注意到命令提示符变了,有个(venv)前缀,表示当前环境是一个名为venv的Python环境。
    下面正常安装各种第三方包,并运行python命令:
    (venv)Mac:myproject michael$ pip install jinja2...Successfully installed jinja2-2.7.3 markupsafe-0.23(venv)Mac:myproject michael$ python myapp.py...
    在venv环境下,用pip安装的包都被安装到venv这个环境下,系统Python环境不受任何影响。也就是说,venv环境是专门针对myproject这个应用创建的。
    退出当前的venv环境,使用deactivate命令:
    (venv)Mac:myproject michael$ deactivate Mac:myproject michael$
    此时就回到了正常的环境,现在pip或python均是在系统Python环境下执行。
    完全可以针对每个应用创建独立的Python运行环境,这样就可以对每个应用的Python环境进行隔离。
    virtualenv是如何创建“独立”的Python运行环境的呢?原理很简单,就是把系统Python复制一份到virtualenv的环境,用命令source venv/bin/activate进入一个virtualenv环境时,virtualenv会修改相关环境变量,让命令python和pip均指向当前的virtualenv环境。
    小结
    virtualenv为应用提供了隔离的Python运行环境,解决了不同应用间多版本的冲突问题。


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    47#
     楼主| 发表于 2017-7-6 14:31:55 | 只看该作者
    图形界面【转】
    Python支持多种图形界面的第三方库,包括:
    • Tk
    • wxWidgets
    • Qt
    • GTK

    等等。
    但是Python自带的库是支持Tk的Tkinter,使用Tkinter,无需安装任何包,就可以直接使用。本章简单介绍如何使用Tkinter进行GUI编程。
    Tkinter
    我们来梳理一下概念:
    我们编写的Python代码会调用内置的Tkinter,Tkinter封装了访问Tk的接口;
    Tk是一个图形库,支持多个操作系统,使用Tcl语言开发;
    Tk会调用操作系统提供的本地GUI接口,完成最终的GUI。
    所以,我们的代码只需要调用Tkinter提供的接口就可以了。
    第一个GUI程序
    使用Tkinter十分简单,我们来编写一个GUI版本的“Hello, world!”。
    第一步是导入Tkinter包的所有内容:
    from tkinter import *
    第二步是从Frame派生一个Application类,这是所有Widget的父容器:
    class Application(Frame):    def __init__(self, master=None):        Frame.__init__(self, master)        self.pack()        self.createWidgets()    def createWidgets(self):        self.helloLabel = Label(self, text='Hello, world!')        self.helloLabel.pack()        self.quitButton = Button(self, text='Quit', command=self.quit)        self.quitButton.pack()
    在GUI中,每个Button、Label、输入框等,都是一个Widget。Frame则是可以容纳其他Widget的Widget,所有的Widget组合起来就是一棵树。
    pack()方法把Widget加入到父容器中,并实现布局。pack()是最简单的布局,grid()可以实现更复杂的布局。
    在createWidgets()方法中,我们创建一个Label和一个Button,当Button被点击时,触发self.quit()使程序退出。
    第三步,实例化Application,并启动消息循环:
    app = Application()# 设置窗口标题:app.master.title('Hello World')# 主消息循环:app.mainloop()
    GUI程序的主线程负责监听来自操作系统的消息,并依次处理每一条消息。因此,如果消息处理非常耗时,就需要在新线程中处理。
    运行这个GUI程序,可以看到下面的窗口:
    点击“Quit”按钮或者窗口的“x”结束程序。
    输入文本
    我们再对这个GUI程序改进一下,加入一个文本框,让用户可以输入文本,然后点按钮后,弹出消息对话框。
    from tkinter import *import tkinter.messagebox as messageboxclass Application(Frame):    def __init__(self, master=None):        Frame.__init__(self, master)        self.pack()        self.createWidgets()    def createWidgets(self):        self.nameInput = Entry(self)        self.nameInput.pack()        self.alertButton = Button(self, text='Hello', command=self.hello)        self.alertButton.pack()    def hello(self):        name = self.nameInput.get() or 'world'        messagebox.showinfo('Message', 'Hello, %s' % name)app = Application()# 设置窗口标题:app.master.title('Hello World')# 主消息循环:app.mainloop()
    当用户点击按钮时,触发hello(),通过self.nameInput.get()获得用户输入的文本后,使用tkMessageBox.showinfo()可以弹出消息对话框。
    程序运行结果如下:
    小结
    Python内置的Tkinter可以满足基本的GUI程序的要求,如果是非常复杂的GUI程序,建议用操作系统原生支持的语言和库来编写。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    48#
     楼主| 发表于 2017-7-6 14:34:12 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:39 编辑

    网络编程【转】
    自从互联网诞生以来,现在基本上所有的程序都是网络程序,很少有单机版的程序了。
    计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信。网络编程就是如何在程序中实现两台计算机的通信。
    举个例子,当你使用浏览器访问新浪网时,你的计算机就和新浪的某台服务器通过互联网连接起来了,然后,新浪的服务器把网页内容作为数据通过互联网传输到你的电脑上。
    由于你的电脑上可能不止浏览器,还有QQ、Skype、Dropbox、邮件客户端等,不同的程序连接的别的计算机也会不同,所以,更确切地说,网络通信是两台计算机上的两个进程之间的通信。比如,浏览器进程和新浪服务器上的某个Web服务进程在通信,而QQ进程是和腾讯的某个服务器上的某个进程在通信。
    网络编程对所有开发语言都是一样的,Python也不例外。用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。
    本章我们将详细介绍Python网络编程的概念和最主要的两种网络类型的编程。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    49#
     楼主| 发表于 2017-7-6 14:38:19 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:40 编辑

    TCP/IP简介[转]
    虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多。
    计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM、Apple和Microsoft都有各自的网络协议,互不兼容,这就好比一群人有的说英语,有的说中文,有的说德语,说同一种语言的人可以交流,不同的语言之间就不行了。
    为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,互联网协议簇(Internet Protocol Suite)就是通用协议标准。Internet是由inter和net两个单词组合起来的,原意就是连接“网络”的网络,有了Internet,任何私有网络,只要支持这个协议,就可以联入互联网。
    因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。
    通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址。互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。
    IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
    IP地址实际上是一个32位整数(称为IPv4),以字符串表示的IP地址如192.168.0.1实际上是把32位整数按8位分组后的数字表示,目的是便于阅读。
    IPv6地址实际上是一个128位整数,它是目前使用的IPv4的升级版,以字符串表示类似于2001:0db8:85a3:0042:1000:8a2e:0370:7334。
    TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
    许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
    一个IP包除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
    端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个IP包来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
    一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
    了解了TCP/IP协议的基本概念,IP地址和端口的概念,我们就可以开始进行网络编程了。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    50#
     楼主| 发表于 2017-7-6 14:44:56 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:42 编辑

    TCP编程[转]
    Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。
    客户端
    大多数连接都是可靠的TCP连接。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。
    举个例子,当我们在浏览器中访问新浪时,我们自己的计算机就是客户端,浏览器会主动向新浪的服务器发起连接。如果一切顺利,新浪的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了。
    所以,我们要创建一个基于TCP连接的Socket,可以这样做:
    # 导入socket库:import socket# 创建一个socket:s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 建立连接:s.connect(('www.sina.com.cn', 80))
    创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个Socket对象就创建成功,但是还没有建立连接。
    客户端要主动发起TCP连接,必须知道服务器的IP地址和端口号。新浪网站的IP地址可以用域名www.sina.com.cn自动转换到IP地址,但是怎么知道新浪服务器的端口号呢?
    答案是作为服务器,提供什么样的服务,端口号就必须固定下来。由于我们想要访问网页,因此新浪提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。
    因此,我们连接新浪服务器的代码如下:
    s.connect(('www.sina.com.cn', 80))
    注意参数是一个tuple,包含地址和端口号。
    建立TCP连接后,我们就可以向新浪服务器发送请求,要求返回首页的内容:
    # 发送数据:s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
    TCP连接创建的是双向通道,双方都可以同时给对方发数据。但是谁先发谁后发,怎么协调,要根据具体的协议来决定。例如,HTTP协议规定客户端必须先发请求给服务器,服务器收到后才发数据给客户端。
    发送的文本格式必须符合HTTP标准,如果格式没问题,接下来就可以接收新浪服务器返回的数据了:
    # 接收数据:buffer = []while True:    # 每次最多接收1k字节:    d = s.recv(1024)    if d:        buffer.append(d)    else:        breakdata = b''.join(buffer)
    接收数据时,调用recv(max)方法,一次最多接收指定的字节数,因此,在一个while循环中反复接收,直到recv()返回空数据,表示接收完毕,退出循环。
    当我们接收完数据后,调用close()方法关闭Socket,这样,一次完整的网络通信就结束了:
    # 关闭连接:s.close()
    接收到的数据包括HTTP头和网页本身,我们只需要把HTTP头和网页分离一下,把HTTP头打印出来,网页内容保存到文件:
    header, html = data.split(b'\r\n\r\n', 1)print(header.decode('utf-8'))# 把接收的数据写入文件:with open('sina.html', 'wb') as f:    f.write(html)
    现在,只需要在浏览器中打开这个sina.html文件,就可以看到新浪的首页了。
    服务器
    和客户端编程相比,服务器编程就要复杂一些。
    服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。
    所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。
    但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。
    我们来编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的字符串加上Hello再发回去。
    首先,创建一个基于IPv4和TCP协议的Socket:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    然后,我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0绑定到所有的网络地址,还可以用127.0.0.1绑定到本机地址。127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。
    端口号需要预先指定。因为我们写的这个服务不是标准服务,所以用9999这个端口号。请注意,小于1024的端口号必须要有管理员权限才能绑定:
    # 监听端口:s.bind(('127.0.0.1', 9999))
    紧接着,调用listen()方法开始监听端口,传入的参数指定等待连接的最大数量:
    s.listen(5)print('Waiting for connection...')
    接下来,服务器程序通过一个永久循环来接受来自客户端的连接,accept()会等待并返回一个客户端的连接:
    while True:    # 接受一个新连接:    sock, addr = s.accept()    # 创建新线程来处理TCP连接:    t = threading.Thread(target=tcplink, args=(sock, addr))    t.start()
    每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接:
    def tcplink(sock, addr):    print('Accept new connection from %s:%s...' % addr)    sock.send(b'Welcome!')    while True:        data = sock.recv(1024)        time.sleep(1)        if not data or data.decode('utf-8') == 'exit':            break        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))    sock.close()    print('Connection from %s:%s closed.' % addr)
    连接建立后,服务器首先发一条欢迎消息,然后等待客户端数据,并加上Hello再发送给客户端。如果客户端发送了exit字符串,就直接关闭连接。
    要测试这个服务器程序,我们还需要编写一个客户端程序:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 建立连接:s.connect(('127.0.0.1', 9999))# 接收欢迎消息:print(s.recv(1024).decode('utf-8'))for data in [b'Michael', b'Tracy', b'Sarah']:    # 发送数据:    s.send(data)    print(s.recv(1024).decode('utf-8'))s.send(b'exit')s.close()
    我们需要打开两个命令行窗口,一个运行服务器程序,另一个运行客户端程序,就可以看到效果了:
    需要注意的是,客户端程序运行完毕就退出了,而服务器程序会永远运行下去,必须按Ctrl+C退出程序。
    小结
    用TCP协议进行Socket编程在Python中十分简单,对于客户端,要主动连接服务器的IP和指定端口,对于服务器,要首先监听指定端口,然后,对每一个新的连接,创建一个线程或进程来处理。通常,服务器程序会无限运行下去。
    同一个端口,被一个Socket绑定了以后,就不能被别的Socket绑定了。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    51#
     楼主| 发表于 2017-7-6 14:49:21 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:43 编辑

    UDP编程[转]
    TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。
    使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。
    虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。
    我们来看看如何通过UDP协议传输数据。和TCP类似,使用UDP的通信双方也分为客户端和服务器。服务器首先需要绑定端口:
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定端口:s.bind(('127.0.0.1', 9999))
    创建Socket时,SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样,但是不需要调用listen()方法,而是直接接收来自任何客户端的数据:
    print('Bind UDP on 9999...')while True:    # 接收数据:    data, addr = s.recvfrom(1024)    print('Received from %s:%s.' % addr)    s.sendto(b'Hello, %s!' % data, addr)
    recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。
    注意这里省掉了多线程,因为这个例子很简单。
    客户端使用UDP时,首先仍然创建基于UDP的Socket,然后,不需要调用connect(),直接通过sendto()给服务器发数据:
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)for data in [b'Michael', b'Tracy', b'Sarah']:    # 发送数据:    s.sendto(data, ('127.0.0.1', 9999))    # 接收数据:    print(s.recv(1024).decode('utf-8'))s.close()
    从服务器接收数据仍然调用recv()方法。
    仍然用两个命令行分别启动服务器和客户端测试,结果如下:
    小结
    UDP的使用与TCP类似,但是不需要建立连接。此外,服务器绑定UDP端口和TCP端口互不冲突,也就是说,UDP的9999端口与TCP的9999端口可以各自绑定。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    52#
     楼主| 发表于 2017-7-6 14:53:22 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:45 编辑

    电子邮件【转】
    Email的历史比Web还要久远,直到现在,Email也是互联网上应用非常广泛的服务。
    几乎所有的编程语言都支持发送和接收电子邮件,但是,先等等,在我们开始编写代码之前,有必要搞清楚电子邮件是如何在互联网上运作的。
    我们来看看传统邮件是如何运作的。假设你现在在北京,要给一个香港的朋友发一封信,怎么做呢?
    首先你得写好信,装进信封,写上地址,贴上邮票,然后就近找个邮局,把信仍进去。
    信件会从就近的小邮局转运到大邮局,再从大邮局往别的城市发,比如先发到天津,再走海运到达香港,也可能走京九线到香港,但是你不用关心具体路线,你只需要知道一件事,就是信件走得很慢,至少要几天时间。
    信件到达香港的某个邮局,也不会直接送到朋友的家里,因为邮局的叔叔是很聪明的,他怕你的朋友不在家,一趟一趟地白跑,所以,信件会投递到你的朋友的邮箱里,邮箱可能在公寓的一层,或者家门口,直到你的朋友回家的时候检查邮箱,发现信件后,就可以取到邮件了。
    电子邮件的流程基本上也是按上面的方式运作的,只不过速度不是按天算,而是按秒算。
    现在我们回到电子邮件,假设我们自己的电子邮件地址是me@163.com,对方的电子邮件地址是friend@sina.com(注意地址都是虚构的哈),现在我们用Outlook或者Foxmail之类的软件写好邮件,填上对方的Email地址,点“发送”,电子邮件就发出去了。这些电子邮件软件被称为MUA:Mail User Agent——邮件用户代理。
    Email从MUA发出去,不是直接到达对方电脑,而是发到MTA:Mail Transfer Agent——邮件传输代理,就是那些Email服务提供商,比如网易、新浪等等。由于我们自己的电子邮件是163.com,所以,Email首先被投递到网易提供的MTA,再由网易的MTA发到对方服务商,也就是新浪的MTA。这个过程中间可能还会经过别的MTA,但是我们不关心具体路线,我们只关心速度。
    Email到达新浪的MTA后,由于对方使用的是@sina.com的邮箱,因此,新浪的MTA会把Email投递到邮件的最终目的地MDA:Mail Delivery Agent——邮件投递代理。Email到达MDA后,就静静地躺在新浪的某个服务器上,存放在某个文件或特殊的数据库里,我们将这个长期保存邮件的地方称之为电子邮箱。
    同普通邮件类似,Email不会直接到达对方的电脑,因为对方电脑不一定开机,开机也不一定联网。对方要取到邮件,必须通过MUA从MDA上把邮件取到自己的电脑上。
    所以,一封电子邮件的旅程就是:
    发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人
    有了上述基本概念,要编写程序来发送和接收邮件,本质上就是:
    • 编写MUA把邮件发到MTA;
    • 编写MUA从MDA上收邮件。

    发邮件时,MUA和MTA使用的协议就是SMTP:Simple Mail Transfer Protocol,后面的MTA到另一个MTA也是用SMTP协议。
    收邮件时,MUA和MDA使用的协议有两种:POP:Post Office Protocol,目前版本是3,俗称POP3;IMAP:Internet Message Access Protocol,目前版本是4,优点是不但能取邮件,还可以直接操作MDA上存储的邮件,比如从收件箱移到垃圾箱,等等。
    邮件客户端软件在发邮件时,会让你先配置SMTP服务器,也就是你要发到哪个MTA上。假设你正在使用163的邮箱,你就不能直接发到新浪的MTA上,因为它只服务新浪的用户,所以,你得填163提供的SMTP服务器地址:smtp.163.com,为了证明你是163的用户,SMTP服务器还要求你填写邮箱地址和邮箱口令,这样,MUA才能正常地把Email通过SMTP协议发送到MTA。
    类似的,从MDA收邮件时,MDA服务器也要求验证你的邮箱口令,确保不会有人冒充你收取你的邮件,所以,Outlook之类的邮件客户端会要求你填写POP3或IMAP服务器地址、邮箱地址和口令,这样,MUA才能顺利地通过POP或IMAP协议从MDA取到邮件。
    在使用Python收发邮件前,请先准备好至少两个电子邮件,如xxx@163.comxxx@sina.comxxx@qq.com等,注意两个邮箱不要用同一家邮件服务商。
    最后特别注意,目前大多数邮件服务商都需要手动打开SMTP发信和POP收信的功能,否则只允许在网页登录:


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    53#
     楼主| 发表于 2017-7-6 15:07:31 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:51 编辑

    SMTP发送邮件[转]
    SMTP是发送邮件的协议,Python内置对SMTP的支持,可以发送纯文本邮件、HTML邮件以及带附件的邮件。
    Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件。
    首先,我们来构造一个最简单的纯文本邮件:
    from email.mime.text import MIMETextmsg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
    注意到构造MIMEText对象时,第一个参数就是邮件正文,第二个参数是MIME的subtype,传入'plain'表示纯文本,最终的MIME就是'text/plain',最后一定要用utf-8编码保证多语言兼容性。
    然后,通过SMTP发出去:
    # 输入Email地址和口令:from_addr = input('From: ')password = input('Password: ')# 输入收件人地址:to_addr = input('To: ')# 输入SMTP服务器地址:smtp_server = input('SMTP server: ')import smtplibserver = smtplib.SMTP(smtp_server, 25) # SMTP协议默认端口是25server.set_debuglevel(1)server.login(from_addr, password)server.sendmail(from_addr, [to_addr], msg.as_string())server.quit()
    我们用set_debuglevel(1)就可以打印出和SMTP服务器交互的所有信息。SMTP协议就是简单的文本命令和响应。login()方法用来登录SMTP服务器,sendmail()方法就是发邮件,由于可以一次发给多个人,所以传入一个list,邮件正文是一个str,as_string()把MIMEText对象变成str。
    如果一切顺利,就可以在收件人信箱中收到我们刚发送的Email:
    仔细观察,发现如下问题:
    • 邮件没有主题;
    • 收件人的名字没有显示为友好的名字,比如Mr Green <green@example.com>;
    • 明明收到了邮件,却提示不在收件人中。
    这是因为邮件主题、如何显示发件人、收件人等信息并不是通过SMTP协议发给MTA,而是包含在发给MTA的文本中的,所以,我们必须把From、To和Subject添加到MIMEText中,才是一封完整的邮件:
    from email import encodersfrom email.header import Headerfrom email.mime.text import MIMETextfrom email.utils import parseaddr, formataddrimport smtplibdef _format_addr(s):    name, addr = parseaddr(s)    return formataddr((Header(name, 'utf-8').encode(), addr))from_addr = input('From: ')password = input('Password: ')to_addr = input('To: ')smtp_server = input('SMTP server: ')msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)msg['To'] = _format_addr('管理员 <%s>' % to_addr)msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()server = smtplib.SMTP(smtp_server, 25)server.set_debuglevel(1)server.login(from_addr, password)server.sendmail(from_addr, [to_addr], msg.as_string())server.quit()
    我们编写了一个函数_format_addr()来格式化一个邮件地址。注意不能简单地传入name <addr@example.com>,因为如果包含中文,需要通过Header对象进行编码。
    msg['To']接收的是字符串而不是list,如果有多个邮件地址,用,分隔即可。
    再发送一遍邮件,就可以在收件人邮箱中看到正确的标题、发件人和收件人:
    你看到的收件人的名字很可能不是我们传入的管理员,因为很多邮件服务商在显示邮件时,会把收件人名字自动替换为用户注册的名字,但是其他收件人名字的显示不受影响。
    如果我们查看Email的原始内容,可以看到如下经过编码的邮件头:
    From: =?utf-8?b?UHl0aG9u54ix5aW96ICF?= <xxxxxx@163.com>To: =?utf-8?b?566h55CG5ZGY?= <xxxxxx@qq.com>Subject: =?utf-8?b?5p2l6IeqU01UUOeahOmXruWAmeKApuKApg==?=
    这就是经过Header对象编码的文本,包含utf-8编码信息和Base64编码的文本。如果我们自己来手动构造这样的编码文本,显然比较复杂。
    发送HTML邮件
    如果我们要发送HTML邮件,而不是普通的纯文本文件怎么办?方法很简单,在构造MIMEText对象时,把HTML字符串传进去,再把第二个参数由plain变为html就可以了:
    msg = MIMEText('<html><body><h1>Hello</h1>' +    '<p>send by <a href="http://www.python.org">Python</a>...</p>' +    '</body></html>', 'html', 'utf-8')
    再发送一遍邮件,你将看到以HTML显示的邮件:
    发送附件
    如果Email中要加上附件怎么办?带附件的邮件可以看做包含若干部分的邮件:文本和各个附件本身,所以,可以构造一个MIMEMultipart对象代表邮件本身,然后往里面加上一个MIMEText作为邮件正文,再继续往里面加上表示附件的MIMEBase对象即可:
    # 邮件对象:msg = MIMEMultipart()msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)msg['To'] = _format_addr('管理员 <%s>' % to_addr)msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()# 邮件正文是MIMEText:msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))# 添加附件就是加上一个MIMEBase,从本地读取一个图片:with open('/Users/michael/Downloads/test.png', 'rb') as f:    # 设置附件的MIME和文件名,这里是png类型:    mime = MIMEBase('image', 'png', filename='test.png')    # 加上必要的头信息:    mime.add_header('Content-Disposition', 'attachment', filename='test.png')    mime.add_header('Content-ID', '<0>')    mime.add_header('X-Attachment-Id', '0')    # 把附件的内容读进来:    mime.set_payload(f.read())    # 用Base64编码:    encoders.encode_base64(mime)    # 添加到MIMEMultipart:    msg.attach(mime)
    然后,按正常发送流程把msg(注意类型已变为MIMEMultipart)发送出去,就可以收到如下带附件的邮件:
    发送图片
    如果要把一个图片嵌入到邮件正文中怎么做?直接在HTML邮件中链接图片地址行不行?答案是,大部分邮件服务商都会自动屏蔽带有外链的图片,因为不知道这些链接是否指向恶意网站。
    要把图片嵌入到邮件正文中,我们只需按照发送附件的方式,先把邮件作为附件添加进去,然后,在HTML中通过引用src="cid:0"就可以把附件作为图片嵌入了。如果有多个图片,给它们依次编号,然后引用不同的cid:x即可。
    把上面代码加入MIMEMultipart的MIMEText从plain改为html,然后在适当的位置引用图片:
    msg.attach(MIMEText('<html><body><h1>Hello</h1>' +    '<p><img src="cid:0"></p>' +    '</body></html>', 'html', 'utf-8'))
    再次发送,就可以看到图片直接嵌入到邮件正文的效果:
    同时支持HTML和Plain格式
    如果我们发送HTML邮件,收件人通过浏览器或者Outlook之类的软件是可以正常浏览邮件内容的,但是,如果收件人使用的设备太古老,查看不了HTML邮件怎么办?
    办法是在发送HTML的同时再附加一个纯文本,如果收件人无法查看HTML格式的邮件,就可以自动降级查看纯文本邮件。
    利用MIMEMultipart就可以组合一个HTML和Plain,要注意指定subtype是alternative:
    msg = MIMEMultipart('alternative')msg['From'] = ...msg['To'] = ...msg['Subject'] = ...msg.attach(MIMEText('hello', 'plain', 'utf-8'))msg.attach(MIMEText('<html><body><h1>Hello</h1></body></html>', 'html', 'utf-8'))# 正常发送msg对象...


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    54#
     楼主| 发表于 2017-7-6 15:08:32 | 只看该作者
    加密SMTP
    使用标准的25端口连接SMTP服务器时,使用的是明文传输,发送邮件的整个过程可能会被窃听。要更安全地发送邮件,可以加密SMTP会话,实际上就是先创建SSL安全连接,然后再使用SMTP协议发送邮件。
    某些邮件服务商,例如Gmail,提供的SMTP服务必须要加密传输。我们来看看如何通过Gmail提供的安全SMTP发送邮件。
    必须知道,Gmail的SMTP端口是587,因此,修改代码如下:
    smtp_server = 'smtp.gmail.com'smtp_port = 587server = smtplib.SMTP(smtp_server, smtp_port)server.starttls()# 剩下的代码和前面的一模一样:server.set_debuglevel(1)...
    只需要在创建SMTP对象后,立刻调用starttls()方法,就创建了安全连接。后面的代码和前面的发送邮件代码完全一样。
    如果因为网络问题无法连接Gmail的SMTP服务器,请相信我们的代码是没有问题的,你需要对你的网络设置做必要的调整。
    小结
    使用Python的smtplib发送邮件十分简单,只要掌握了各种邮件类型的构造方法,正确设置好邮件头,就可以顺利发出。
    构造一个邮件对象就是一个Messag对象,如果构造一个MIMEText对象,就表示一个文本邮件对象,如果构造一个MIMEImage对象,就表示一个作为附件的图片,要把多个对象组合起来,就用MIMEMultipart对象,而MIMEBase可以表示任何对象。它们的继承关系如下:
    Message+- MIMEBase   +- MIMEMultipart   +- MIMENonMultipart      +- MIMEMessage      +- MIMEText      +- MIMEImage
    这种嵌套关系就可以构造出任意复杂的邮件。你可以通过email.mime文档查看它们所在的包以及详细的用法。

    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    55#
     楼主| 发表于 2017-7-6 15:09:54 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:52 编辑

    POP3收取邮件[转]
    SMTP用于发送邮件,如果要收取邮件呢?
    收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上。收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3
    Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件。
    注意到POP3协议收取的不是一个已经可以阅读的邮件本身,而是邮件的原始文本,这和SMTP协议很像,SMTP发送的也是经过编码后的一大段文本。
    要把POP3收取的文本变成可以阅读的邮件,还需要用email模块提供的各种类来解析原始文本,变成可阅读的邮件对象。
    所以,收取邮件分两步:
    第一步:用poplib把邮件的原始文本下载到本地;
    第二部:用email解析原始文本,还原为邮件对象。
    通过POP3下载邮件
    POP3协议本身很简单,以下面的代码为例,我们来获取最新的一封邮件内容:
    import poplib# 输入邮件地址, 口令和POP3服务器地址:email = input('Email: ')password = input('Password: ')pop3_server = input('POP3 server: ')# 连接到POP3服务器:server = poplib.POP3(pop3_server)# 可以打开或关闭调试信息:server.set_debuglevel(1)# 可选:打印POP3服务器的欢迎文字:print(server.getwelcome().decode('utf-8'))# 身份认证:server.user(email)server.pass_(password)# stat()返回邮件数量和占用空间:print('Messages: %s. Size: %s' % server.stat())# list()返回所有邮件的编号:resp, mails, octets = server.list()# 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]print(mails)# 获取最新一封邮件, 注意索引号从1开始:index = len(mails)resp, lines, octets = server.retr(index)# lines存储了邮件的原始文本的每一行,# 可以获得整个邮件的原始文本:msg_content = b'\r\n'.join(lines).decode('utf-8')# 稍后解析出邮件:msg = Parser().parsestr(msg_content)# 可以根据邮件索引号直接从服务器删除邮件:# server.dele(index)# 关闭连接:server.quit()
    用POP3获取邮件其实很简单,要获取所有邮件,只需要循环使用retr()把每一封邮件内容拿到即可。真正麻烦的是把邮件的原始内容解析为可以阅读的邮件对象。
    解析邮件
    解析邮件的过程和上一节构造邮件正好相反,因此,先导入必要的模块:
    from email.parser import Parserfrom email.header import decode_headerfrom email.utils import parseaddrimport poplib
    只需要一行代码就可以把邮件内容解析为Message对象:
    msg = Parser().parsestr(msg_content)
    但是这个Message对象本身可能是一个MIMEMultipart对象,即包含嵌套的其他MIMEBase对象,嵌套可能还不止一层。
    所以我们要递归地打印出Message对象的层次结构:
    # indent用于缩进显示:def print_info(msg, indent=0):    if indent == 0:        for header in ['From', 'To', 'Subject']:            value = msg.get(header, '')            if value:                if header=='Subject':                    value = decode_str(value)                else:                    hdr, addr = parseaddr(value)                    name = decode_str(hdr)                    value = u'%s <%s>' % (name, addr)            print('%s%s: %s' % ('  ' * indent, header, value))    if (msg.is_multipart()):        parts = msg.get_payload()        for n, part in enumerate(parts):            print('%spart %s' % ('  ' * indent, n))            print('%s--------------------' % ('  ' * indent))            print_info(part, indent + 1)    else:        content_type = msg.get_content_type()        if content_type=='text/plain' or content_type=='text/html':            content = msg.get_payload(decode=True)            charset = guess_charset(msg)            if charset:                content = content.decode(charset)            print('%sText: %s' % ('  ' * indent, content + '...'))        else:            print('%sAttachment: %s' % ('  ' * indent, content_type))
    邮件的Subject或者Email中包含的名字都是经过编码后的str,要正常显示,就必须decode:
    def decode_str(s):    value, charset = decode_header(s)[0]    if charset:        value = value.decode(charset)    return value
    decode_header()返回一个list,因为像Cc、Bcc这样的字段可能包含多个邮件地址,所以解析出来的会有多个元素。上面的代码我们偷了个懒,只取了第一个元素。
    文本邮件的内容也是str,还需要检测编码,否则,非UTF-8编码的邮件都无法正常显示:
    def guess_charset(msg):    charset = msg.get_charset()    if charset is None:        content_type = msg.get('Content-Type', '').lower()        pos = content_type.find('charset=')        if pos >= 0:            charset = content_type[pos + 8:].strip()    return charset
    把上面的代码整理好,我们就可以来试试收取一封邮件。先往自己的邮箱发一封邮件,然后用浏览器登录邮箱,看看邮件收到没,如果收到了,我们就来用Python程序把它收到本地:
    运行程序,结果如下:
    +OK Welcome to coremail Mail Pop3 Server (163coms[...])Messages: 126. Size: 27228317From: Test <xxxxxx@qq.com>To: Python爱好者 <xxxxxx@163.com>Subject: 用POP3收取邮件part 0--------------------  part 0  --------------------    Text: Python可以使用POP3收取邮件……...  part 1  --------------------    Text: Python可以<a href="...">使用POP3</a>收取邮件……...part 1--------------------  Attachment: application/octet-stream
    我们从打印的结构可以看出,这封邮件是一个MIMEMultipart,它包含两部分:第一部分又是一个MIMEMultipart,第二部分是一个附件。而内嵌的MIMEMultipart是一个alternative类型,它包含一个纯文本格式的MIMEText和一个HTML格式的MIMEText。
    小结
    用Python的poplib模块收取邮件分两步:第一步是用POP3协议把邮件获取到本地,第二步是用email模块把原始邮件解析为Message对象,然后,用适当的形式把邮件内容展示给用户即可。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    56#
     楼主| 发表于 2017-7-6 15:11:28 | 只看该作者
    本帖最后由 八戒你干嘛 于 2017-7-6 16:54 编辑

    访问数据库【转】
    程序运行的时候,数据都是在内存中的。当程序终止的时候,通常都需要将数据保存到磁盘上,无论是保存到本地磁盘,还是通过网络保存到服务器上,最终都会将数据写入磁盘文件。
    而如何定义数据的存储格式就是一个大问题。如果我们自己来定义存储格式,比如保存一个班级所有学生的成绩单:
    名字
    成绩
    Michael99
    Bob85
    Bart59
    Lisa87
    你可以用一个文本文件保存,一行保存一个学生,用,隔开:
    Michael,99Bob,85Bart,59Lisa,87
    你还可以用JSON格式保存,也是文本文件:
    [    {"name":"Michael","score":99},    {"name":"Bob","score":85},    {"name":"Bart","score":59},    {"name":"Lisa","score":87}]
    你还可以定义各种保存格式,但是问题来了:
    存储和读取需要自己实现,JSON还是标准,自己定义的格式就各式各样了;
    不能做快速查询,只有把数据全部读到内存中才能自己遍历,但有时候数据的大小远远超过了内存(比如蓝光电影,40GB的数据),根本无法全部读入内存。
    为了便于程序保存和读取数据,而且,能直接通过条件快速查询到指定的数据,就出现了数据库(Database)这种专门用于集中存储和查询的软件。
    数据库软件诞生的历史非常久远,早在1950年数据库就诞生了。经历了网状数据库,层次数据库,我们现在广泛使用的关系数据库是20世纪70年代基于关系模型的基础上诞生的。
    关系模型有一套复杂的数学理论,但是从概念上是十分容易理解的。举个学校的例子:
    假设某个XX省YY市ZZ县第一实验小学有3个年级,要表示出这3个年级,可以在Excel中用一个表格画出来:
    每个年级又有若干个班级,要把所有班级表示出来,可以在Excel中再画一个表格:
    这两个表格有个映射关系,就是根据Grade_ID可以在班级表中查找到对应的所有班级:
    也就是Grade表的每一行对应Class表的多行,在关系数据库中,这种基于表(Table)的一对多的关系就是关系数据库的基础。
    根据某个年级的ID就可以查找所有班级的行,这种查询语句在关系数据库中称为SQL语句,可以写成:
    SELECT * FROM classes WHERE grade_id = '1';
    结果也是一个表:
    ---------+----------+----------grade_id | class_id | name---------+----------+----------1        | 11       | 一年级一班---------+----------+----------1        | 12       | 一年级二班---------+----------+----------1        | 13       | 一年级三班---------+----------+----------
    类似的,Class表的一行记录又可以关联到Student表的多行记录:
    由于本教程不涉及到关系数据库的详细内容,如果你想从零学习关系数据库和基本的SQL语句,如果你想从零学习关系数据库和基本的SQL语句,请自行搜索相关课程。
    NoSQL
    你也许还听说过NoSQL数据库,很多NoSQL宣传其速度和规模远远超过关系数据库,所以很多同学觉得有了NoSQL是否就不需要SQL了呢?千万不要被他们忽悠了,连SQL都不明白怎么可能搞明白NoSQL呢?
    数据库类别
    既然我们要使用关系数据库,就必须选择一个关系数据库。目前广泛使用的关系数据库也就这么几种:
    付费的商用数据库:
    • Oracle,典型的高富帅;
    • SQL Server,微软自家产品,Windows定制专款;
    • DB2,IBM的产品,听起来挺高端;
    • Sybase,曾经跟微软是好基友,后来关系破裂,现在家境惨淡。

    这些数据库都是不开源而且付费的,最大的好处是花了钱出了问题可以找厂家解决,不过在Web的世界里,常常需要部署成千上万的数据库服务器,当然不能把大把大把的银子扔给厂家,所以,无论是Google、Facebook,还是国内的BAT,无一例外都选择了免费的开源数据库:
    • MySQL,大家都在用,一般错不了;
    • PostgreSQL,学术气息有点重,其实挺不错,但知名度没有MySQL高;
    • sqlite,嵌入式数据库,适合桌面和移动应用。

    作为Python开发工程师,选择哪个免费数据库呢?当然是MySQL。因为MySQL普及率最高,出了错,可以很容易找到解决方法。而且,围绕MySQL有一大堆监控和运维的工具,安装和使用很方便。
    为了能继续后面的学习,你需要从MySQL官方网站下载并安装MySQL Community Server 5.6,这个版本是免费的,其他高级版本是要收钱的(请放心,收钱的功能我们用不上)。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    57#
     楼主| 发表于 2017-7-6 15:13:17 | 只看该作者
    使用SQLite[转]
    SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。
    Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。
    在使用SQLite前,我们先要搞清楚几个概念:
    表是数据库中存放关系数据的集合,一个数据库里面通常都包含多个表,比如学生的表,班级的表,学校的表,等等。表和表之间通过外键关联。
    要操作关系数据库,首先需要连接到数据库,一个数据库连接称为Connection;
    连接到数据库后,需要打开游标,称之为Cursor,通过Cursor执行SQL语句,然后,获得执行结果。
    Python定义了一套操作数据库的API接口,任何数据库要连接到Python,只需要提供符合Python标准的数据库驱动即可。
    由于SQLite的驱动内置在Python标准库中,所以我们可以直接来操作SQLite数据库。
    我们在Python交互式命令行实践一下:
    # 导入SQLite驱动:>>> import sqlite3# 连接到SQLite数据库# 数据库文件是test.db# 如果文件不存在,会自动在当前目录创建:>>> conn = sqlite3.connect('test.db')# 创建一个Cursor:>>> cursor = conn.cursor()# 执行一条SQL语句,创建user表:>>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')<sqlite3.Cursor object at 0x10f8aa260># 继续执行一条SQL语句,插入一条记录:>>> cursor.execute('insert into user (id, name) values (\'1\', \'Michael\')')<sqlite3.Cursor object at 0x10f8aa260># 通过rowcount获得插入的行数:>>> cursor.rowcount1# 关闭Cursor:>>> cursor.close()# 提交事务:>>> conn.commit()# 关闭Connection:>>> conn.close()
    我们再试试查询记录:
    >>> conn = sqlite3.connect('test.db')>>> cursor = conn.cursor()# 执行查询语句:>>> cursor.execute('select * from user where id=?', ('1',))<sqlite3.Cursor object at 0x10f8aa340># 获得查询结果集:>>> values = cursor.fetchall()>>> values[('1', 'Michael')]>>> cursor.close()>>> conn.close()
    使用Python的DB-API时,只要搞清楚Connection和Cursor对象,打开后一定记得关闭,就可以放心地使用。
    使用Cursor对象执行insert,update,delete语句时,执行结果由rowcount返回影响的行数,就可以拿到执行结果。
    使用Cursor对象执行select语句时,通过featchall()可以拿到结果集。结果集是一个list,每个元素都是一个tuple,对应一行记录。
    如果SQL语句带有参数,那么需要把参数按照位置传递给execute()方法,有几个?占位符就必须对应几个参数,例如:
    cursor.execute('select * from user where name=? and pwd=?', ('abc', 'password'))
    SQLite支持常见的标准SQL语句以及几种常见的数据类型。具体文档请参阅SQLite官方网站。
    小结
    在Python中操作数据库时,要先导入数据库对应的驱动,然后,通过Connection对象和Cursor对象操作数据。
    要确保打开的Connection对象和Cursor对象都正确地被关闭,否则,资源就会泄露。
    如何才能确保出错的情况下也关闭掉Connection对象和Cursor对象呢?请回忆try:...except:...finally:...的用法。
    练习
    请编写函数,在Sqlite中根据分数段查找指定的名字:
    # -*- coding: utf-8 -*-import os, sqlite3db_file = os.path.join(os.path.dirname(__file__), 'test.db')if os.path.isfile(db_file):    os.remove(db_file)# 初始数据:conn = sqlite3.connect(db_file)cursor = conn.cursor()cursor.execute('create table user(id varchar(20) primary key, name varchar(20), score int)')cursor.execute(r"insert into user values ('A-001', 'Adam', 95)")cursor.execute(r"insert into user values ('A-002', 'Bart', 62)")cursor.execute(r"insert into user values ('A-003', 'Lisa', 78)")cursor.close()conn.commit()conn.close()def get_score_in(low, high):    ' 返回指定分数区间的名字,按分数从低到高排序 '# 测试:assert get_score_in(80, 95) == ['Adam'], get_score_in(80, 95)assert get_score_in(60, 80) == ['Bart', 'Lisa'], get_score_in(60, 80)assert get_score_in(60, 100) == ['Bart', 'Lisa', 'Adam'], get_score_in(60, 100)print('Pass') Run


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    58#
     楼主| 发表于 2017-7-6 15:21:10 | 只看该作者
    使用MySQL[转]
    MySQL是Web世界中使用最广泛的数据库服务器。SQLite的特点是轻量级、可嵌入,但不能承受高并发访问,适合桌面和移动应用。而MySQL是为服务器端设计的数据库,能承受高并发访问,同时占用的内存也远远大于SQLite。
    此外,MySQL内部有多种数据库引擎,最常用的引擎是支持数据库事务的InnoDB。
    安装MySQL
    可以直接从MySQL官方网站下载最新的Community Server 5.6.x版本。MySQL是跨平台的,选择对应的平台下载安装文件,安装即可。
    安装时,MySQL会提示输入root用户的口令,请务必记清楚。如果怕记不住,就把口令设置为password。
    在Windows上,安装时请选择UTF-8编码,以便正确地处理中文。
    在Mac或Linux上,需要编辑MySQL的配置文件,把数据库默认的编码全部改为UTF-8。MySQL的配置文件默认存放在/etc/my.cnf或者/etc/mysql/my.cnf:
    [client]default-character-set = utf8[mysqld]default-storage-engine = INNODBcharacter-set-server = utf8collation-server = utf8_general_ci
    重启MySQL后,可以通过MySQL的客户端命令行检查编码:
    $ mysql -u root -pEnter password: Welcome to the MySQL monitor......mysql> show variables like '%char%';+--------------------------+--------------------------------------------------------+| Variable_name            | Value                                                  |+--------------------------+--------------------------------------------------------+| character_set_client     | utf8                                                   || character_set_connection | utf8                                                   || character_set_database   | utf8                                                   || character_set_filesystem | binary                                                 || character_set_results    | utf8                                                   || character_set_server     | utf8                                                   || character_set_system     | utf8                                                   || character_sets_dir       | /usr/local/mysql-5.1.65-osx10.6-x86_64/share/charsets/ |+--------------------------+--------------------------------------------------------+8 rows in set (0.00 sec)
    看到utf8字样就表示编码设置正确。
    注:如果MySQL的版本≥5.5.3,可以把编码设置为utf8mb4,utf8mb4和utf8完全兼容,但它支持最新的Unicode标准,可以显示emoji字符。
    安装MySQL驱动
    由于MySQL服务器以独立的进程运行,并通过网络对外服务,所以,需要支持Python的MySQL驱动来连接到MySQL服务器。MySQL官方提供了mysql-connector-python驱动,但是安装的时候需要给pip命令加上参数--allow-external:
    $ pip install mysql-connector-python --allow-external mysql-connector-python
    如果上面的命令安装失败,可以试试另一个驱动:
    $ pip install mysql-connector
    我们演示如何连接到MySQL服务器的test数据库:
    # 导入MySQL驱动:>>> import mysql.connector# 注意把password设为你的root口令:>>> conn = mysql.connector.connect(user='root', password='password', database='test')>>> cursor = conn.cursor()# 创建user表:>>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')# 插入一行记录,注意MySQL的占位符是%s:>>> cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael'])>>> cursor.rowcount1# 提交事务:>>> conn.commit()>>> cursor.close()# 运行查询:>>> cursor = conn.cursor()>>> cursor.execute('select * from user where id = %s', ('1',))>>> values = cursor.fetchall()>>> values[('1', 'Michael')]# 关闭Cursor和Connection:>>> cursor.close()True>>> conn.close()
    由于Python的DB-API定义都是通用的,所以,操作MySQL的数据库代码和SQLite类似。
    小结
    • 执行INSERT等操作后要调用commit()提交事务;
    • MySQL的SQL占位符是%s。



    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    59#
     楼主| 发表于 2017-7-6 15:22:45 | 只看该作者
    使用SQLAlchemy[转]
    数据库表是一个二维表,包含多行多列。把一个表的内容用Python的数据结构表示出来的话,可以用一个list表示多行,list的每一个元素是tuple,表示一行记录,比如,包含id和name的user表:
    [    ('1', 'Michael'),    ('2', 'Bob'),    ('3', 'Adam')]
    Python的DB-API返回的数据结构就是像上面这样表示的。
    但是用tuple表示一行很难看出表的结构。如果把一个tuple用class实例来表示,就可以更容易地看出表的结构来:
    class User(object):    def __init__(self, id, name):        self.id = id        self.name = name[    User('1', 'Michael'),    User('2', 'Bob'),    User('3', 'Adam')]
    这就是传说中的ORM技术:Object-Relational Mapping,把关系数据库的表结构映射到对象上。是不是很简单?
    但是由谁来做这个转换呢?所以ORM框架应运而生。
    在Python中,最有名的ORM框架是SQLAlchemy。我们来看看SQLAlchemy的用法。
    首先通过pip安装SQLAlchemy:
    $ pip install sqlalchemy
    然后,利用上次我们在MySQL的test数据库中创建的user表,用SQLAlchemy来试试:
    第一步,导入SQLAlchemy,并初始化DBSession:
    # 导入:from sqlalchemy import Column, String, create_enginefrom sqlalchemy.orm import sessionmakerfrom sqlalchemy.ext.declarative import declarative_base# 创建对象的基类:Base = declarative_base()# 定义User对象:class User(Base):    # 表的名字:    __tablename__ = 'user'    # 表的结构:    id = Column(String(20), primary_key=True)    name = Column(String(20))# 初始化数据库连接:engine = create_engine('mysql+mysqlconnector://root:password@localhost:3306/test')# 创建DBSession类型:DBSession = sessionmaker(bind=engine)
    以上代码完成SQLAlchemy的初始化和具体每个表的class定义。如果有多个表,就继续定义其他class,例如School:
    class School(Base):    __tablename__ = 'school'    id = ...    name = ...
    create_engine()用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息:
    '数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'
    你只需要根据需要替换掉用户名、口令等信息即可。
    下面,我们看看如何向数据库表中添加一行记录。
    由于有了ORM,我们向数据库表中添加一行记录,可以视为添加一个User对象:
    # 创建session对象:session = DBSession()# 创建新User对象:new_user = User(id='5', name='Bob')# 添加到session:session.add(new_user)# 提交即保存到数据库:session.commit()# 关闭session:session.close()
    可见,关键是获取session,然后把对象添加到session,最后提交并关闭。DBSession对象可视为当前数据库连接。
    如何从数据库表中查询数据呢?有了ORM,查询出来的可以不再是tuple,而是User对象。SQLAlchemy提供的查询接口如下:
    # 创建Session:session = DBSession()# 创建Query查询,filter是where条件,最后调用one()返回唯一行,如果调用all()则返回所有行:user = session.query(User).filter(User.id=='5').one()# 打印类型和对象的name属性:print('type:', type(user))print('name:', user.name)# 关闭Session:session.close()
    运行结果如下:
    type: <class '__main__.User'>name: Bob
    可见,ORM就是把数据库表的行与相应的对象建立关联,互相转换。
    由于关系数据库的多个表还可以用外键实现一对多、多对多等关联,相应地,ORM框架也可以提供两个对象之间的一对多、多对多等功能。
    例如,如果一个User拥有多个Book,就可以定义一对多关系如下:
    class User(Base):    __tablename__ = 'user'    id = Column(String(20), primary_key=True)    name = Column(String(20))    # 一对多:    books = relationship('Book')class Book(Base):    __tablename__ = 'book'    id = Column(String(20), primary_key=True)    name = Column(String(20))    # “多”的一方的book表是通过外键关联到user表的:    user_id = Column(String(20), ForeignKey('user.id'))
    当我们查询一个User对象时,该对象的books属性将返回一个包含若干个Book对象的list。
    小结
    ORM框架的作用就是把数据库表的一行记录与一个对象互相做自动转换。
    正确使用ORM的前提是了解关系数据库的原理。


    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    前天 09:07
  • 签到天数: 11 天

    连续签到: 2 天

    [LV.3]测试连长

    60#
     楼主| 发表于 2017-7-6 15:24:19 | 只看该作者
    Web开发【转】
    最早的软件都是运行在大型机上的,软件使用者通过“哑终端”登陆到大型机上去运行软件。后来随着PC机的兴起,软件开始主要运行在桌面上,而数据库这样的软件运行在服务器端,这种Client/Server模式简称CS架构。
    随着互联网的兴起,人们发现,CS架构不适合Web,最大的原因是Web应用程序的修改和升级非常迅速,而CS架构需要每个客户端逐个升级桌面App,因此,Browser/Server模式开始流行,简称BS架构。
    在BS架构下,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web页面,并把Web页面展示给用户即可。
    当然,Web页面也具有极强的交互性。由于Web页面是用HTML编写的,而HTML具备超强的表现力,并且,服务器端升级后,客户端无需任何部署就可以使用到新的版本,因此,BS架构迅速流行起来。
    今天,除了重量级的软件如Office,Photoshop等,大部分软件都以Web形式提供。比如,新浪提供的新闻、博客、微博等服务,均是Web应用。
    Web应用开发可以说是目前软件开发中最重要的部分。Web开发也经历了好几个阶段:
    • 静态Web页面:由文本编辑器直接编辑并生成静态的HTML页面,如果要修改Web页面的内容,就需要再次编辑HTML源文件,早期的互联网Web页面就是静态的;
    • CGI:由于静态Web页面无法与用户交互,比如用户填写了一个注册表单,静态Web页面就无法处理。要处理用户发送的动态数据,出现了Common Gateway Interface,简称CGI,用C/C++编写。
    • ASP/JSP/PHP:由于Web应用特点是修改频繁,用C/C++这样的低级语言非常不适合Web开发,而脚本语言由于开发效率高,与HTML结合紧密,因此,迅速取代了CGI模式。ASP是微软推出的用VBScript脚本编程的Web开发技术,而JSP用Java来编写脚本,PHP本身则是开源的脚本语言。
    • MVC:为了解决直接用脚本语言嵌入HTML导致的可维护性差的问题,Web应用也引入了Model-View-Controller的模式,来简化Web开发。ASP发展为ASP.Net,JSP和PHP也有一大堆MVC框架。

    目前,Web开发技术仍在快速发展中,异步开发、新的MVVM前端技术层出不穷。
    Python的诞生历史比Web还要早,由于Python是一种解释型的脚本语言,开发效率高,所以非常适合用来做Web开发。
    Python有上百种Web开发框架,有很多成熟的模板技术,选择Python开发Web应用,不但开发效率高,而且运行速度快。
    本章我们会详细讨论Python Web开发技术。


    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-9-21 23:32 , Processed in 0.098625 second(s), 21 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表