第八课 - 正则表达式 (作业)

奶茶馆价格结算系统优化:
1、 使用正则表达式判断顾客输入的手机号是否符合手机号设置规则:
1) 以数字 1 开头
2) 第二位为 3578 中的任意数字
3) 其余 9 位为 0-9 任意数字
2、 输出手机号运营商,判断规则:
 移动运营商:手机号前三位为 134、135、136、137、138、139
 联通运营商:手机号前三位为 130、131、132、155、156、176
 电信运营商:手机号前三位为 133、153、173、177、180、181
```
def OutputPhoneProvider(str):
cell_phone_info = {"(134|135|136|137|138|139)":"China Mobile", "(130|131|132|155|156|176)":"China Unicom", "(133|153|173|177|180|181)":"ChinaTelecom"}
for key in cell_phone_info.keys():
field = re.match(key, str)
if field != None:
print (cell_phone_info[key])
return

phoneNum = input("Please input the cell phone number:")
rs = re.match(r"1[3578]\d{9}$", phoneNum);
if rs != None:
OutputPhoneProvider(phoneNum)
else:
print("others")
```

3、 使用正则表达式判断输入奶茶编号,如果不在 1-5 范围内,输出: Woops!我们只售卖以上五种奶茶哦!新口味敬请期待!
```
import re
teaNum = input("Please input the tea number:")

rs = re.match(r"[1-5]$", teaNum)
if rs == None:
print("Woops! Please re-input the tea number!")
```
继续阅读 »
奶茶馆价格结算系统优化:
1、 使用正则表达式判断顾客输入的手机号是否符合手机号设置规则:
1) 以数字 1 开头
2) 第二位为 3578 中的任意数字
3) 其余 9 位为 0-9 任意数字
2、 输出手机号运营商,判断规则:
 移动运营商:手机号前三位为 134、135、136、137、138、139
 联通运营商:手机号前三位为 130、131、132、155、156、176
 电信运营商:手机号前三位为 133、153、173、177、180、181
```
def OutputPhoneProvider(str):
cell_phone_info = {"(134|135|136|137|138|139)":"China Mobile", "(130|131|132|155|156|176)":"China Unicom", "(133|153|173|177|180|181)":"ChinaTelecom"}
for key in cell_phone_info.keys():
field = re.match(key, str)
if field != None:
print (cell_phone_info[key])
return

phoneNum = input("Please input the cell phone number:")
rs = re.match(r"1[3578]\d{9}$", phoneNum);
if rs != None:
OutputPhoneProvider(phoneNum)
else:
print("others")
```

3、 使用正则表达式判断输入奶茶编号,如果不在 1-5 范围内,输出: Woops!我们只售卖以上五种奶茶哦!新口味敬请期待!
```
import re
teaNum = input("Please input the tea number:")

rs = re.match(r"[1-5]$", teaNum)
if rs == None:
print("Woops! Please re-input the tea number!")
``` 收起阅读 »

第八课 - 正则表达式

# 概述
正则表达式是一个特殊的字符序列,它常常用于检查是否与某种模式匹配。第八节课主要从以下几方面介绍了Python正则表达式的用法。
(1)re模块的使用
(2)字符匹配、数量表示、边界表示
(3)正则表达式的高级用法
(4)贪婪与非贪婪模式

# re模块
(一)match(正则表达式,待匹配字符串)
(1)采用从左向右逐项匹配,从起始位置起。
(2)用于正则匹配检查,如果“待匹配字符串”能够匹配“正则表达式”,则match方法返回匹配对象,否则返回None
```
import re #导入re模块

#从起始位置开始匹配
rs = re.match("chinahadoop", "chinahadoop.cn")
print(rs.group())

#不从起始位置开始匹配
rs = re.match("cn", "chinahadoop.cn")
```
运行结果:
```
chinahadoop
None
```

(二)group()方法 :用来返回字符串的匹配部分

