python基础入门IO文件读写操作与邮件发送
承接文章:针对萌新思路分享:基于urllib.request的威客平台订单发现推送爬虫(一)之前的第一篇因为刚注册成功不能立刻发帖,我把内容保存进word文档里,但是出现了富文本编辑器的格式失效问题,之后我会重新编辑一下那个帖子。
主要是想分享一下思路,之前我们用爬虫获取了威客网站最新的订单信息,现在这篇分享主要是简单介绍一下如何使用IO操作文件的读写来判断当前获得的需求是否为最新需求,同时使用邮件及时推送消息。
上回的代码是:
from lxml import etree
import urllib.request
import re
#获取HTML代码的方法
#传入参数url是网址字符串
#返回参数是结果字符串
def getHtmlCode(url):
#创建对象
req_one = urllib.request.Request(url)
#header信息
req_one.add_header('User-Agent', 'Mozilla/6.0')
#加入 try catch模块防止request报错导致结束程序
try:
#注意!timeout参数如果设置了,有可能出现XX秒内没有执行完毕报错
res_one = urllib.request.urlopen(req_one,timeout=1)
#内容重编码后复制给变量
htmlcode = res_one.read().decode('utf-8')
#关闭urlopen方法
res_one.close()
#把结果返回
return htmlcode
except:
print("报错IP问题无法获得")
return "error"
#通过XPath获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByXPath(code):
#构建eTree
html = etree.HTML(code)
#通过XPath获取标题所在<a>标签的属性
name = html.xpath('/html/body/div/div/div/div/div/div/h3/a')
#通过XPath获取预算所在标签的属性
price = html.xpath('/html/body/div/div/div/div/div/div/h3/b')
print('通过XPath获取的最新的需求是:')
#一个html标签之间的文本信息使用.text获取
print(name.text.replace(' ','').replace(' ',''))
#a 标签的href属性也就是链接属性可以使用get('属性名')来获取
print(name.get('href'))
print(name.get('indus_name'))
print(price.text+'\r\n\r\n\r\n')
#通过字符串获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByKeyWord(code):
startTarget = '<div class="task_class_list_li_box">'
#先找到列表所在的DIV层,使用index方法
taskStartIndex = code.index(startTarget)+len(startTarget)
taskEndIndex = code.index(startTarget,taskStartIndex,len(code))
untreatedInfo = code
taskUrl = re.search('https://task.epwk.com/\d*/', untreatedInfo) .group(0)
taskTitle = re.search('title=".*?"',untreatedInfo).group(0).replace('title=','').replace('"','')
print('通过字符串获取的最新的需求是:')
print(taskUrl)
print(taskTitle)
if __name__ == '__main__':
htmlCode = getHtmlCode("https://task.epwk.com/sj/?o=7")
getInfoByXPath(htmlCode)
getInfoByKeyWord(htmlCode)
执行的结果是:
我们已经获得了目前最新的一个需求的各种信息,现在需要判断这个信息是不是最新的,请允许我使用一种比较麻烦的方式来为大家介绍简单的OS文件读写操作。
先简单介绍一下:
python这边我目前知道种操作文件的方法,一种是简单的利用open()、close()、write() 方法来实现io操作。但是这样对于新手来说比较麻烦,还有很多类似使用完必须close()关闭,不然容易引起IOError的细节。
对此,python专门引入了一个“with”机制来处理文件的IO操作
比如:
with open('设计最新需求.txt','r',encoding='utf-8') as fileread:
old = fileread.read()
在这里 open后边的括号就包含了全部的操作所需要声明的东西。
第一个'设计最新需求.txt',是文件名,我只简单写了文件名,也可以给它前边添加具体的盘符路径,不管是Linux还是Windows只要给它正确的路径,就一定可以找到相应的文件。
顺便一提,如果不设置具体路径,Windows环境下,新建的文件会与这个py文件处于同一个文件夹内。但是Linux环境下,根据不同的服务器配置规则,不设置路径时文件可能会被保存在:“/root”目录下
第二个'r',这里是IO操作的具体选择参数,这里的r是指read也就是读取模式,这个模式下python只做读取工作,不能修改或追加,也就是最后只能使用read()方法。要注意的是,如果第一个路径文件名参数给了一个找不到的文件,那么read时会报错。
需要我们在每一个read前都判断一下当前路径有没有这个文件,使用os.path.exists('文件名')方法来判断。这个方法会返回一个Boolean也就是“是/否”的一个状态判断值,然后加入if判断即可:
if os.path.exists('设计最新需求.txt'):
with open('设计最新需求.txt','r',encoding='utf-8') as fileread:
old = fileread.read()
常用的还有:
w覆写模式(write):向文件内写入数据,不管这个文件内有没有其他数据,也不管这个文件存不存在,哪怕没有找到前边给出的文件,python也会帮我们创建,然后写入数据。
a 增加模式(add):与上一个 w 模式相同,是用来写入数据的,如果没有这个文件,a模式同样会帮我们创建并写入,但是与w模式不同的是: a 模式不会覆盖原来的信息,只会把这次要写入的数据添加进原来已有数据的后面。
同时,还有一些类似rb、r+、rb+这些以二进制或者读写模式来打开之类的,有兴趣的可以自己有空了解相关的内容。
第三个encoding='utf-8',这个是文件操作时使用的字符编码集,如果不专门声明python有可能会用gbk来解析咱们的文档,导致错误。
刚刚举了一个读取文件的例子,现在再举一个写入文件的例子:
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write(‘hello world’)
可以看到只要规定的不同的操作参数,就可以使用不同的方法来操作文件了。而且with open的操作不需要我们再主动关闭,也不容易出问题。
那么现在,我们已经可以获取最新的需求,也可以把一段信息保存进文件,或读取文件的信息,我们就可以使用这种方式来判断当前获取到的需求是不是最新的需求了。
大致的逻辑是:
获得html代码
从html代码里找到现在最新的需求
找到用于保存之前最新需求的文件(如果没有则创建一个保存最新需求的文件,并存入现在的最新需求)
获取之前的最新需求
比较现在的最新需求和之前的最新需求是否一样,如果一样证明这个不是最新需求
如果不一样证明我们发现了最新需求
用代码表示就是:
from lxml import etree
import urllib.request
import re
import os
#获取HTML代码的方法
#传入参数url是网址字符串
#返回参数是结果字符串
def getHtmlCode(url):
#创建对象
req_one = urllib.request.Request(url)
#header信息
req_one.add_header('User-Agent', 'Mozilla/6.0')
#加入 try catch模块防止request报错导致结束程序
try:
#注意!timeout参数如果设置了,有可能出现XX秒内没有执行完毕报错
res_one = urllib.request.urlopen(req_one,timeout=1)
#内容重编码后复制给变量
htmlcode = res_one.read().decode('utf-8')
#关闭urlopen方法
res_one.close()
#把结果返回
return htmlcode
except:
print("报错IP问题无法获得")
return "error"
#通过XPath获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByXPath(code):
#构建eTree
html = etree.HTML(code)
#通过XPath获取标题所在<a>标签的属性
name = html.xpath('/html/body/div/div/div/div/div/div/h3/a')
#通过XPath获取预算所在标签的属性
price = html.xpath('/html/body/div/div/div/div/div/div/h3/b')
print('通过XPath获取的最新的需求是:')
#一个html标签之间的文本信息使用.text获取
print(name.text.replace(' ','').replace(' ',''))
#a 标签的href属性也就是链接属性可以使用get('属性名')来获取
print(name.get('href'))
print(name.get('indus_name'))
print(price.text+'\r\n\r\n\r\n')
return name.text.replace(' ','').replace(' ',''),name.get('href'),name.get('indus_name')
#通过字符串获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByKeyWord(code):
startTarget = '<div class="task_class_list_li_box">'
#先找到列表所在的DIV层,使用index方法
taskStartIndex = code.index(startTarget)+len(startTarget)
taskEndIndex = code.index(startTarget,taskStartIndex,len(code))
untreatedInfo = code
taskUrl = re.search('https://task.epwk.com/\d*/', untreatedInfo) .group(0)
taskTitle = re.search('title=".*?"',untreatedInfo).group(0).replace('title=','').replace('"','')
print('通过字符串获取的最新的需求是:')
print(taskUrl)
print(taskTitle)
return taskUrl,taskTitle
#保存需求信息
#传入参数taskInfo是需要保存的需求信息字符串
#无返回参数
def saveTaskByOS(taskInfo):
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write(taskInfo)
print('保存成功!')
#读取文件获取旧的最新需求信息
#无参数
#返回参数为获取的需求信息
def getOldTaskInfo():
#读取文件需要先判断这个文件是否存在
if os.path.exists('设计最新需求.txt'):
#读取文件
with open('设计最新需求.txt','r',encoding='utf-8') as fileread:
old = fileread.read()
print('旧需求是:')
print(old)
return old
#如果文件不存在,则创建并将信息写入文件
else:
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write('null')
return 'null'
print('未发现文件,新建成功')
if __name__ == '__main__':
#获得html代码
htmlCode = getHtmlCode("https://task.epwk.com/sj/?o=7")
#从html代码里找到现在最新的需求
infoList = getInfoByKeyWord(htmlCode)
#把最新需求的返回值拼接成一个string字符串
taskInfo = infoList+infoList
#获取旧的需求
oldInfo = getOldTaskInfo()
#判断新需求是否为最新的需求
if(taskInfo == oldInfo):
print('未发现新需求')
else:
print('发现新需求:'+taskInfo)
saveTaskByOS(taskInfo)
最后的结果如下图:
可以看到,第一次执行程序通过发现两个需求不一样这个事情判断出了我们获取的最新的这个需求是一个用户新发布的需求。
而紧接着执行的第二次,程序比对了两个需求,发现是同一个需求。
现在,我们已经可以获取最新的设计需求了,大家可以稍微回顾一下如何进行io操作。接下来就需要把获取到的最新消息发送给我们了。
现在获取信息大概常用的就是:
1、使用消息推送接口
2、使用邮件服务
最开始我是想过使用类似钉钉的消息推送接口发送聊天消息、群消息或者企业信息。但是钉钉偏向移动端,打开第三方链接是使用本身的浏览器,保存不了登陆信息,看消息也比较麻烦。
最后我使用了邮件来发送新订单,好处是电脑、手机都可以操作,电脑用客户端直接点击链接可以用默认浏览器打开需求详情,登陆之后可以打开就投标。比较方便。
那么基础的邮件发送需要开通相关的IMAP/SMTP服务类似163邮箱、QQ邮箱,需要开启授权码,以163邮箱为例:
开启服务之后,新建授权码或得到一串16位全部由大写字母组成的授权码。(QQ邮箱开启需要发送一条短信到QQ指定号码里)
然后我们使用python的smtplib来发送邮件,同时需要导入Header和MIMEText
import smtplib
from email.header import Header
from email.mime.text import MIMEText
之后SMTP_host = "smtp.163.com" # SMTP服务器mail_user = "用户" # 用户名
from_passwd = "授权码" # 授权密码,非登录密码
from_account = '发件邮箱也就是用户名' # 发件人邮箱
toAccoutList = '收件人'需要邮箱账号名、授权码(就是刚刚申请开通的那个16位的密码),不需要邮箱的登陆密码。
然后就是发送邮件的代码了:
message = MIMEText(content, 'plain', 'utf-8')# 内容, 格式, 编码
message['Subject'] = subject
try:
smtpObj = smtplib.SMTP_SSL(SMTP_host, 465)# 启用SSL发信, 端口一般是465
smtpObj.login(from_account, from_passwd)# 登录验证
smtpObj.sendmail(from_account, to_account, message.as_string())# 发送
print("向用户"+to_account+"发送邮件成功!")
except smtplib.SMTPException as e:
print("向用户"+to_account+"发送邮件失败!")
print(e)
可以看到,构建发送邮件这样就可以了。需要注意的是:构建MIMEText的时候,传入的三个参数内容, 格式, 编码中,格式有很多种可选的,plain是普通的文本邮件,还可以有HTML格式。之后说HTML格式的邮件。
那么现在全部整合在一起的代码就是:
from lxml import etree
import urllib.request
import re
import os
import smtplib
from email.header import Header
from email.mime.text import MIMEText
SMTP_host = "smtp.163.com" # SMTP服务器
mail_user = "" # 用户名
from_passwd = "" # 授权密码,非登录密码
from_account = '' # 发件人邮箱
toAccoutList = ''
#获取HTML代码的方法
#传入参数url是网址字符串
#返回参数是结果字符串
def getHtmlCode(url):
#创建对象
req_one = urllib.request.Request(url)
#header信息
req_one.add_header('User-Agent', 'Mozilla/6.0')
#加入 try catch模块防止request报错导致结束程序
try:
#注意!timeout参数如果设置了,有可能出现XX秒内没有执行完毕报错
res_one = urllib.request.urlopen(req_one,timeout=1)
#内容重编码后复制给变量
htmlcode = res_one.read().decode('utf-8')
#关闭urlopen方法
res_one.close()
#把结果返回
return htmlcode
except:
print("报错IP问题无法获得")
return "error"
#通过XPath获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByXPath(code):
#构建eTree
html = etree.HTML(code)
#通过XPath获取标题所在<a>标签的属性
name = html.xpath('/html/body/div/div/div/div/div/div/h3/a')
#通过XPath获取预算所在标签的属性
price = html.xpath('/html/body/div/div/div/div/div/div/h3/b')
print('通过XPath获取的最新的需求是:')
#一个html标签之间的文本信息使用.text获取
print(name.text.replace(' ','').replace(' ',''))
#a 标签的href属性也就是链接属性可以使用get('属性名')来获取
print(name.get('href'))
print(name.get('indus_name'))
print(price.text+'\r\n\r\n\r\n')
return name.text.replace(' ','').replace(' ',''),name.get('href'),name.get('indus_name')
#通过字符串获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByKeyWord(code):
startTarget = '<div class="task_class_list_li_box">'
#先找到列表所在的DIV层,使用index方法
taskStartIndex = code.index(startTarget)+len(startTarget)
taskEndIndex = code.index(startTarget,taskStartIndex,len(code))
untreatedInfo = code
taskUrl = re.search('https://task.epwk.com/\d*/', untreatedInfo) .group(0)
taskTitle = re.search('title=".*?"',untreatedInfo).group(0).replace('title=','').replace('"','')
print('通过字符串获取的最新的需求是:')
print(taskUrl)
print(taskTitle)
return taskUrl,taskTitle
#保存需求信息
#传入参数taskInfo是需要保存的需求信息字符串
#无返回参数
def saveTaskByOS(taskInfo):
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write(taskInfo)
print('保存成功!')
#读取文件获取旧的最新需求信息
#无参数
#返回参数为获取的需求信息
def getOldTaskInfo():
#读取文件需要先判断这个文件是否存在
if os.path.exists('设计最新需求.txt'):
#读取文件
with open('设计最新需求.txt','r',encoding='utf-8') as fileread:
old = fileread.read()
print('旧需求是:')
print(old)
return old
#如果文件不存在,则创建并将信息写入文件
else:
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write('null')
return 'null'
print('未发现文件,新建成功')
def send_email(SMTP_host, from_account, from_passwd, to_account, subject, content):
message = MIMEText(content, 'plain', 'utf-8')# 内容, 格式, 编码
message['Subject'] = subject
try:
smtpObj = smtplib.SMTP_SSL(SMTP_host, 465)# 启用SSL发信, 端口一般是465
smtpObj.login(from_account, from_passwd)# 登录验证
smtpObj.sendmail(from_account, to_account, message.as_string())# 发送
print("向用户"+to_account+"发送邮件成功!")
except smtplib.SMTPException as e:
print("向用户"+to_account+"发送邮件失败!")
print(e)
if __name__ == '__main__':
#获得html代码
htmlCode = getHtmlCode("https://task.epwk.com/sj/?o=7")
#从html代码里找到现在最新的需求
infoList = getInfoByKeyWord(htmlCode)
#把最新需求的返回值拼接成一个string字符串
taskInfo = infoList+infoList
#获取旧的需求
oldInfo = getOldTaskInfo()
#判断新需求是否为最新的需求
if(taskInfo == oldInfo):
print('未发现新需求')
else:
print('发现新需求:'+taskInfo)
saveTaskByOS(taskInfo)
send_email(SMTP_host, from_account, from_passwd, toAccoutList,'一品威客新需求',taskInfo)
执行的结果如下:
可以看到我们就收到了最新的通知了。
截止到现在:
我们已经知道如何获取威客网站最新的需求,并且给我们发送通知邮件了。
但是现在的邮件其实大家会发现,我收到的邮件虽然有链接,但是链接不是以超级链接的形式存在,不能点击后跳转,那么就需要把我们发送的信息重新构建一下,以HTML的形式发送了。
也就是构建MIMEText的时候,传入的参数现在是:“plain”,这个是普通的文本邮件,需要改成“HTML”
同时发送的信息也需要构建成HTML网页代码才可以发送。
核心的代码可以写成:
msg+='<a href="'+@参数:网页链接+'"> <h1>'+@参数:需求标题+'</h1> </a></a>'
msg+="<p>需求标题:"+需求标题+"</p>"
也就是我们自己做一个H1或者P标签,给它外边套一个A标签,把这个订单的地址放到A标签的href里。
但是这里有一个非常重要的问题!!!
HTML构建时一定要包含DOCTYPE声明和完整的结构(HTML HEAD BODY这些)
否则,如果收件人不是发件人,网易邮件服务会给出“554, b'DT:SPM”错误提示发送失败。
那么现在对我们来说,一个完整的代码就是:
from lxml import etree
import urllib.request
import re
import os
import smtplib
from email.header import Header
from email.mime.text import MIMEText
SMTP_host = "smtp.163.com" # SMTP服务器
mail_user = "" # 用户名
from_passwd = "" # 授权密码,非登录密码
from_account = '' # 发件人邮箱
toAccoutList = ''
#获取HTML代码的方法
#传入参数url是网址字符串
#返回参数是结果字符串
def getHtmlCode(url):
#创建对象
req_one = urllib.request.Request(url)
#header信息
req_one.add_header('User-Agent', 'Mozilla/6.0')
#加入 try catch模块防止request报错导致结束程序
try:
#注意!timeout参数如果设置了,有可能出现XX秒内没有执行完毕报错
res_one = urllib.request.urlopen(req_one,timeout=1)
#内容重编码后复制给变量
htmlcode = res_one.read().decode('utf-8')
#关闭urlopen方法
res_one.close()
#把结果返回
return htmlcode
except:
print("报错IP问题无法获得")
return "error"
#通过XPath获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByXPath(code):
#构建eTree
html = etree.HTML(code)
#通过XPath获取标题所在<a>标签的属性
name = html.xpath('/html/body/div/div/div/div/div/div/h3/a')
#通过XPath获取预算所在标签的属性
price = html.xpath('/html/body/div/div/div/div/div/div/h3/b')
print('通过XPath获取的最新的需求是:')
#一个html标签之间的文本信息使用.text获取
print(name.text.replace(' ','').replace(' ',''))
#a 标签的href属性也就是链接属性可以使用get('属性名')来获取
print(name.get('href'))
print(name.get('indus_name'))
print(price.text+'\r\n\r\n\r\n')
return name.text.replace(' ','').replace(' ',''),name.get('href'),name.get('indus_name')
#通过字符串获取网页信息的方法
#传入参数url是网址字符串
#无返回参数
def getInfoByKeyWord(code):
startTarget = '<div class="task_class_list_li_box">'
#先找到列表所在的DIV层,使用index方法
taskStartIndex = code.index(startTarget)+len(startTarget)
taskEndIndex = code.index(startTarget,taskStartIndex,len(code))
untreatedInfo = code
taskUrl = re.search('https://task.epwk.com/\d*/', untreatedInfo) .group(0)
taskTitle = re.search('title=".*?"',untreatedInfo).group(0).replace('title=','').replace('"','')
print('通过字符串获取的最新的需求是:')
print(taskUrl)
print(taskTitle)
return taskUrl,taskTitle
#保存需求信息
#传入参数taskInfo是需要保存的需求信息字符串
#无返回参数
def saveTaskByOS(taskInfo):
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write(taskInfo)
print('保存成功!')
#读取文件获取旧的最新需求信息
#无参数
#返回参数为获取的需求信息
def getOldTaskInfo():
#读取文件需要先判断这个文件是否存在
if os.path.exists('设计最新需求.txt'):
#读取文件
with open('设计最新需求.txt','r',encoding='utf-8') as fileread:
old = fileread.read()
print('旧需求是:')
print(old)
return old
#如果文件不存在,则创建并将信息写入文件
else:
with open('设计最新需求.txt','w',encoding='utf-8') as file:
file.write('null')
return 'null'
print('未发现文件,新建成功')
def send_email(SMTP_host, from_account, from_passwd, to_account, subject, content):
message = MIMEText(content, 'HTML', 'utf-8')# 内容, 格式, 编码
message['Subject'] = subject
try:
smtpObj = smtplib.SMTP_SSL(SMTP_host, 465)# 启用SSL发信, 端口一般是465
smtpObj.login(from_account, from_passwd)# 登录验证
smtpObj.sendmail(from_account, to_account, message.as_string())# 发送
print("向用户"+to_account+"发送邮件成功!")
except smtplib.SMTPException as e:
print("向用户"+to_account+"发送邮件失败!")
print(e)
if __name__ == '__main__':
#获得html代码
htmlCode = getHtmlCode("https://task.epwk.com/sj/?o=7")
#从html代码里找到现在最新的需求
infoList = getInfoByKeyWord(htmlCode)
#把最新需求的返回值拼接成一个string字符串
taskInfo = infoList+infoList
msg='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
'''
msg+='<a href="'+infoList+'"> <h1>'+infoList+'</h1> </a></a>'
msg+="<p>需求标题:"+infoList+"</p>"
msg+='''
</body>
</html>
'''
#获取旧的需求
oldInfo = getOldTaskInfo()
#判断新需求是否为最新的需求
if(taskInfo == oldInfo):
print('未发现新需求')
else:
print('发现新需求:'+taskInfo)
saveTaskByOS(taskInfo)
send_email(SMTP_host, from_account, from_passwd, toAccoutList,infoList,msg)
最后的执行结果如下:
可以看到,这里的标题就是超级链接了,可以直接点击跳转。
好的,到今天为止,我们实现了:从威客网站的一个分类里获得最新的需求,如果这个是新需求给我们的邮箱发一个带超级链接的邮件,可以点击直接打开订单,看到订单详情。
现在还差:
1、持续抓取网页内容,获得新订单时给我们发送消息(很简单,各位可以自己尝试完成)
2、我们可以把全部订单信息保存到数据库里,方便统计整理客户的需求趋势,也方便外接一个简单的管理系统或者OA系统比如下图:
3、现在只是,单独的一个分类里获取,客户实际使用是需要多种分类里一起查找的,收件人也会是业务部的多个人,不过这些只是程序算法上的修改,各位有兴趣的可以自己尝试一下。
4、等全部的代码都完成了,如果不部署到服务器上,关机之后就收不到短信了,那如何把我们的项目部署到云服务器上呢?云服务器从购买到使用都应该如何操作?如何让代码一直运行在服务器里不间断?
明天我会继续写这方面的心得。
当然,我不是程序员,也没有专门学过python如果代码里有哪里可以改进的,请不吝赐教,谢谢~ 来学习!! 正在学习python 很高级,准备学习。马克一下 多谢大佬分享 感谢分享{:1_893:} 建议word文档和py源码文件一起打包,贴个地址,这样学习更方便,感谢大佬
页:
[1]