# 字符匹配、数量表示、边界表示
**(一)单字符匹配**
![](http://wenda.chinahadoop.cn/up ... 6f.png)
(1). 匹配除"\n"之外的任意单个字符
```
import re

rs = re.match(".", "a")
print(rs.group())

rs = re.match("...", "abc") #多个字符
print(rs.group())

rs = re.match(".", "\n")
print(rs)
```
运行结果:
```
a
abc
None
```

(2)\s:匹配任意空白字符,如空格,制表符“\t”,换行符“\n”
```
import re
rs = re.match("\s", "\t")
print(rs)
rs = re.match("\s", "\n")
print(rs)
rs = re.match("\s", " ")
print(rs)
```
(3)\S:匹配任意非空字符;和\s模式相反
```
rs = re.match("\S", "\t")
print(rs)
rs = re.match("\S", "abc") #匹配单个字符,从起始位置
print(rs.group())
```
运行结果:
```
None
a
```
(4)[]: 匹配[ ]中列举的字符
```
rs = re.match("[Hh]", "hello")
print(rs.group())
rs = re.match("[Hh]", "Hello")
print(rs.group())

rs = re.match("[0123456789]", "32")
print(rs.group())
rs = re.match("[0-9]", "3")
print(rs.group())
```
运行返回结果:
```
h
H
3
3
```
其他单字符匹配用法不一一列举。
**(二)数量表示**
![](http://wenda.chinahadoop.cn/up ... 03.png)
(1)* 出现次数 n >= 0
```
import re

rs = re.match("1\d*", "1234567") #起始是1,后面数字[0-9]出现任意次
print(rs.group())

rs = re.match("1\d*", "1234567abc") #起始是1,后面数字[0-9]出现任意次
print(rs.group())
```
运行结果:
```
1234567
1234567
```
(2)+ 出现次数n >=1
```
rs = re.match("\d+", "abc")
print(rs)
rs = re.match("\d+", "1abc")
print(rs.group())
```
运行结果:
```
None
1
```
(3){m}, {m,} 和 {m, n}
```
#{m} : 一个字符出现m次
rs = re.match("\d{3}", "123abc")
print(rs.group())

#{m,} :一个字符至少出现m次
rs = re.match("\d{1,}", "123467abc") #等价于+至少一次
print(rs.group())

#{m,n} :一个字符出现m到n次
rs = re.match("\d{0,1}", "1abc") #等价于?至多一次
print(rs.group())
```
运行结果:
```
123
123467
1
```
(4)\转义字符
```
str1 = "hello\\world"
print(str1)

str2 = "hello\\\\world"
print(str2)
str3 = r"hello\\world" #原生字符:r"str"
print(str3)
rs = re.match("\w{5}\\\\\\\\\w{5}",str3)
print(rs.group())
rs = re.match(r"\w{5}\\\\\w{5}",str3)
print(rs.group())
```
运行结果:
```
hello\world
hello\\world
hello\\world
hello\\world
hello\\world
```
**(三)边界表示**
(1)字符串与单词边界:$结尾
```
rs = re.match("\w{3,10}@163.com","hello_124@163mcom")
print(rs.group())

rs = re.match("\w{3,10}@163\.com$","hello_124@163.com")
print(rs.group())
```
运行结果:
```
hello_124@163mcom
hello_124@163.com
```
注意:第一个邮箱匹配实际是我们不期望的。但是它仍然被匹配成功,是因为字符**. **被当成单字符匹配了。所以我们需要加上转义字符,让**. **被当成正常字符。
(2)匹配分组:()分组,索引可由自己制定,比如?P
```
html_str = "python"
rs = re.match(r"<.+><.+>.+",html_str)
print(rs.group())
html_str2 = "python</head>"
rs = re.match(r"<.+><.+>.+",html_str2) #wrong to match
print(rs.group())
rs = re.match(r"<(.+)><(.+)>.+",html_str) #\2 and \1 is an index
print(rs.group())
rs = re.match(r"<(?P.+)><(?P.+)>.+",html_str)
print(rs.group())
```
运行结果:
```
python
python</head>
python
python
```
# 正则表达式的高级用法:
**search(), findall(), finditer(), and sub()方法**
```
#search
rs = re.search("car","haha car carbal abcar carbal")
print(rs.group())

#findall : return a list
rs = re.findall("car","haha car carbal abcar carbal")
print(rs)

mail_str = "zhangsan:helloworld@163.com,li:123456@qq.cn"
list = re.findall(r"(\w{3,20}@(163|qq)\.(com|cn))",mail_str)
print(list)

#finditer
itor = re.finditer(r"\w{3,20}@(163|qq)\.(com|cn)",mail_str)
for it in itor:
print(it.group())

#sub :java 被python替换。sub()方法的第二个参数还可以是一个函数。
str = "java python c cpp java"
rs = re.sub(r"java","python",str)
print(rs)
```
运行结果:
```
car
['car', 'car', 'car', 'car']
[('helloworld@163.com', '163', 'com'), ('123456@qq.cn', 'qq', 'cn')]
helloworld@163.com
123456@qq.cn
python python c cpp python
```
# 贪婪与非贪婪模式
(1)贪婪模式:尽可能的匹配更多的字符
(2)非贪婪模式:与贪婪模式相反
```
rs = re.findall(r"hello\d*","hello12345")
print(rs)

rs = re.findall(r"hello\d*?","hello12345")
print(rs)
```
运行结果:
```
['hello12345']
['hello']
```
作业链接:
http://wenda.chinahadoop.cn/article/702
继续阅读 »
# 概述
正则表达式是一个特殊的字符序列,它常常用于检查是否与某种模式匹配。第八节课主要从以下几方面介绍了Python正则表达式的用法。
(1)re模块的使用
(2)字符匹配、数量表示、边界表示
(3)正则表达式的高级用法
(4)贪婪与非贪婪模式

# re模块
(一)match(正则表达式,待匹配字符串)
(1)采用从左向右逐项匹配,从起始位置起。
(2)用于正则匹配检查,如果“待匹配字符串”能够匹配“正则表达式”,则match方法返回匹配对象,否则返回None
```
import re #导入re模块

#从起始位置开始匹配
rs = re.match("chinahadoop", "chinahadoop.cn")
print(rs.group())

#不从起始位置开始匹配
rs = re.match("cn", "chinahadoop.cn")
```
运行结果:
```
chinahadoop
None
```

(二)group()方法 :用来返回字符串的匹配部分

# 字符匹配、数量表示、边界表示
**(一)单字符匹配**
![](http://wenda.chinahadoop.cn/up ... 6f.png)
(1). 匹配除"\n"之外的任意单个字符
```
import re

rs = re.match(".", "a")
print(rs.group())

rs = re.match("...", "abc") #多个字符
print(rs.group())

rs = re.match(".", "\n")
print(rs)
```
运行结果:
```
a
abc
None
```

(2)\s:匹配任意空白字符,如空格,制表符“\t”,换行符“\n”
```
import re
rs = re.match("\s", "\t")
print(rs)
rs = re.match("\s", "\n")
print(rs)
rs = re.match("\s", " ")
print(rs)
```
(3)\S:匹配任意非空字符;和\s模式相反
```
rs = re.match("\S", "\t")
print(rs)
rs = re.match("\S", "abc") #匹配单个字符,从起始位置
print(rs.group())
```
运行结果:
```
None
a
```
(4)[]: 匹配[ ]中列举的字符
```
rs = re.match("[Hh]", "hello")
print(rs.group())
rs = re.match("[Hh]", "Hello")
print(rs.group())

rs = re.match("[0123456789]", "32")
print(rs.group())
rs = re.match("[0-9]", "3")
print(rs.group())
```
运行返回结果:
```
h
H
3
3
```
其他单字符匹配用法不一一列举。
**(二)数量表示**
![](http://wenda.chinahadoop.cn/up ... 03.png)
(1)* 出现次数 n >= 0
```
import re

rs = re.match("1\d*", "1234567") #起始是1,后面数字[0-9]出现任意次
print(rs.group())

rs = re.match("1\d*", "1234567abc") #起始是1,后面数字[0-9]出现任意次
print(rs.group())
```
运行结果:
```
1234567
1234567
```
(2)+ 出现次数n >=1
```
rs = re.match("\d+", "abc")
print(rs)
rs = re.match("\d+", "1abc")
print(rs.group())
```
运行结果:
```
None
1
```
(3){m}, {m,} 和 {m, n}
```
#{m} : 一个字符出现m次
rs = re.match("\d{3}", "123abc")
print(rs.group())

#{m,} :一个字符至少出现m次
rs = re.match("\d{1,}", "123467abc") #等价于+至少一次
print(rs.group())

#{m,n} :一个字符出现m到n次
rs = re.match("\d{0,1}", "1abc") #等价于?至多一次
print(rs.group())
```
运行结果:
```
123
123467
1
```
(4)\转义字符
```
str1 = "hello\\world"
print(str1)

str2 = "hello\\\\world"
print(str2)
str3 = r"hello\\world" #原生字符:r"str"
print(str3)
rs = re.match("\w{5}\\\\\\\\\w{5}",str3)
print(rs.group())
rs = re.match(r"\w{5}\\\\\w{5}",str3)
print(rs.group())
```
运行结果:
```
hello\world
hello\\world
hello\\world
hello\\world
hello\\world
```
**(三)边界表示**
(1)字符串与单词边界:$结尾
```
rs = re.match("\w{3,10}@163.com","hello_124@163mcom")
print(rs.group())

rs = re.match("\w{3,10}@163\.com$","hello_124@163.com")
print(rs.group())
```
运行结果:
```
hello_124@163mcom
hello_124@163.com
```
注意:第一个邮箱匹配实际是我们不期望的。但是它仍然被匹配成功,是因为字符**. **被当成单字符匹配了。所以我们需要加上转义字符,让**. **被当成正常字符。
(2)匹配分组:()分组,索引可由自己制定,比如?P
```
html_str = "python"
rs = re.match(r"<.+><.+>.+",html_str)
print(rs.group())
html_str2 = "python</head>"
rs = re.match(r"<.+><.+>.+",html_str2) #wrong to match
print(rs.group())
rs = re.match(r"<(.+)><(.+)>.+",html_str) #\2 and \1 is an index
print(rs.group())
rs = re.match(r"<(?P.+)><(?P.+)>.+",html_str)
print(rs.group())
```
运行结果:
```
python
python</head>
python
python
```
# 正则表达式的高级用法:
**search(), findall(), finditer(), and sub()方法**
```
#search
rs = re.search("car","haha car carbal abcar carbal")
print(rs.group())

#findall : return a list
rs = re.findall("car","haha car carbal abcar carbal")
print(rs)

mail_str = "zhangsan:helloworld@163.com,li:123456@qq.cn"
list = re.findall(r"(\w{3,20}@(163|qq)\.(com|cn))",mail_str)
print(list)

#finditer
itor = re.finditer(r"\w{3,20}@(163|qq)\.(com|cn)",mail_str)
for it in itor:
print(it.group())

#sub :java 被python替换。sub()方法的第二个参数还可以是一个函数。
str = "java python c cpp java"
rs = re.sub(r"java","python",str)
print(rs)
```
运行结果:
```
car
['car', 'car', 'car', 'car']
[('helloworld@163.com', '163', 'com'), ('123456@qq.cn', 'qq', 'cn')]
helloworld@163.com
123456@qq.cn
python python c cpp python
```
# 贪婪与非贪婪模式
(1)贪婪模式:尽可能的匹配更多的字符
(2)非贪婪模式:与贪婪模式相反
```
rs = re.findall(r"hello\d*","hello12345")
print(rs)

rs = re.findall(r"hello\d*?","hello12345")
print(rs)
```
运行结果:
```
['hello12345']
['hello']
```
作业链接:
http://wenda.chinahadoop.cn/article/702 收起阅读 »

第八课

请看链接:http://note.youdao.com/notesha ... 1DFC4
 
如果有错,请帮忙指出,谢谢~
 

正则表达式.png

 
继续阅读 »
请看链接:http://note.youdao.com/notesha ... 1DFC4
 
如果有错,请帮忙指出,谢谢~
 

正则表达式.png

  收起阅读 »

第三课 多线程及分布式爬虫

一、多线程爬虫

image.png

(1)多线程的复杂性
a.资源、数据的安全性:锁保护
b.原子性:数据操作是天然互斥的
c.同步等待:wait()、notify()、notifyAll()
d.死锁:多个线程多资源互锁,造成死锁
e.容灾:任何线程出现错误,整个进程都会停止
(2)多线程的优势
a.内存空间共享
b.提高CPU使用效率
c.开发便捷
d.轻、创建、销毁的开销小

image.png

(3)Python线程
a.支持多线程(JavaScript、PHP不支持多线程)
b.python线程直接映射到native线程,(Java1.4的Java线程是JVM实现的,共同运行在一个native thread)
c.GIL(Global Interpretor Lock):对于多核的应用能力有限
(4)实现一个多线程爬虫
a.创建一个线程池thread=
b.确认url队列线程安全Queue Deque
c.从队列取出url,分配一个线程开始爬取pop()/get() threading.Thread
d.如果线程池满了,循环等待,直到线程结束t.is_alive()
e.从线程池移除已经完成下载的线程threads.remove(t)
f.如果当前级别的url已遍历完成,t.join()函数等待所有线程结束,然后开始下一级别的爬取
(4)多线程爬虫评价
优势:
1.有效利用CPU时间
2.极大减小下载出错、阻塞对抓取速度的影响,整体上提高下载的速度
3.对于没有反爬虫限制的网站,下载速度可以多倍增加
局限性:
1.对于有反爬的网站,速度提升有限
2.提高了复杂度,对编码要求高
3.线程越多,每个线程获得的时间就越少,同时线程切换更频繁也带来额外开销
4.线程之间资源竞争更激烈
二、多进程爬虫

image.png


image.png

(1)多进程爬虫评估
目的:
1.控制线程数量;
2.对线程进行隔离,减少资源竞争
3.某些环境下,在单机上利用多个IP来伪装
局限性:
1.不能突破网络瓶颈
2.单机单IP的情况下变得没有意义
3.数据交换的代价更大
(2)进程间通信
1.管道(PIPE)
2.信号(Signal):复杂
3.消息队列:Posix及system V
4.共享内存:速度最快,需要结合信号量达到进程间同步及互斥
5.信号量:用于数据同步
6.Socket:可以标准化,可以用于多机
(3)Android进程通信Binder

image.png

(4)Android进程通信AIDL

image.png

(5)创建多进程爬虫
Solution A - C/S模式
1.一个服务进程,入队及出队URL,入队需检查是否已经下载
2.监控目前的爬取状态,进度
3.多个爬取进程,从服务进程获取url,并将新的url返回给服务进程
4.使用Socket来做IPC
Solution B- 数据库模式
1.使用数据库来读写爬取列表
2.多个爬取进程,url的获取与增加都通过数据库操作
(6)C/S v.s.数据库
CS优势
1.运行速度快,添加、修改、查询都是内存的BIT位操作
2.扩展方便,例如动态url队列重排
数据库:
1.开发便捷,数据库天生具备读写保护及支持IPC
2.只需写一个爬虫程序
(7)创建MYSQL数据库表

image.png


image.png


image.png

三、分布式系统
(一)概念
(1)、A program
            is the code you write
(2)、A process
            is what you get when you run it
(3)、A message
            is used to communicate between processes
(4)、A packet
           is a fragment of a message that might travel on a wire
(5)、A protocol
           is a formal description of a message formats and the rules that two processes must follow in                     order to exchange those messages
(6)、A network
         is the infrastructure that links computer,workstations ,terminals,servers etc.It consists of routers             which are connected by communication links.
(7)、A component
         can be a process or any piece of hardware required to run a process,support communitions                       between processes,store data,etc.
(8)、A distributed system
        is an application that executes a collection of  protocols to coordinate the actions of multiple                    processes on a network,such that all components cooperate together perform a single or small              set of related tasks.
(二)、优势
(1)容错
(2)高可用
(3)可恢复
(4)一致性
(5)scalable:It can operate correctly even as some aspect of the system is scaled to a large size.
(6)predictable performance:the ability to provide desired responsiveness in a timely manner.
(7)安全性
(三)、分布式爬虫系统

image.png

四、Master-Slave结构
(一)、Master-Slave结构
(1)、有一个主机对所有服务器进行管理。绝大多数分布式系统,都是master-slave的主从模式。而我们之前的的爬虫是完全独立的,依次从URL队列里获取URL,进行抓取。
(2)、当爬虫服务器多的时候,必须能通过一个中心节点对从节点进行管理
(3)、能对整体的爬取进行控制
(4)、爬虫之间信息共享的桥梁
(5)、负载控制
(一)、Remote Procedure Calls
(1)、Specifies the protocol for client-server communitions
(2)、Develops the client program
(3)、Develops the server program

image.png

 
继续阅读 »
一、多线程爬虫

image.png

(1)多线程的复杂性
a.资源、数据的安全性:锁保护
b.原子性:数据操作是天然互斥的
c.同步等待:wait()、notify()、notifyAll()
d.死锁:多个线程多资源互锁,造成死锁
e.容灾:任何线程出现错误,整个进程都会停止
(2)多线程的优势
a.内存空间共享
b.提高CPU使用效率
c.开发便捷
d.轻、创建、销毁的开销小

image.png

(3)Python线程
a.支持多线程(JavaScript、PHP不支持多线程)
b.python线程直接映射到native线程,(Java1.4的Java线程是JVM实现的,共同运行在一个native thread)
c.GIL(Global Interpretor Lock):对于多核的应用能力有限
(4)实现一个多线程爬虫
a.创建一个线程池thread=
b.确认url队列线程安全Queue Deque
c.从队列取出url,分配一个线程开始爬取pop()/get() threading.Thread
d.如果线程池满了,循环等待,直到线程结束t.is_alive()
e.从线程池移除已经完成下载的线程threads.remove(t)
f.如果当前级别的url已遍历完成,t.join()函数等待所有线程结束,然后开始下一级别的爬取
(4)多线程爬虫评价
优势:
1.有效利用CPU时间
2.极大减小下载出错、阻塞对抓取速度的影响,整体上提高下载的速度
3.对于没有反爬虫限制的网站,下载速度可以多倍增加
局限性:
1.对于有反爬的网站,速度提升有限
2.提高了复杂度,对编码要求高
3.线程越多,每个线程获得的时间就越少,同时线程切换更频繁也带来额外开销
4.线程之间资源竞争更激烈
二、多进程爬虫

image.png


image.png

(1)多进程爬虫评估
目的:
1.控制线程数量;
2.对线程进行隔离,减少资源竞争
3.某些环境下,在单机上利用多个IP来伪装
局限性:
1.不能突破网络瓶颈
2.单机单IP的情况下变得没有意义
3.数据交换的代价更大
(2)进程间通信
1.管道(PIPE)
2.信号(Signal):复杂
3.消息队列:Posix及system V
4.共享内存:速度最快,需要结合信号量达到进程间同步及互斥
5.信号量:用于数据同步
6.Socket:可以标准化,可以用于多机
(3)Android进程通信Binder

image.png

(4)Android进程通信AIDL

image.png

(5)创建多进程爬虫
Solution A - C/S模式
1.一个服务进程,入队及出队URL,入队需检查是否已经下载
2.监控目前的爬取状态,进度
3.多个爬取进程,从服务进程获取url,并将新的url返回给服务进程
4.使用Socket来做IPC
Solution B- 数据库模式
1.使用数据库来读写爬取列表
2.多个爬取进程,url的获取与增加都通过数据库操作
(6)C/S v.s.数据库
CS优势
1.运行速度快,添加、修改、查询都是内存的BIT位操作
2.扩展方便,例如动态url队列重排
数据库:
1.开发便捷,数据库天生具备读写保护及支持IPC
2.只需写一个爬虫程序
(7)创建MYSQL数据库表

image.png


image.png


image.png

三、分布式系统
(一)概念
(1)、A program
            is the code you write
(2)、A process
            is what you get when you run it
(3)、A message
            is used to communicate between processes
(4)、A packet
           is a fragment of a message that might travel on a wire
(5)、A protocol
           is a formal description of a message formats and the rules that two processes must follow in                     order to exchange those messages
(6)、A network
         is the infrastructure that links computer,workstations ,terminals,servers etc.It consists of routers             which are connected by communication links.
(7)、A component
         can be a process or any piece of hardware required to run a process,support communitions                       between processes,store data,etc.
(8)、A distributed system
        is an application that executes a collection of  protocols to coordinate the actions of multiple                    processes on a network,such that all components cooperate together perform a single or small              set of related tasks.
(二)、优势
(1)容错
(2)高可用
(3)可恢复
(4)一致性
(5)scalable:It can operate correctly even as some aspect of the system is scaled to a large size.
(6)predictable performance:the ability to provide desired responsiveness in a timely manner.
(7)安全性
(三)、分布式爬虫系统

image.png

四、Master-Slave结构
(一)、Master-Slave结构
(1)、有一个主机对所有服务器进行管理。绝大多数分布式系统,都是master-slave的主从模式。而我们之前的的爬虫是完全独立的,依次从URL队列里获取URL,进行抓取。
(2)、当爬虫服务器多的时候,必须能通过一个中心节点对从节点进行管理
(3)、能对整体的爬取进行控制
(4)、爬虫之间信息共享的桥梁
(5)、负载控制
(一)、Remote Procedure Calls
(1)、Specifies the protocol for client-server communitions
(2)、Develops the client program
(3)、Develops the server program

image.png

  收起阅读 »

第二课 登陆及动态网页抓取

一、XPath
(1)、基本语法

image.png

(2)、@属性
在DOM 树,以路径的方式查询节点
通过 @符号来选取属性

image.png

rel  class href 都是属性,可以通过 "//*[@class='external text']" 来选取对应元素
= 符号要求属性完全匹配,可以用 contains contains 方法来部分匹配,例如:
“//*[contains(@class, ‘external’)]”  可以匹配,而 "//*[@class='external']"  则不能
(3)、运算符
and 和 or 运算符

image.png


二、正则表达式
正则表达式是对字符串操作的一种逻辑公式,就用事先定义好些特字符、及这些特定的组合,成一个“规则串”,串”用来表达对字符的一种过滤 逻辑
在爬虫的解析中,经常会将正则表达式与 Dom 选择器结合使用。正则表达式适用于字符串特征比较明显的情况,但是同样正则表达可能在HTML源码里多次出现;而Dom选择器可以通过 class及 id来精确找到DOM块,从而缩小查找范围。

image.png

(1)、爬虫常用规则
           a.获取标签下的文本,'<th [^>]*>(.*? )</th >'
           b.查找特定类别的链接,例如 /wiki/不包含 Category 目录:
             '<ahref href="/wiki/(?!Category:)[^/>]*>(.*?)<'
           c.查找商品外链,例如 jd 的商品外链为7位数字的a标签节点:'/\d{7}.html '
           d.查找淘宝的商品信息, ' 或者 " 开始及结尾
            'href=[\"\']{1}(//detail.taobao.com/item.htm[^>\"\'\s]+?)"'
(2)贪婪模式及非贪婪模式

image.png

 
三、动态网页
(1)、动态网页使用场景
• 单页模式
单页模式指的是不需要外部跳转网,例如个人设置中心经常就是单页
• 页面交互多的场景
一部分网页上,有很多的用户交互接口,例如去哪儿机票选择网页,用户可以反复修改查询的参数
• 内容及模块丰富的网页
有些网页内容很丰富,一次加载完对服务器压力大 ,而且这种方式延时也会很差,用户往往也不会查看所有内容
(2)、动态网页带来的挑战
对于爬虫:
• 简单下载 HMTL HMTL已经不行了,必须得有一个 Web容器来运行HTML的脚本
• 增加了爬取的时间
• 增加了计算机的 CPU 、内存的资源消耗
•增加了爬取的不确定性
对于网站:
• 为了配合搜索引擎的爬取,与相关信息会采用静态方式
•与搜索无关的信息,例如商品的价格评论,仍然会使用动态加载

image.png


image.png

(3)、分析动态网页
• 使用 BeyondCompare 或者 SVN等工具,来对比网页大致找出动态加载的部分
• 针对要提取的部分,别查看 html only与full webpage,找出动态数据的部分
• 记录下它们的 class 或 id , 试着用以下代码来提取,如果不能提取,说明是动态的:
from lxml import etree 
f = open ('./s7-full.htm')
c = f.read().decode( 'gbk')
f.close () 
e = etree.HTML (c) 
print e.xpath (u'//span[@class="price J-p-10524731933"]')
(4)、Python Web 引擎
• PyQt PySide :基于 QT 的python web 引擎,需要 图形界面的支持安装大量的以来 。安装和配置复杂,尤其是安装图形系统对于服务器来说代价很大。
• Selenium :一个自动化的 Web测试工具,可以支持包括 Firefox、 chrome、PhatomJS 、IE 等多种浏览器的的连接和测试。
PhantonJs:一个基于 Webkit的 Headless的Web引擎,支持JavaScript 。相比 PyQt 等方案, phamtoms可以部署在没有UI的服务器上。
PhantomJS + Selenium 
(5)、安装
Selenium 
pip install Selenium 
PhantomJS 
• PhantomJS 需要先安装 nodejs
# yum install nodejs
• 为了加速,将 NPM 的源改为国内的淘宝
$ npm install -g cnpm -- registry=https:// registry.npm.taobao.org registry.npm.taobao.orgregistry.npm.taobao.orgregistry.npm.taobao.org
• 利用 NPM的Package Manager安装phantomjs
$ npm-g install phantomjs -prebuilt
(6)、使用phantomjs来加载动态页面
# import webdriver from selenium
from selenium import webdriver
# load PhantomJS driver 
driver=webdriver.PhantomJS (service_args =['-- ignore -ssl -errors=true' ])
# set window size, better to fit the whole page in order to
# avoid dynamically loading data 
driver.set_window_size(1280 , 2400 ) # optional 
# data page content
driver.get(cur_url)
# use page_source to get html content
content = driver.page_source
 
(7)、set_window_size
对于动态网页,有可能存在大量数据是根视图来加载的PhantomJS允许客户端设置用来模拟渲染页面的窗口的尺寸,这个如尺寸如果设置比较小,我们就不得用javascript的scroll命令来模拟页面往下滑 动的效果以显示更多内容,所我们可设置一个相对大窗口高度来渲染
driver.set_window_size(1280 , 2400 ) # optional 
(8)、set_window_size
Selenium实现了一系列的类似于 xpath选择器的方法,使得我们可以直接 调用 river.find_element () 来进行元素的选择,但是这些都基于 Python 的实现,执行效率非常低大约是基于 C的 正则表达式或 lxml的10 倍的时间 ,因此 不建议使用 built -in 的选择器,而是采用 lxml或者 re 对 driver.page_source(html 文本)进行操作
find_element(self, by='id', value=None)
find_element_by_class_name(self, name)
find_element_by_id(self, id_)
find_element_by_css_selector(self, css_selector)
(9)、Useful Methods & Properties
Selenium通过浏览器的驱动,支持大量HTML及Javascript的操作,常用的可以包括:
• page_source : 获取当前的 html文本
• title:HTML的title
• current_url:当前网页的 URL
• get_cookie() &get_cookies() :获取当前的 cookie cookie
• delete_cookie() &delete_all_cookies() :删除所有的cookie
• add_cookie() :添加一段 cookie
• set_page_load_timeout() :设置网页超时
• execute_script() :同步执行一段 javascript命令
• execute_async_script() :异步执行 javascript命令
(10)、Close and Clear
Selenium通过内嵌的浏览器 driver与浏览器进程通信,因此在退出的时 候必须调用 driver.close() 及 driver.quit() 来退出 PhantomJS,否则PhantomJS会一直运行在后台并占用系统资源。

image.png

1.send_signal is recommended
driver.service.process.send_signal(signal.SIGTERM)
2. driver.close () this is not guaranteed to close PhantomJS
3. to assure it's closed, run below command in terminal pgrep phantomjs | xargs kill
 
(11)、提取动态数据
1. 加载的过程中,根据网络环境优劣会存在一些延时因此要多次尝试提取,提取不到不意味着数据不存在或者网络出错
2. 动态页面的元素,所使用id或 class经常会不止一 个,例如京东件商品的“好评率”, class包括了rate 和 percent -con 两种,因此需要对两种情况都进行尝试。更通用的,如果一个元素不能找到而selenium并没有报网络错误,那么可能这个元素的class或id有了新的定义,我们需要将找不到页面及元素信息记录在日志里,使得后续可以分析,找出新的定义并对这一类页面重新提取信息。

image.png


image.png


image.png


image.png


image.png

 

image.png

 
继续阅读 »
一、XPath
(1)、基本语法

image.png

(2)、@属性
在DOM 树,以路径的方式查询节点
通过 @符号来选取属性

image.png

rel  class href 都是属性,可以通过 "//*[@class='external text']" 来选取对应元素
= 符号要求属性完全匹配,可以用 contains contains 方法来部分匹配,例如:
“//*[contains(@class, ‘external’)]”  可以匹配,而 "//*[@class='external']"  则不能
(3)、运算符
and 和 or 运算符

image.png


二、正则表达式
正则表达式是对字符串操作的一种逻辑公式,就用事先定义好些特字符、及这些特定的组合,成一个“规则串”,串”用来表达对字符的一种过滤 逻辑
在爬虫的解析中,经常会将正则表达式与 Dom 选择器结合使用。正则表达式适用于字符串特征比较明显的情况,但是同样正则表达可能在HTML源码里多次出现;而Dom选择器可以通过 class及 id来精确找到DOM块,从而缩小查找范围。

image.png

(1)、爬虫常用规则
           a.获取标签下的文本,'<th [^>]*>(.*? )</th >'
           b.查找特定类别的链接,例如 /wiki/不包含 Category 目录:
             '<ahref href="/wiki/(?!Category:)[^/>]*>(.*?)<'
           c.查找商品外链,例如 jd 的商品外链为7位数字的a标签节点:'/\d{7}.html '
           d.查找淘宝的商品信息, ' 或者 " 开始及结尾
            'href=[\"\']{1}(//detail.taobao.com/item.htm[^>\"\'\s]+?)"'
(2)贪婪模式及非贪婪模式

image.png

 
三、动态网页
(1)、动态网页使用场景
• 单页模式
单页模式指的是不需要外部跳转网,例如个人设置中心经常就是单页
• 页面交互多的场景
一部分网页上,有很多的用户交互接口,例如去哪儿机票选择网页,用户可以反复修改查询的参数
• 内容及模块丰富的网页
有些网页内容很丰富,一次加载完对服务器压力大 ,而且这种方式延时也会很差,用户往往也不会查看所有内容
(2)、动态网页带来的挑战
对于爬虫:
• 简单下载 HMTL HMTL已经不行了,必须得有一个 Web容器来运行HTML的脚本
• 增加了爬取的时间
• 增加了计算机的 CPU 、内存的资源消耗
•增加了爬取的不确定性
对于网站:
• 为了配合搜索引擎的爬取,与相关信息会采用静态方式
•与搜索无关的信息,例如商品的价格评论,仍然会使用动态加载

image.png


image.png

(3)、分析动态网页
• 使用 BeyondCompare 或者 SVN等工具,来对比网页大致找出动态加载的部分
• 针对要提取的部分,别查看 html only与full webpage,找出动态数据的部分
• 记录下它们的 class 或 id , 试着用以下代码来提取,如果不能提取,说明是动态的:
from lxml import etree 
f = open ('./s7-full.htm')
c = f.read().decode( 'gbk')
f.close () 
e = etree.HTML (c) 
print e.xpath (u'//span[@class="price J-p-10524731933"]')
(4)、Python Web 引擎
• PyQt PySide :基于 QT 的python web 引擎,需要 图形界面的支持安装大量的以来 。安装和配置复杂,尤其是安装图形系统对于服务器来说代价很大。
• Selenium :一个自动化的 Web测试工具,可以支持包括 Firefox、 chrome、PhatomJS 、IE 等多种浏览器的的连接和测试。
PhantonJs:一个基于 Webkit的 Headless的Web引擎,支持JavaScript 。相比 PyQt 等方案, phamtoms可以部署在没有UI的服务器上。
PhantomJS + Selenium 
(5)、安装
Selenium 
pip install Selenium 
PhantomJS 
• PhantomJS 需要先安装 nodejs
# yum install nodejs
• 为了加速,将 NPM 的源改为国内的淘宝
$ npm install -g cnpm -- registry=https:// registry.npm.taobao.org registry.npm.taobao.orgregistry.npm.taobao.orgregistry.npm.taobao.org
• 利用 NPM的Package Manager安装phantomjs
$ npm-g install phantomjs -prebuilt
(6)、使用phantomjs来加载动态页面
# import webdriver from selenium
from selenium import webdriver
# load PhantomJS driver 
driver=webdriver.PhantomJS (service_args =['-- ignore -ssl -errors=true' ])
# set window size, better to fit the whole page in order to
# avoid dynamically loading data 
driver.set_window_size(1280 , 2400 ) # optional 
# data page content
driver.get(cur_url)
# use page_source to get html content
content = driver.page_source
 
(7)、set_window_size
对于动态网页,有可能存在大量数据是根视图来加载的PhantomJS允许客户端设置用来模拟渲染页面的窗口的尺寸,这个如尺寸如果设置比较小,我们就不得用javascript的scroll命令来模拟页面往下滑 动的效果以显示更多内容,所我们可设置一个相对大窗口高度来渲染
driver.set_window_size(1280 , 2400 ) # optional 
(8)、set_window_size
Selenium实现了一系列的类似于 xpath选择器的方法,使得我们可以直接 调用 river.find_element () 来进行元素的选择,但是这些都基于 Python 的实现,执行效率非常低大约是基于 C的 正则表达式或 lxml的10 倍的时间 ,因此 不建议使用 built -in 的选择器,而是采用 lxml或者 re 对 driver.page_source(html 文本)进行操作
find_element(self, by='id', value=None)
find_element_by_class_name(self, name)
find_element_by_id(self, id_)
find_element_by_css_selector(self, css_selector)
(9)、Useful Methods & Properties
Selenium通过浏览器的驱动,支持大量HTML及Javascript的操作,常用的可以包括:
• page_source : 获取当前的 html文本
• title:HTML的title
• current_url:当前网页的 URL
• get_cookie() &get_cookies() :获取当前的 cookie cookie
• delete_cookie() &delete_all_cookies() :删除所有的cookie
• add_cookie() :添加一段 cookie
• set_page_load_timeout() :设置网页超时
• execute_script() :同步执行一段 javascript命令
• execute_async_script() :异步执行 javascript命令
(10)、Close and Clear
Selenium通过内嵌的浏览器 driver与浏览器进程通信,因此在退出的时 候必须调用 driver.close() 及 driver.quit() 来退出 PhantomJS,否则PhantomJS会一直运行在后台并占用系统资源。

image.png

1.send_signal is recommended
driver.service.process.send_signal(signal.SIGTERM)
2. driver.close () this is not guaranteed to close PhantomJS
3. to assure it's closed, run below command in terminal pgrep phantomjs | xargs kill
 
(11)、提取动态数据
1. 加载的过程中,根据网络环境优劣会存在一些延时因此要多次尝试提取,提取不到不意味着数据不存在或者网络出错
2. 动态页面的元素,所使用id或 class经常会不止一 个,例如京东件商品的“好评率”, class包括了rate 和 percent -con 两种,因此需要对两种情况都进行尝试。更通用的,如果一个元素不能找到而selenium并没有报网络错误,那么可能这个元素的class或id有了新的定义,我们需要将找不到页面及元素信息记录在日志里,使得后续可以分析,找出新的定义并对这一类页面重新提取信息。

image.png


image.png


image.png


image.png


image.png

 

image.png

  收起阅读 »

类属性、类方法

类属性是类的属性,可以在类内(包括_init_构造方法)中修改类属性,也可以"类名.类属性=”来修改,但不能通过实例方法“实例名.类属性=”修改类属性。
类产生实例时都要运行_init_,可能会修改类属性,当通过“实例名.属性”调用时,先检查构造方法中有无同名属性,如有则调实例的属性,如没有则调类属性。类属性是所有实例共有,类属性的改变将影响所有实例。
 
类方法,用@classmethod标识,带(cls),用来操作类属性,可以通过“类名。类方法”和“实例。类方法”调用。
 
类和实例是两种类型的对象,类方法操作类属性,实例方法操作实例属性,由于实例是基于类产生的,所有实例继承了类的属性,不同实例可以有不同的实例属性值,但类属性值相同。类的属性和方法都可以通过实例来调用,但类的属性值只能通过类方法来修改。关系容易乱,推荐各管各的,不混着操作。
继续阅读 »
类属性是类的属性,可以在类内(包括_init_构造方法)中修改类属性,也可以"类名.类属性=”来修改,但不能通过实例方法“实例名.类属性=”修改类属性。
类产生实例时都要运行_init_,可能会修改类属性,当通过“实例名.属性”调用时,先检查构造方法中有无同名属性,如有则调实例的属性,如没有则调类属性。类属性是所有实例共有,类属性的改变将影响所有实例。
 
类方法,用@classmethod标识,带(cls),用来操作类属性,可以通过“类名。类方法”和“实例。类方法”调用。
 
类和实例是两种类型的对象,类方法操作类属性,实例方法操作实例属性,由于实例是基于类产生的,所有实例继承了类的属性,不同实例可以有不同的实例属性值,但类属性值相同。类的属性和方法都可以通过实例来调用,但类的属性值只能通过类方法来修改。关系容易乱,推荐各管各的,不混着操作。 收起阅读 »

第七课:面向对象(下)、异常处理、包和模块

# 概述:
上一节主要介绍了面向对象的一些基本概念:类,类的组成元素“方法和属性”,类的特性“继承,多态和封装”。这一节课分为三部分:
(一)类属性、类方法及调用、静态方法及调用和单例类
(二)异常处理
(三)包和模块

# 第一部分:类方法、静态方法和单例类
**类属性**
(1)类属性区别于实例的属性,类属性属于类对象,被实例对象所共享
(2)查找属性规则:先实例对象的实例属性;后类对象的类(?)属性
(3)通过实例对象不能修改类属性的值;如果修改的属性在实例中不存在,则动态添加实例属性
```
class Person:
sum_num = 0

def __init__(self):
Person.sum_num += 1 #修改类属性值,每创建一个实例对象,类属性值加1

#实例对象p1和类对象的类属性值一致
p1 = Person("zhangsan")
print(p1.sum_num, Person.sum_num)


#通过实例对象不能修改类属性值,如果修改的属性在实例总不存在,则动态添加实例属性
p1.sum_num = 100
print(p1.sum_num, Person.sum_num)


```
**类方法及调用**

(1)使用@classmethod修饰的方法,第一个参数是cls
(2)类方法操作类属性;实例方法操作实例对象属性
(3)调用方式:类名.类方法();实例对象.类方法() #不推荐用此方法调用类方法
```
class Date(object):

def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year

#类方法
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
```
**静态方法及调用**

(1)使用@staticmethod修饰的方法,不需要传入任何参数
(2)应用在跟类和对象没有关系的一些功能上。尽量少用静态方法。
```
#静态方法
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
```
**单例类**
(1)__new__(cls)是超类object类内置的方法。参数cls是类对象
(2)用户创建对象,返回创建对象的引用。
```
#通过id(cls)可以查看类对象以及实例对象的地址
class DataBaseObj(object):
def __init__(self): #对象初始化
print("-------init 构造函数-------")

def __new__(cls): #构造对象
print("cls_id:" id(cls))
return object.__new__(cls) #返回对象的引用
```
(3)在整个程序系统中,只有一个实例对象。概念等同设计模式中的singleton模式。
```
class SingleInstance:
__instance = None
def __init__(self):
print("-----init-----")

def __new__(cls):
if cls.__instance == None:
cls.__instance = object.__new__(cls)
return cls.__instance

s1 = SingleInstance()
print(id(s1))
s2 = SingleInstance()
print(id(s2))
```

# 第二部分:异常处理
(1)捕获异常
```
try:
逻辑代码
except ExceptionType as err:
异常处理
```
(2)捕获多个异常
```
try:
逻辑代码
except (ExceptionType1, ExceptionType2, ...) as err:
异常处理
```
(3)捕获所有可能发生的异常
```
try:
逻辑代码
except Exception as err:
异常处理
```
(4)finally
```
try:
逻辑代码
except Exception as err:
异常处理
finally:
代码块 (无论是否有异常发生,代码都会执行到这里)
```
(4)异常处理的意义:
* 防止某个业务产生异常而影响到整个程序中的其他业务
* 异常处理的本质不是解决异常,而是将出现异常时的不良影响降到最低

# 第三部分:包和模块
**(一)Python项目结构:**
(1)1个项目:n个包
(2)一个包:n个模块
(3)模块的名字:以.py为扩展名命名的文件
**(二)引入模块的方法:**
(1)导入一个模块:import model_name 或者 import module_name.model_name
(2)导入多个模块:import model_name1, model_name2, ...
(3)导入某个模块中的函数:from model_name import fun1, fun2
**(三)import model_name Vs from model_name import fun**
(1)前者是导入一模块,如果想引用模块的内容(class, method,variables...)必须用全名,即 [module_name].[attr_name]。
(2)后者是导入某一指定对象(class, 其它对象...), 然后就可以直接用这些对象,不需要加上[module_name], 即[attr_name]。
**(四)特别用法:**
(1)如果你只想导入一个包里面的某个模块被其他包所使用,你可以在__init__.py文件里添加一句:__all__ = ["model_name"]
(2)批量导入:from package import *;(1)对(2)起约束作用。
继续阅读 »
# 概述:
上一节主要介绍了面向对象的一些基本概念:类,类的组成元素“方法和属性”,类的特性“继承,多态和封装”。这一节课分为三部分:
(一)类属性、类方法及调用、静态方法及调用和单例类
(二)异常处理
(三)包和模块

# 第一部分:类方法、静态方法和单例类
**类属性**
(1)类属性区别于实例的属性,类属性属于类对象,被实例对象所共享
(2)查找属性规则:先实例对象的实例属性;后类对象的类(?)属性
(3)通过实例对象不能修改类属性的值;如果修改的属性在实例中不存在,则动态添加实例属性
```
class Person:
sum_num = 0

def __init__(self):
Person.sum_num += 1 #修改类属性值,每创建一个实例对象,类属性值加1

#实例对象p1和类对象的类属性值一致
p1 = Person("zhangsan")
print(p1.sum_num, Person.sum_num)


#通过实例对象不能修改类属性值,如果修改的属性在实例总不存在,则动态添加实例属性
p1.sum_num = 100
print(p1.sum_num, Person.sum_num)


```
**类方法及调用**

(1)使用@classmethod修饰的方法,第一个参数是cls
(2)类方法操作类属性;实例方法操作实例对象属性
(3)调用方式:类名.类方法();实例对象.类方法() #不推荐用此方法调用类方法
```
class Date(object):

def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year

#类方法
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
```
**静态方法及调用**

(1)使用@staticmethod修饰的方法,不需要传入任何参数
(2)应用在跟类和对象没有关系的一些功能上。尽量少用静态方法。
```
#静态方法
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
```
**单例类**
(1)__new__(cls)是超类object类内置的方法。参数cls是类对象
(2)用户创建对象,返回创建对象的引用。
```
#通过id(cls)可以查看类对象以及实例对象的地址
class DataBaseObj(object):
def __init__(self): #对象初始化
print("-------init 构造函数-------")

def __new__(cls): #构造对象
print("cls_id:" id(cls))
return object.__new__(cls) #返回对象的引用
```
(3)在整个程序系统中,只有一个实例对象。概念等同设计模式中的singleton模式。
```
class SingleInstance:
__instance = None
def __init__(self):
print("-----init-----")

def __new__(cls):
if cls.__instance == None:
cls.__instance = object.__new__(cls)
return cls.__instance

s1 = SingleInstance()
print(id(s1))
s2 = SingleInstance()
print(id(s2))
```

# 第二部分:异常处理
(1)捕获异常
```
try:
逻辑代码
except ExceptionType as err:
异常处理
```
(2)捕获多个异常
```
try:
逻辑代码
except (ExceptionType1, ExceptionType2, ...) as err:
异常处理
```
(3)捕获所有可能发生的异常
```
try:
逻辑代码
except Exception as err:
异常处理
```
(4)finally
```
try:
逻辑代码
except Exception as err:
异常处理
finally:
代码块 (无论是否有异常发生,代码都会执行到这里)
```
(4)异常处理的意义:
* 防止某个业务产生异常而影响到整个程序中的其他业务
* 异常处理的本质不是解决异常,而是将出现异常时的不良影响降到最低

# 第三部分:包和模块
**(一)Python项目结构:**
(1)1个项目:n个包
(2)一个包:n个模块
(3)模块的名字:以.py为扩展名命名的文件
**(二)引入模块的方法:**
(1)导入一个模块:import model_name 或者 import module_name.model_name
(2)导入多个模块:import model_name1, model_name2, ...
(3)导入某个模块中的函数:from model_name import fun1, fun2
**(三)import model_name Vs from model_name import fun**
(1)前者是导入一模块,如果想引用模块的内容(class, method,variables...)必须用全名,即 [module_name].[attr_name]。
(2)后者是导入某一指定对象(class, 其它对象...), 然后就可以直接用这些对象,不需要加上[module_name], 即[attr_name]。
**(四)特别用法:**
(1)如果你只想导入一个包里面的某个模块被其他包所使用,你可以在__init__.py文件里添加一句:__all__ = ["model_name"]
(2)批量导入:from package import *;(1)对(2)起约束作用。
收起阅读 »

第一课 爬虫的基础技术

一、HTTP协议及HTML相关技术
(一)、HTTP协议

image.png


1.png


2.png



 
(二)、HTTP HEADER


image.png


红色标记比较重要
Keep-Alive功能使客户端到服务器的连接持续有效,当出现对后继请求时, Keep-Alive功能避免了建立或者重新建立连接。
HTTP/1.1默认所有情况下在HTTP1.1中所有连接都被保持,除非在请求头或响应头中指明要关闭:Connection:Close

image.png


(三)、HTTP请求方法

image.png


(四)、HTTP响应状态码
1、2XX  成功
2、3XX  跳转
3、4XX  客户端错误
4、5XX  服务器错误
 
300
1、300 Multiple Choices 存在多个可用的资源,处理或丢弃
2、301 Moved Permanetly重定向
3、302 Found 重定向
4、304 Not Modified 请求的资源未更新,丢弃
一些 Python 库,例如urllib2已结对重定向做了处理,会自动跳转;动态网页处理的时候也是自动跳转,所以不需要单独处理。
 
400、500
1、400 Bad Request客户端请求有语法错误,不能被服务器所 请求有语法错误,不能被服务器所理解
2、401 Unauthorized请求未经授权,这个状态代码必须和 WWW-Authenticate报头域一起使用
3、403 Forbidden服务器收到请求,但是拒绝提供 服务
4、404 Not Found请求资源不存在, eg :输入了错误的URL
5、 500 Internal Server Error服务器 发生不可预期的错误
6、503 Server Unavailable服务器当前不能处理客户端的请求,一段时间后可能恢复正常
 
错误处理
1、400 Bad Request 检查请求的参数或者路径
2、401 Unauthorized如果需要授权的网页,尝试重新登录
3、403 ForbiddenForbidden Forbidden
(1)如果是需要登录的网站,尝试重新登录
(2)IP 被封,暂停爬取并增加虫的等待时间,如果拨号网络尝试重新联网更改 IP
4、404 Not Found 直接丢弃
6、5XX 服务器错误, 直接丢弃,并计数,如果连续不成功,WARNING并停止爬取
 
(五)、HTML及CSS


image.png


image.png


HTML 超文本标记语言,简单来说可以认为一种规范或者协议浏 览器根据HTML的语言规范来解析
HTML里与爬虫相关的规范有以下:
这一类 tagtag 标记了外链用来做抓取,而 tr 、p用来进行内容抽取
• Tag:<a> <tr > <p >
id 、class 用在 css 上的,能帮助我们定位元素,作用于tr p 类似:
• Class: class=“home -price”
• Id: id=‘price -frame’ 
 
(六)、DOM树


image.png


(七)、Javascript
Javascript就是运行在前端的编程语言,最典型用动态网页数据、内容加载及呈现上, javascript做网络请求的时候最常用技术成为AJAX(Asynchronous JavaScript and XML),专门用来异步请求数据,这些是我们抓取的时候需要用到的。
 
二、宽度及深度抓取
(一)、网页抓取原理

image.png


(二)、爬虫的抓取对象类型
1、静态网页
2、动态网页
3、Web Service
(三)、深度优先策略


image.png


 
(四)、宽度优先策略


image.png


 
(五)、选择哪种策略
1、重要的网页距离种子站点比较近
2、 万维网的深度并没有很,一个页多路径可以到达 
3、 宽度优先有利于多爬虫并行合作抓取
4、深度限制于宽度优先相结合
 
三、不重复抓取策略
(一)、如何记录抓取历史
1. 将访问过的URL保存到数据库效率太低
2. 用HashSet HashSet 将访问过的 URL 保存起来。那只需接近 O(1)的代价就可以查到一个 URL是否被访问过了。 消耗内存
3. URL 经过 MD5 或SHA -1等单向哈希后再保存到 HashSet 或数据库。
4. Bit-Map方法。建立一个 BitSet ,将每个 URL 经过一个哈希函数映射到某一位。
(二)、MD5函数
MD5 签名是一个哈希函数,可以将任意长度的据量转换为固定长度数字,(通常是 4个整型, 128 位)。计算机不可能有 2的128 那么大内存,因此实际的哈 希表都会是 URL.MD5再%n (即取模)。现实世界的URL组合必然超越哈希表的槽位数,因此碰撞是一定存在的,一般的Hash函数,例如Java的HashTable是一 个HASH 表再跟上一个链表,链表里存的是碰撞的结果。

image.png


(三)、提高效率
1、评估网站的页数量
2、选择合适的 HASH 算法和空间阈值, 降低碰撞几率
3、选择合适的存储结构和算法
(四)、评估网页数量
site:网址
(五)、BITMAP方式记录
将URL的MD5值再次哈希,用一个或多BIT位来记录一个URL :
1. 确定空间大小 e.g.  facebook 1.5Gb
2. 按倍增加槽位 e.g. 16GB
3. HASH算法映射 ( murmurhash3, cityhash)Python: mmh3 bitarray
 
pip install murmurhash3 bitarray


image.png



• 优势:对存储进行了进一步压缩,在 MD5 的基础上,可以从 128 位最多压缩到 1位,一般情况如果用 4bit或者 8bit表示一个 url ,也能压缩 32 或者 16 倍
• 缺陷 :碰撞概率增加
(六)、Bloom Filter 
Bloom Filter使用了多个哈希函数,而不是一个。 创建一个 m位BitSet,先将所有位初始化为 0,然后选择 k个不同的哈希函数。第 i个哈希函数对字符串str哈希的结果记为 h(i,str ),且 h(i,str )的范围是 0到m-1。
只能插入,不能删除!!


image.png


(七)、pybloomfilter
安装
pip install pybloomfilter(可能运行时会 crash crash )
git clone https://github.com/axiak/pybloomfiltermmap.git 
python setup.py install
构造函数
class pybloomfilter.BloomFilter(capacity : int, error_rate:float[,filename=None:String][, perm=0755 ])
并不实际检查容量,如果需要比较低的error_rate,则需要设置更大的容量


image.png



官方文档
https://media.readthedocs.org/pdf/pybloomfiltermmap3/latest/pybloomfiltermmap3.pdf 
 
(八)、如何有效记录抓取历史
1、多数情况下不需要压缩,尤其网页量少的
2、网页数量大的情况下,使用 Bloom Filter压缩
3、重点是计算碰撞概率,并根据来确定存储空间的阈值
4、分布式系统、将散列映射到多台主机的内存
 
四、网站结构分析
1、网站对爬虫的限制
2、利用sitemap来分析网站结构和估算目标网页的规模
3、有效率抓取特定内容
      利用sitemap里的信息,直接对网页.html进行抓取
4、对网站目录结构进行分析
大多数网站都会存在明确的 toptodown 的分类目录结构,我们可以进入特定行抓取
对于 www.mafengwo.cn这个网站,所有旅游的记都位于 www.mafengwo.cn/mdd下,按照城市进 行了分类,每个城市的游记位于首页。
城市的首页: /travel-scenic scenic-spot/mafengwo/10774 .html
游记的分页格式: /yj /10774/1-0-01 .html
游记的页面: /i/3523364.html
继续阅读 »
一、HTTP协议及HTML相关技术
(一)、HTTP协议

image.png


1.png


2.png



 
(二)、HTTP HEADER


image.png


红色标记比较重要
Keep-Alive功能使客户端到服务器的连接持续有效,当出现对后继请求时, Keep-Alive功能避免了建立或者重新建立连接。
HTTP/1.1默认所有情况下在HTTP1.1中所有连接都被保持,除非在请求头或响应头中指明要关闭:Connection:Close

image.png


(三)、HTTP请求方法

image.png


(四)、HTTP响应状态码
1、2XX  成功
2、3XX  跳转
3、4XX  客户端错误
4、5XX  服务器错误
 
300
1、300 Multiple Choices 存在多个可用的资源,处理或丢弃
2、301 Moved Permanetly重定向
3、302 Found 重定向
4、304 Not Modified 请求的资源未更新,丢弃
一些 Python 库,例如urllib2已结对重定向做了处理,会自动跳转;动态网页处理的时候也是自动跳转,所以不需要单独处理。
 
400、500
1、400 Bad Request客户端请求有语法错误,不能被服务器所 请求有语法错误,不能被服务器所理解
2、401 Unauthorized请求未经授权,这个状态代码必须和 WWW-Authenticate报头域一起使用
3、403 Forbidden服务器收到请求,但是拒绝提供 服务
4、404 Not Found请求资源不存在, eg :输入了错误的URL
5、 500 Internal Server Error服务器 发生不可预期的错误
6、503 Server Unavailable服务器当前不能处理客户端的请求,一段时间后可能恢复正常
 
错误处理
1、400 Bad Request 检查请求的参数或者路径
2、401 Unauthorized如果需要授权的网页,尝试重新登录
3、403 ForbiddenForbidden Forbidden
(1)如果是需要登录的网站,尝试重新登录
(2)IP 被封,暂停爬取并增加虫的等待时间,如果拨号网络尝试重新联网更改 IP
4、404 Not Found 直接丢弃
6、5XX 服务器错误, 直接丢弃,并计数,如果连续不成功,WARNING并停止爬取
 
(五)、HTML及CSS


image.png


image.png


HTML 超文本标记语言,简单来说可以认为一种规范或者协议浏 览器根据HTML的语言规范来解析
HTML里与爬虫相关的规范有以下:
这一类 tagtag 标记了外链用来做抓取,而 tr 、p用来进行内容抽取
• Tag:<a> <tr > <p >
id 、class 用在 css 上的,能帮助我们定位元素,作用于tr p 类似:
• Class: class=“home -price”
• Id: id=‘price -frame’ 
 
(六)、DOM树


image.png


(七)、Javascript
Javascript就是运行在前端的编程语言,最典型用动态网页数据、内容加载及呈现上, javascript做网络请求的时候最常用技术成为AJAX(Asynchronous JavaScript and XML),专门用来异步请求数据,这些是我们抓取的时候需要用到的。
 
二、宽度及深度抓取
(一)、网页抓取原理

image.png


(二)、爬虫的抓取对象类型
1、静态网页
2、动态网页
3、Web Service
(三)、深度优先策略


image.png


 
(四)、宽度优先策略


image.png


 
(五)、选择哪种策略
1、重要的网页距离种子站点比较近
2、 万维网的深度并没有很,一个页多路径可以到达 
3、 宽度优先有利于多爬虫并行合作抓取
4、深度限制于宽度优先相结合
 
三、不重复抓取策略
(一)、如何记录抓取历史
1. 将访问过的URL保存到数据库效率太低
2. 用HashSet HashSet 将访问过的 URL 保存起来。那只需接近 O(1)的代价就可以查到一个 URL是否被访问过了。 消耗内存
3. URL 经过 MD5 或SHA -1等单向哈希后再保存到 HashSet 或数据库。
4. Bit-Map方法。建立一个 BitSet ,将每个 URL 经过一个哈希函数映射到某一位。
(二)、MD5函数
MD5 签名是一个哈希函数,可以将任意长度的据量转换为固定长度数字,(通常是 4个整型, 128 位)。计算机不可能有 2的128 那么大内存,因此实际的哈 希表都会是 URL.MD5再%n (即取模)。现实世界的URL组合必然超越哈希表的槽位数,因此碰撞是一定存在的,一般的Hash函数,例如Java的HashTable是一 个HASH 表再跟上一个链表,链表里存的是碰撞的结果。

image.png


(三)、提高效率
1、评估网站的页数量
2、选择合适的 HASH 算法和空间阈值, 降低碰撞几率
3、选择合适的存储结构和算法
(四)、评估网页数量
site:网址
(五)、BITMAP方式记录
将URL的MD5值再次哈希,用一个或多BIT位来记录一个URL :
1. 确定空间大小 e.g.  facebook 1.5Gb
2. 按倍增加槽位 e.g. 16GB
3. HASH算法映射 ( murmurhash3, cityhash)Python: mmh3 bitarray
 
pip install murmurhash3 bitarray


image.png



• 优势:对存储进行了进一步压缩,在 MD5 的基础上,可以从 128 位最多压缩到 1位,一般情况如果用 4bit或者 8bit表示一个 url ,也能压缩 32 或者 16 倍
• 缺陷 :碰撞概率增加
(六)、Bloom Filter 
Bloom Filter使用了多个哈希函数,而不是一个。 创建一个 m位BitSet,先将所有位初始化为 0,然后选择 k个不同的哈希函数。第 i个哈希函数对字符串str哈希的结果记为 h(i,str ),且 h(i,str )的范围是 0到m-1。
只能插入,不能删除!!


image.png


(七)、pybloomfilter
安装
pip install pybloomfilter(可能运行时会 crash crash )
git clone https://github.com/axiak/pybloomfiltermmap.git 
python setup.py install
构造函数
class pybloomfilter.BloomFilter(capacity : int, error_rate:float[,filename=None:String][, perm=0755 ])
并不实际检查容量,如果需要比较低的error_rate,则需要设置更大的容量


image.png



官方文档
https://media.readthedocs.org/pdf/pybloomfiltermmap3/latest/pybloomfiltermmap3.pdf 
 
(八)、如何有效记录抓取历史
1、多数情况下不需要压缩,尤其网页量少的
2、网页数量大的情况下,使用 Bloom Filter压缩
3、重点是计算碰撞概率,并根据来确定存储空间的阈值
4、分布式系统、将散列映射到多台主机的内存
 
四、网站结构分析
1、网站对爬虫的限制
2、利用sitemap来分析网站结构和估算目标网页的规模
3、有效率抓取特定内容
      利用sitemap里的信息,直接对网页.html进行抓取
4、对网站目录结构进行分析
大多数网站都会存在明确的 toptodown 的分类目录结构,我们可以进入特定行抓取
对于 www.mafengwo.cn这个网站,所有旅游的记都位于 www.mafengwo.cn/mdd下,按照城市进 行了分类,每个城市的游记位于首页。
城市的首页: /travel-scenic scenic-spot/mafengwo/10774 .html
游记的分页格式: /yj /10774/1-0-01 .html
游记的页面: /i/3523364.html 收起阅读 »

第七课 面向对象(下)和异常处理

       因为周六晚上带孩子上辅导班,这次课是周日补的,笔记也是补的,左边是示例,右边知识点;上了这么七次课,感觉学到了很多学校课堂学不到的东西,就是对待一个项目的设计思想,给老师点赞,给小象点赞。
IMG_1525(20180423-151023).jpg


IMG_1526(20180423-151030).jpg


IMG_1527(20180423-151041).jpg


IMG_1528(20180423-151047).jpg


IMG_1529(20180423-151055).jpg


IMG_1530(20180423-151101).jpg
继续阅读 »
       因为周六晚上带孩子上辅导班,这次课是周日补的,笔记也是补的,左边是示例,右边知识点;上了这么七次课,感觉学到了很多学校课堂学不到的东西,就是对待一个项目的设计思想,给老师点赞,给小象点赞。
IMG_1525(20180423-151023).jpg


IMG_1526(20180423-151030).jpg


IMG_1527(20180423-151041).jpg


IMG_1528(20180423-151047).jpg


IMG_1529(20180423-151055).jpg


IMG_1530(20180423-151101).jpg
收起阅读 »

第七课

链接:http://note.youdao.com/notesha ... 7266E
如有错误,请指出,谢谢!!
下面是脑图:
类.png


包和模块_(1).png


异常处理.png

 
继续阅读 »
链接:http://note.youdao.com/notesha ... 7266E
如有错误,请指出,谢谢!!
下面是脑图:
类.png


包和模块_(1).png


异常处理.png

  收起阅读 »

#奶茶作业#第四、第五次作业

根据要求完成后,添加了日志管理,以及奶茶推荐,奶茶推荐做的比较粗糙,是分析所有已经购买的客户后,根据最喜欢这个奶茶的所有客户第二喜欢的奶茶来进行推荐,感觉不用dataframe和SQL操作起来还是很费劲,谢谢。(源码太长了超限制了,放有道了)
 
'''
奶茶店
第四次作业修改
'''
 
import csv
import time
import os
 
 
#欢迎语
welcomeWord = '小象奶茶店馆售卖宇宙无敌奶茶,奶茶虽好可不要贪杯哦!每次限尝鲜一种口味:'
naichaWord = '1.原味奶茶 3元 2.香蕉奶茶 5元 3.草莓奶茶 5元 4.香芋奶茶 7元 5.珍珠冰奶茶 7元'
sorryWord1 = 'Woops!我们只售卖以上'
sorryWord2 = '种奶茶口味哦!新口味敬请期待!'
naicha = {'1':'原味奶茶','2':'香蕉奶茶','3':'草莓奶茶','4':'香芋奶茶','5':'珍珠冰奶茶'}
naichaCost = {'1':3,'2':5,'3':5,'4':7,'5':7}
f_log_user='user_log.csv'
f_info_user='infoUsers.csv'
f_log_recome = 'log_recome.csv'
f_like_user='like_users.csv'
#print(len(naicha))
 
#设置会员 会员文件名称为 infoUsers.csv
members =
membersLog =
mphone = {}
 
class Users:
id=None
phone=None
birthday=None
sex=None
constellation=None
adress=None
with open(f_info_user,'a',encoding='utf-8') as info_file:
pass
#log=None 思考后觉得奶茶日志放在users类里以后不好维护
 
#判断是否为存在用户
def is_user(self,id):
with open('infoUsers.csv','r',encoding='utf-8') as info_file:
reader = csv.reader(info_file)
for row in reader:
if row[0]==id:
return True
return False
#获取用户文件行数
def get_infoUser_line(self):
with open(f_info_user,'r',encoding='utf-8') as info_files:
reader=csv.reader(info_files)
i = 0
for row in reader:
i += 1
print(i)
return i
#写入属性
def put_info(self,id,phone,birthday,sex,constellation,adress):
self.id=id
self.phone=phone
self.birthday=birthday
self.sex=sex
self.constellation=constellation
self.adress=adress
#将属性写入文件
def write_info(self):
header = ['id', 'phone', 'birthday', 'sex', 'constellation', 'adress']
with open (f_info_user, 'a', newline='', encoding='utf-8') as info_file:
writer = csv.DictWriter (info_file, header)
#writer.writeheader ()
writer.writerow (
{'id': self.id, 'phone': self.phone, 'birthday': self.birthday, 'sex': self.sex, 'constellation': self.constellation,
'adress': self.adress})
#读取use对象数值
def get_info(self):
return {'id':self.id,'phone':self.phone,'birthday':self.birthday,'sex':self.sex,'constellation':self.constellation,'adress':self.adress}
# def get_naichalog(self,):
# pass
 
#日志类,记录正常日志和每个奶茶的的购买情况
#daytime->日期,daylog->列表,就是每次的购买情况
class NaichaLog:
#判断日志名称
strtime=time.strftime('%Y%m%d',time.localtime())
log_file_name='naicha_log_'+strtime+'.csv'
#用户日志
user_log_name=f_log_user
#判断日志是否存在后,创建日志
if os.path.exists(log_file_name):
pass
else:
header=['id','time']
for key in naicha.keys():
header.append(key)
with open(log_file_name,'w',encoding='utf-8') as log_file:
writer=csv.writer(log_file)
writer.writerow(header)
if os.path.exists(user_log_name):
pass
else:
header=['id']
for key in naicha.keys():
header.append(key)
with open (log_file_name, 'w', encoding='utf-8') as log_file:
writer = csv.writer (log_file)
writer.writerow(header)
 
#print(log_file_name)
#写入每日日志
def writeDayLog(self, userid, daylog):
with open(self.log_file_name,'a',encoding='utf-8') as log_file:
writer=csv.writer(log_file)
naichaList=[userid,time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())]
for key in naicha.keys():
naichaList.append(daylog.setdefault(key,0))
writer.writerow(naichaList)
#写入用户购买日志
def writeUserLog(self,userid,naichalog):
with open(self.user_log_name,'a',encoding='utf-8') as log_file:
#reader=csv.reader(log_file)
writer=csv.writer(log_file)
newrow=[userid]
for key in naicha.keys():
newrow.append(naichalog[key])
print(newrow)
writer.writerow(newrow)
#newrow2 = [userid]
# for key in naicha.keys ():
# newrow2.append(naichalog.setdefault(key, 0))
# writer.writerow(newrow2)
 
#读取每日日志
def readDayLog(self,daytime):
pass
#读取用户购买日志
def readUserLog(self,userid):
pass
 
#推荐函数,在顾客购买之前推荐顾客经常买的奶茶,在顾客购买之后,推荐口味相似的客户购买的奶茶
#每日购物流水为 daylog_2018XXXX.csv
#顾客购买奶茶具体数据文件名称 logUsers.csv
#在关店后调用,根据后台信息计算口味相似顾客可能购买的1种奶茶
#先找出用户购买数量最多和第二多的奶茶,然后将每种奶茶买的最多的用户进行汇总,找出除了他们买的占比数量最多的第二多的奶茶,并再下次购买时推荐给客户
#将数据库中的每种奶茶购买最多的用户数据取出,将数据中除了此种奶茶的其他奶茶的按照一个客户一分计分,
# 计分后,分值除以用户总数,得出一个百分数,取出前两位数值,若数值高于80%,则给予用户推荐。
#计算顾客最喜欢的奶茶
def getUsersFisrstNaicha():
print('开始计算推荐...')
 
#writefilename = time.strftime ('log_recome_%Y%m%d.csv', time.localtime ())
 
writefile = open (f_log_recome, 'w', encoding='utf-8')
writer = csv.writer (writefile)
infofile = open (f_info_user, 'r', encoding='utf-8')
inreader = csv.reader (infofile)
recomelist =
 
for row in inreader:
new = [row[0], 0, 0, 0, 0, 0]
logfile = open (f_log_user, 'r', encoding='utf-8')
freader = csv.reader (logfile)
for row2 in freader:
if row2[0] == row[0]:
i = 1
while i < len (new):
new[i] = new[i] + int(row2[i])
i+=1
new2=new[1:len(new)]
i=0
j=0
while i < len(new2):
if max(new2)==new2[i]:
maxitem=i+1
new3=new2
new3.remove(new2[i])
while j < len(new2):
if max(new3)==new2[j] and j!=i :
maxitem2=j+1
break
j+=1
break
i+=1
#new2.sort()
# print(new2)
new.append(str(maxitem))
new.append(str(maxitem2))
recomelist.append(new)
logfile.close()
writer.writerows (recomelist)
writefile.close()
infofile.close()
print('计算完毕')
 
 
def showUsersRecomNaicha():
allRecome={}
for key in naicha.keys():
showUserLike = {'1':0,'2':0,'3':0,'4':0,'5':0}#.................妥协= =
logfile = open (f_log_recome, 'r', encoding='utf-8')
reader = csv.reader (logfile)
for row in reader:
if row[len(naicha)+1]==key :
showUserLike[row[len(naicha)+2]]+=1
maxkeyTuple=max(zip(showUserLike.values(),showUserLike.keys()))
maxkey=maxkeyTuple[1]
allRecome[key]=maxkey
logfile.close
with open(f_like_user,'w',encoding='utf-8') as likefile:
writer=csv.DictWriter(likefile,naicha.keys())
writer.writeheader()
writer.writerow(allRecome)
 
def getUserMostBuy(userid):
with open(f_log_recome,'r') as logfile:
reader=csv.reader(logfile)
for row in reader:
if row[0]==userid:
return row[len(naicha)+1]
 
#给客户推荐其他口味的奶茶
def getUsersRecomNaicha(key):
with open(f_like_user,'r') as likefile:
reader=csv.reader(likefile)
header=next(reader)
value=next(reader)
return value[int(key)-1]
 
#购物信息
costPerson =
costAllLog =
 
#设置固定客户数
costerNo = 2
fmemberNo = 37000
 
#设置购物函数
def shopping(member):
buyNo = input ('请输入购买序号:')
 
if buyNo not in naicha:
print (sorryWord1 + '{}'.format (len (naicha.keys ())) + sorryWord2)
else:
buyNumber = int (input ('请输入购买数量:'))
return [member,buyNo,buyNumber]
 
#设置输出购物结果函数
def shoppingPrint(member,shRecord,isnew):
allCost=0
if isnew:
sale=1
else:
sale=0.9
for key in shRecord.keys():
#print(shRecord[key])
#print(type(shRecord[key]))
#print(naichaCost)
allCost=naichaCost[key]*shRecord[key]
allCost*=sale
print('您本次购物购买了:')
for key in shRecord.keys():
print(' {} {}件'.format(naicha[key],shRecord[key]))
print('本次您总共消费了{}元,欢迎您的下次光临!'.format(allCost))
 
#设置购物详情记录函数
def shoppingLog(member,shRecord):
log=
log.append(member)
log.append(shRecord)
return log
 
 
#主程序开始
 
print(welcomeWord)
print(naichaWord)
 
while costerNo>0:
isnew=True
user =Users()
logs=NaichaLog()
 
now = input('\n 请输入会员号(没有会员号请输入 N):')
if now=='N' :
now = str(fmemberNo+user.get_infoUser_line())
print('您的会员号是:{}'.format(now))
phone = input('请输入您的手机号:')
user.put_info(now,phone,'','','','')
user.write_info()
else:
#print(members)
if user.is_user(now):
isnew = False
print ('Welcome {}'.format (now))
else:
isExist = input ('系统里没有此会员号,重新输入请输 Y ,系统分配请输 任意值: ')
if now == 'Y':
now = input ('请输入重新输入会员号:')
else:
now = str (fmemberNo + user.get_infoUser_line ())
print ('您的会员号是:{}'.format (now))
phone = input ('请输入您的手机号:')
user.put_info (now, phone, '', '', '', '')
user.write_info ()
# print(naicha.keys())
 
 
#开始购物
isQuit='i'
# 输出推荐购买
mostbuy = getUserMostBuy (now)
print ('小象记得您最喜欢的奶茶是 {} 哦~也推荐您尝试 {} ,偶尔换换新口味~'.format (naicha[mostbuy], naicha[getUsersRecomNaicha (mostbuy)]))
 
shoppingPersonRecord={}
while isQuit !='q':
naichaNow=shopping(now)
if naichaNow[1] in shoppingPersonRecord:
shoppingPersonRecord[naichaNow[1]]+=int(naichaNow[2])
else:
shoppingPersonRecord[naichaNow[1]]=int(naichaNow[2])
isQuit=input('是否继续购物?(继续 c/退出 q)')
 
#print(shoppingPersonRecord)
 
#输出购买
shoppingPrint(now,shoppingPersonRecord,isnew)
logs.writeDayLog(now,shoppingPersonRecord)
logs.writeUserLog(now,shoppingPersonRecord)
 
costPerson=(shoppingLog(now,shoppingPersonRecord))
costAllLog.append(costPerson)
costerNo -= 1
 
print('今日已闭店,欢迎您明天光临!')
#重新计算推荐算法,并生成日志
getUsersFisrstNaicha()
showUsersRecomNaicha()
#结账后输出交易明细
isMaster =input('进入贤者模式,请输入密码:')
while isMaster!='whoisyourdady':
isMaster=input('输入密码错误请重新输入:')
print(costAllLog)
 
 
 
继续阅读 »
根据要求完成后,添加了日志管理,以及奶茶推荐,奶茶推荐做的比较粗糙,是分析所有已经购买的客户后,根据最喜欢这个奶茶的所有客户第二喜欢的奶茶来进行推荐,感觉不用dataframe和SQL操作起来还是很费劲,谢谢。(源码太长了超限制了,放有道了)
 
'''
奶茶店
第四次作业修改
'''
 
import csv
import time
import os
 
 
#欢迎语
welcomeWord = '小象奶茶店馆售卖宇宙无敌奶茶,奶茶虽好可不要贪杯哦!每次限尝鲜一种口味:'
naichaWord = '1.原味奶茶 3元 2.香蕉奶茶 5元 3.草莓奶茶 5元 4.香芋奶茶 7元 5.珍珠冰奶茶 7元'
sorryWord1 = 'Woops!我们只售卖以上'
sorryWord2 = '种奶茶口味哦!新口味敬请期待!'
naicha = {'1':'原味奶茶','2':'香蕉奶茶','3':'草莓奶茶','4':'香芋奶茶','5':'珍珠冰奶茶'}
naichaCost = {'1':3,'2':5,'3':5,'4':7,'5':7}
f_log_user='user_log.csv'
f_info_user='infoUsers.csv'
f_log_recome = 'log_recome.csv'
f_like_user='like_users.csv'
#print(len(naicha))
 
#设置会员 会员文件名称为 infoUsers.csv
members =
membersLog =
mphone = {}
 
class Users:
id=None
phone=None
birthday=None
sex=None
constellation=None
adress=None
with open(f_info_user,'a',encoding='utf-8') as info_file:
pass
#log=None 思考后觉得奶茶日志放在users类里以后不好维护
 
#判断是否为存在用户
def is_user(self,id):
with open('infoUsers.csv','r',encoding='utf-8') as info_file:
reader = csv.reader(info_file)
for row in reader:
if row[0]==id:
return True
return False
#获取用户文件行数
def get_infoUser_line(self):
with open(f_info_user,'r',encoding='utf-8') as info_files:
reader=csv.reader(info_files)
i = 0
for row in reader:
i += 1
print(i)
return i
#写入属性
def put_info(self,id,phone,birthday,sex,constellation,adress):
self.id=id
self.phone=phone
self.birthday=birthday
self.sex=sex
self.constellation=constellation
self.adress=adress
#将属性写入文件
def write_info(self):
header = ['id', 'phone', 'birthday', 'sex', 'constellation', 'adress']
with open (f_info_user, 'a', newline='', encoding='utf-8') as info_file:
writer = csv.DictWriter (info_file, header)
#writer.writeheader ()
writer.writerow (
{'id': self.id, 'phone': self.phone, 'birthday': self.birthday, 'sex': self.sex, 'constellation': self.constellation,
'adress': self.adress})
#读取use对象数值
def get_info(self):
return {'id':self.id,'phone':self.phone,'birthday':self.birthday,'sex':self.sex,'constellation':self.constellation,'adress':self.adress}
# def get_naichalog(self,):
# pass
 
#日志类,记录正常日志和每个奶茶的的购买情况
#daytime->日期,daylog->列表,就是每次的购买情况
class NaichaLog:
#判断日志名称
strtime=time.strftime('%Y%m%d',time.localtime())
log_file_name='naicha_log_'+strtime+'.csv'
#用户日志
user_log_name=f_log_user
#判断日志是否存在后,创建日志
if os.path.exists(log_file_name):
pass
else:
header=['id','time']
for key in naicha.keys():
header.append(key)
with open(log_file_name,'w',encoding='utf-8') as log_file:
writer=csv.writer(log_file)
writer.writerow(header)
if os.path.exists(user_log_name):
pass
else:
header=['id']
for key in naicha.keys():
header.append(key)
with open (log_file_name, 'w', encoding='utf-8') as log_file:
writer = csv.writer (log_file)
writer.writerow(header)
 
#print(log_file_name)
#写入每日日志
def writeDayLog(self, userid, daylog):
with open(self.log_file_name,'a',encoding='utf-8') as log_file:
writer=csv.writer(log_file)
naichaList=[userid,time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())]
for key in naicha.keys():
naichaList.append(daylog.setdefault(key,0))
writer.writerow(naichaList)
#写入用户购买日志
def writeUserLog(self,userid,naichalog):
with open(self.user_log_name,'a',encoding='utf-8') as log_file:
#reader=csv.reader(log_file)
writer=csv.writer(log_file)
newrow=[userid]
for key in naicha.keys():
newrow.append(naichalog[key])
print(newrow)
writer.writerow(newrow)
#newrow2 = [userid]
# for key in naicha.keys ():
# newrow2.append(naichalog.setdefault(key, 0))
# writer.writerow(newrow2)
 
#读取每日日志
def readDayLog(self,daytime):
pass
#读取用户购买日志
def readUserLog(self,userid):
pass
 
#推荐函数,在顾客购买之前推荐顾客经常买的奶茶,在顾客购买之后,推荐口味相似的客户购买的奶茶
#每日购物流水为 daylog_2018XXXX.csv
#顾客购买奶茶具体数据文件名称 logUsers.csv
#在关店后调用,根据后台信息计算口味相似顾客可能购买的1种奶茶
#先找出用户购买数量最多和第二多的奶茶,然后将每种奶茶买的最多的用户进行汇总,找出除了他们买的占比数量最多的第二多的奶茶,并再下次购买时推荐给客户
#将数据库中的每种奶茶购买最多的用户数据取出,将数据中除了此种奶茶的其他奶茶的按照一个客户一分计分,
# 计分后,分值除以用户总数,得出一个百分数,取出前两位数值,若数值高于80%,则给予用户推荐。
#计算顾客最喜欢的奶茶
def getUsersFisrstNaicha():
print('开始计算推荐...')
 
#writefilename = time.strftime ('log_recome_%Y%m%d.csv', time.localtime ())
 
writefile = open (f_log_recome, 'w', encoding='utf-8')
writer = csv.writer (writefile)
infofile = open (f_info_user, 'r', encoding='utf-8')
inreader = csv.reader (infofile)
recomelist =
 
for row in inreader:
new = [row[0], 0, 0, 0, 0, 0]
logfile = open (f_log_user, 'r', encoding='utf-8')
freader = csv.reader (logfile)
for row2 in freader:
if row2[0] == row[0]:
i = 1
while i < len (new):
new[i] = new[i] + int(row2[i])
i+=1
new2=new[1:len(new)]
i=0
j=0
while i < len(new2):
if max(new2)==new2[i]:
maxitem=i+1
new3=new2
new3.remove(new2[i])
while j < len(new2):
if max(new3)==new2[j] and j!=i :
maxitem2=j+1
break
j+=1
break
i+=1
#new2.sort()
# print(new2)
new.append(str(maxitem))
new.append(str(maxitem2))
recomelist.append(new)
logfile.close()
writer.writerows (recomelist)
writefile.close()
infofile.close()
print('计算完毕')
 
 
def showUsersRecomNaicha():
allRecome={}
for key in naicha.keys():
showUserLike = {'1':0,'2':0,'3':0,'4':0,'5':0}#.................妥协= =
logfile = open (f_log_recome, 'r', encoding='utf-8')
reader = csv.reader (logfile)
for row in reader:
if row[len(naicha)+1]==key :
showUserLike[row[len(naicha)+2]]+=1
maxkeyTuple=max(zip(showUserLike.values(),showUserLike.keys()))
maxkey=maxkeyTuple[1]
allRecome[key]=maxkey
logfile.close
with open(f_like_user,'w',encoding='utf-8') as likefile:
writer=csv.DictWriter(likefile,naicha.keys())
writer.writeheader()
writer.writerow(allRecome)
 
def getUserMostBuy(userid):
with open(f_log_recome,'r') as logfile:
reader=csv.reader(logfile)
for row in reader:
if row[0]==userid:
return row[len(naicha)+1]
 
#给客户推荐其他口味的奶茶
def getUsersRecomNaicha(key):
with open(f_like_user,'r') as likefile:
reader=csv.reader(likefile)
header=next(reader)
value=next(reader)
return value[int(key)-1]
 
#购物信息
costPerson =
costAllLog =
 
#设置固定客户数
costerNo = 2
fmemberNo = 37000
 
#设置购物函数
def shopping(member):
buyNo = input ('请输入购买序号:')
 
if buyNo not in naicha:
print (sorryWord1 + '{}'.format (len (naicha.keys ())) + sorryWord2)
else:
buyNumber = int (input ('请输入购买数量:'))
return [member,buyNo,buyNumber]
 
#设置输出购物结果函数
def shoppingPrint(member,shRecord,isnew):
allCost=0
if isnew:
sale=1
else:
sale=0.9
for key in shRecord.keys():
#print(shRecord[key])
#print(type(shRecord[key]))
#print(naichaCost)
allCost=naichaCost[key]*shRecord[key]
allCost*=sale
print('您本次购物购买了:')
for key in shRecord.keys():
print(' {} {}件'.format(naicha[key],shRecord[key]))
print('本次您总共消费了{}元,欢迎您的下次光临!'.format(allCost))
 
#设置购物详情记录函数
def shoppingLog(member,shRecord):
log=
log.append(member)
log.append(shRecord)
return log
 
 
#主程序开始
 
print(welcomeWord)
print(naichaWord)
 
while costerNo>0:
isnew=True
user =Users()
logs=NaichaLog()
 
now = input('\n 请输入会员号(没有会员号请输入 N):')
if now=='N' :
now = str(fmemberNo+user.get_infoUser_line())
print('您的会员号是:{}'.format(now))
phone = input('请输入您的手机号:')
user.put_info(now,phone,'','','','')
user.write_info()
else:
#print(members)
if user.is_user(now):
isnew = False
print ('Welcome {}'.format (now))
else:
isExist = input ('系统里没有此会员号,重新输入请输 Y ,系统分配请输 任意值: ')
if now == 'Y':
now = input ('请输入重新输入会员号:')
else:
now = str (fmemberNo + user.get_infoUser_line ())
print ('您的会员号是:{}'.format (now))
phone = input ('请输入您的手机号:')
user.put_info (now, phone, '', '', '', '')
user.write_info ()
# print(naicha.keys())
 
 
#开始购物
isQuit='i'
# 输出推荐购买
mostbuy = getUserMostBuy (now)
print ('小象记得您最喜欢的奶茶是 {} 哦~也推荐您尝试 {} ,偶尔换换新口味~'.format (naicha[mostbuy], naicha[getUsersRecomNaicha (mostbuy)]))
 
shoppingPersonRecord={}
while isQuit !='q':
naichaNow=shopping(now)
if naichaNow[1] in shoppingPersonRecord:
shoppingPersonRecord[naichaNow[1]]+=int(naichaNow[2])
else:
shoppingPersonRecord[naichaNow[1]]=int(naichaNow[2])
isQuit=input('是否继续购物?(继续 c/退出 q)')
 
#print(shoppingPersonRecord)
 
#输出购买
shoppingPrint(now,shoppingPersonRecord,isnew)
logs.writeDayLog(now,shoppingPersonRecord)
logs.writeUserLog(now,shoppingPersonRecord)
 
costPerson=(shoppingLog(now,shoppingPersonRecord))
costAllLog.append(costPerson)
costerNo -= 1
 
print('今日已闭店,欢迎您明天光临!')
#重新计算推荐算法,并生成日志
getUsersFisrstNaicha()
showUsersRecomNaicha()
#结账后输出交易明细
isMaster =input('进入贤者模式,请输入密码:')
while isMaster!='whoisyourdady':
isMaster=input('输入密码错误请重新输入:')
print(costAllLog)
 
 
  收起阅读 »

第六课(类)

以下是链接:http://note.youdao.com/notesha ... 94C2D
如果错误,请指出,谢谢!
 
下面是脑图
类.png

 
继续阅读 »
以下是链接:http://note.youdao.com/notesha ... 94C2D
如果错误,请指出,谢谢!
 
下面是脑图
类.png

  收起阅读 »

第六课 - 面向对象编程

# 面向对象编程
**面向对象编程**,简称OOP:是一种解决软件服用的设计和编程方法。这种方法把软件系统中相似的逻辑操作、数据、状态等以类的形式描述出来,通过对象实例在软件系统中复用,从而提高软件开发效率。

# 类和对象
**定义和关系**
**类**:一个事物的抽象,定义了一类事物的属性和行为。类具有继承,多态,封装等特性。
```
class 类名: #类命名规则遵守“大驼峰”
#默认要传入一个self参数,第一个参数必须是sel
def 方法名(self [, 参数列表]):
```
**对象**:通过类创建的一个具体事物,也就是类的实例,它具有状态和行为,可以做具体的事情。
```
对象变量名 = 类名()
```
**两者关系**:类相当于创建对象的模板,根据类可以创建一个或者多个对象实例。

# 类的方法
**类的构造方法**
_ _init_ _构造方法:用于对象创建时初始化
调用时间:在对象被实例化时被程序自动调用
程序不显示定义init方法,则程序默认调用一个无参init方法
对象创建过程示意图:
![](http://wenda.chinahadoop.cn/up ... 07.png)

**类的私有方法**
只能在类内部调用,在类的外部无法调用
私有方法:在方法名前添加两个下划线,私有属性如是
类内部调用私有方法要使用**self.private_method()**的方式调用
```
class Comrade:
#私有方法
def __send_message(self):
print("消息已经向上级汇报")

def answer_secret(self, secret):
if secret == "芝麻开门":
print("接头成功!")
self.__send_message()#调用私有方法
else:
print("接头失败!")
comrade = Comrade()
comrade.answer_secret("芝麻开门")
```
**类的访问权限**
```
#设置对象属性
def __init__(self, gender, variety, name):
self.gender = gender
self.variety = variety
self.name = name
```
修改对象属性有两种形式:
* 直接对属性值进行赋值
* 通过方法对属性值进行修改。这是推荐方式。可以达到对数据进行封装。


# 类的特性一:继承和多重继承
**继承**
定义:在程序中,子类从父类继承,但是子类又可以提供除父类外自身独有的方法和属性。比如猫和狗都是动物,动物如果是父类的话,那么猫和狗就是动物的子类。父类通常是抽象出带有一些共性的特征和方法的类。
```
#父类Animal
class Animal:
def eat():
print("eat")
#Dog
class Dog(Animal):
def shout()
print("汪汪汪")
wangcai = Dog()
#调用父类的eat
wangcai.eat()
#调用自身成员方法
wangcai.shout()
```
**继承注意事项**
* 子类的非有属性、方法,会被子类继承
* 子类中方法的查找:先子类方法 =》后父类方法
* 子类可以继承祖先类的非私有属性和方法
* 在子类中调用父类的方法:ClassName.Method(self)

**多重继承**
定义:一个类可以从多个父类继承。
多继承的定义方式:
```python
class AI:
#人脸识别
def face_recongnition(self):
print("人脸识别")
def data_handle(self):
print("AI数据处理")

class BigData:
def data_analysis(self):
print("数据分析")
def data_handle(self):
print("BigData数据处理")

class Python(BigData, AI): #多重继承,Python从BigData和AI两个类继承
def operation(self):
print("自动化运维")
```
对于多重继承,子类调用查找同名方法的顺序:
* 取决于小括号内继承父类从左到右的顺序查找。
* 可以通过类的方法 _ _mro_ _来查看查找顺序。

# 类的特性二:多态
定义:一个抽象类有多个子类,不同的类表现出不同的形态。比如狗和猫,他们都会叫,但是叫的声音是不同的。
子类对父类允许访问的方法的实现过程进行重新编写
在子类中定义与父类同名的方法。子类可以根据需要,定义合适的方法实现逻辑。

# 综合例子: 继承和多态
```
class Animal:
def __init__(self):
print("---animal构造方法---")

def __private_method(self):
print("私有方法")
def eat(self):
print("----吃----")
def drink(self):
print("----喝----")
def run(self):
print("----跑----")

class Dog(Animal):
def __init__(self):
print("dog构造方法")
#父类方法重写
def run(self): #多态
print("摇着尾巴跑")
def hand(self):
Animal.run(self) #在子类中调用父类
print("------握手-----")

class GoldenDog(Dog):
def guid(self):
print("我能导航!")
```
继续阅读 »
# 面向对象编程
**面向对象编程**,简称OOP:是一种解决软件服用的设计和编程方法。这种方法把软件系统中相似的逻辑操作、数据、状态等以类的形式描述出来,通过对象实例在软件系统中复用,从而提高软件开发效率。

# 类和对象
**定义和关系**
**类**:一个事物的抽象,定义了一类事物的属性和行为。类具有继承,多态,封装等特性。
```
class 类名: #类命名规则遵守“大驼峰”
#默认要传入一个self参数,第一个参数必须是sel
def 方法名(self [, 参数列表]):
```
**对象**:通过类创建的一个具体事物,也就是类的实例,它具有状态和行为,可以做具体的事情。
```
对象变量名 = 类名()
```
**两者关系**:类相当于创建对象的模板,根据类可以创建一个或者多个对象实例。

# 类的方法
**类的构造方法**
_ _init_ _构造方法:用于对象创建时初始化
调用时间:在对象被实例化时被程序自动调用
程序不显示定义init方法,则程序默认调用一个无参init方法
对象创建过程示意图:
![](http://wenda.chinahadoop.cn/up ... 07.png)

**类的私有方法**
只能在类内部调用,在类的外部无法调用
私有方法:在方法名前添加两个下划线,私有属性如是
类内部调用私有方法要使用**self.private_method()**的方式调用
```
class Comrade:
#私有方法
def __send_message(self):
print("消息已经向上级汇报")

def answer_secret(self, secret):
if secret == "芝麻开门":
print("接头成功!")
self.__send_message()#调用私有方法
else:
print("接头失败!")
comrade = Comrade()
comrade.answer_secret("芝麻开门")
```
**类的访问权限**
```
#设置对象属性
def __init__(self, gender, variety, name):
self.gender = gender
self.variety = variety
self.name = name
```
修改对象属性有两种形式:
* 直接对属性值进行赋值
* 通过方法对属性值进行修改。这是推荐方式。可以达到对数据进行封装。


# 类的特性一:继承和多重继承
**继承**
定义:在程序中,子类从父类继承,但是子类又可以提供除父类外自身独有的方法和属性。比如猫和狗都是动物,动物如果是父类的话,那么猫和狗就是动物的子类。父类通常是抽象出带有一些共性的特征和方法的类。
```
#父类Animal
class Animal:
def eat():
print("eat")
#Dog
class Dog(Animal):
def shout()
print("汪汪汪")
wangcai = Dog()
#调用父类的eat
wangcai.eat()
#调用自身成员方法
wangcai.shout()
```
**继承注意事项**
* 子类的非有属性、方法,会被子类继承
* 子类中方法的查找:先子类方法 =》后父类方法
* 子类可以继承祖先类的非私有属性和方法
* 在子类中调用父类的方法:ClassName.Method(self)

**多重继承**
定义:一个类可以从多个父类继承。
多继承的定义方式:
```python
class AI:
#人脸识别
def face_recongnition(self):
print("人脸识别")
def data_handle(self):
print("AI数据处理")

class BigData:
def data_analysis(self):
print("数据分析")
def data_handle(self):
print("BigData数据处理")

class Python(BigData, AI): #多重继承,Python从BigData和AI两个类继承
def operation(self):
print("自动化运维")
```
对于多重继承,子类调用查找同名方法的顺序:
* 取决于小括号内继承父类从左到右的顺序查找。
* 可以通过类的方法 _ _mro_ _来查看查找顺序。

# 类的特性二:多态
定义:一个抽象类有多个子类,不同的类表现出不同的形态。比如狗和猫,他们都会叫,但是叫的声音是不同的。
子类对父类允许访问的方法的实现过程进行重新编写
在子类中定义与父类同名的方法。子类可以根据需要,定义合适的方法实现逻辑。

# 综合例子: 继承和多态
```
class Animal:
def __init__(self):
print("---animal构造方法---")

def __private_method(self):
print("私有方法")
def eat(self):
print("----吃----")
def drink(self):
print("----喝----")
def run(self):
print("----跑----")

class Dog(Animal):
def __init__(self):
print("dog构造方法")
#父类方法重写
def run(self): #多态
print("摇着尾巴跑")
def hand(self):
Animal.run(self) #在子类中调用父类
print("------握手-----")

class GoldenDog(Dog):
def guid(self):
print("我能导航!")
``` 收起阅读 »

第六课 面向对象(中)

这次课程信息量巨大,知识点不多,个个都是精华。依旧左边示例,右边知识点。

IMG_1480(20180418-212850).jpg


IMG_1481(20180418-212857).jpg


IMG_1482(20180418-212904).jpg


IMG_1483(20180418-212911).jpg

 
继续阅读 »
这次课程信息量巨大,知识点不多,个个都是精华。依旧左边示例,右边知识点。

IMG_1480(20180418-212850).jpg


IMG_1481(20180418-212857).jpg


IMG_1482(20180418-212904).jpg


IMG_1483(20180418-212911).jpg

  收起阅读 »

第一章节:基础语法 课后作业

lis="""
***********************************************
    请输入您想购买的奶茶:
        1:原味冰奶茶  3元
        2:香蕉冰奶茶  5元
        3:草莓病奶茶  4元
        4:香草冰奶茶  7元
        5:珍珠冰奶茶  7元
***********************************************
    您是否是我店会员?会员享有9折优惠!
        T:是会员。
        F:不是会员。
        注:不区分大小写!
***********************************************
"""
dic = {'1':['原味冰奶茶',3],'2':['香蕉冰奶茶',5],'3':['草莓病奶茶',4],'4':['香草冰奶茶',7],'5':['珍珠冰奶茶',7]}
ty = ('1','2','3','4','5')
huiyuan = {'y':['是',0.9],'n':['否',1.0]}


name = input(lis)
if name in ty:
    num = int(input('请输入您想购买的数量:'))
    hy = input('会员?').lower()
    if hy=='y':
        zjia = dic[name][1]*num*0.9
    else:
        zjia = dic[name][1]*num
    print("*"*100)
    print('%5s %8s s s s %21s'%('类别','单价','数量','会员','折扣','总价'))
    print()
    print('%s %5s d s %21.1f %25.2f'%(dic[name][0],dic[name][1],num,huiyuan[hy][0],huiyuan[hy][1],zjia))
    print("*"*100)
else:
    print('Woops!我们只售卖以上五种奶茶哦,新口味敬请期待!')
继续阅读 »
lis="""
***********************************************
    请输入您想购买的奶茶:
        1:原味冰奶茶  3元
        2:香蕉冰奶茶  5元
        3:草莓病奶茶  4元
        4:香草冰奶茶  7元
        5:珍珠冰奶茶  7元
***********************************************
    您是否是我店会员?会员享有9折优惠!
        T:是会员。
        F:不是会员。
        注:不区分大小写!
***********************************************
"""
dic = {'1':['原味冰奶茶',3],'2':['香蕉冰奶茶',5],'3':['草莓病奶茶',4],'4':['香草冰奶茶',7],'5':['珍珠冰奶茶',7]}
ty = ('1','2','3','4','5')
huiyuan = {'y':['是',0.9],'n':['否',1.0]}


name = input(lis)
if name in ty:
    num = int(input('请输入您想购买的数量:'))
    hy = input('会员?').lower()
    if hy=='y':
        zjia = dic[name][1]*num*0.9
    else:
        zjia = dic[name][1]*num
    print("*"*100)
    print('%5s %8s s s s %21s'%('类别','单价','数量','会员','折扣','总价'))
    print()
    print('%s %5s d s %21.1f %25.2f'%(dic[name][0],dic[name][1],num,huiyuan[hy][0],huiyuan[hy][1],zjia))
    print("*"*100)
else:
    print('Woops!我们只售卖以上五种奶茶哦,新口味敬请期待!')
收起阅读 »