震惊,我发邮件让小张查个数据,居然10秒不到就邮件回复我了,怎么这么快?...

然后,我真的震惊了,本以为小张至少要查个20分钟的,结果10秒不到就回复我了:

好奇心超强的我等回到公司,马上问小张,为什么能这么快?居然不告诉我。

但是好奇心超强的我,并不会因此放弃,在我努力研究了2天python后,哈哈哈,我自己也做了一套一模一样的系统,下面我会将这些技术毫无保留的告诉你。

其实原理很简单,就是把本地要被查询的excel表统统注册成sql表,启动一个收邮件的客户端,不停的扫描未读邮件,一旦发现有满足条件的未读邮件,就启动读取语句,并启动sql查询,然后再启动一个邮件发送端,把邮件回去回去即可。

废话不多说,看看实现代码吧。(第5版和第6版也开始启动中,欢迎持续关注噢)

第四版指定配置文件规则构建可进行sql查询的DAO层编写用于页面渲染的view层编写用于接收邮件的Controller编写发送邮件相关的工具类编写串通整个流程的Context启动对象

第四版

本系统前面已经开发了三个版本,这个版本参考MVC设计思想进行重构,以页面渲染方式回复邮件。

指定配置文件规则

config.py文件的内容如下:

# 白名单,只处理列表内的邮件地址发来的邮件
blacklist = ["aaa@qq.com", "bbb@163.com", "ccc@qq.com", "ddd@163.com"]imapServer = "imap.163.com"
smtpServer = "smtp.163.com"
user = 'xxx@163.com'
password = 'xxx'# 可配置为一个路径字符串,或者文件名列表
# 类型为字符串时,被认为是路径,将搜索该路径下的所有xlsx文件,类型为列表时,则只加载指定文件名
files = r"D:\PycharmProjects\jupyter\self_test\excel_query_system\exceldata"  # 表示读取该路径下的所有xlsx文件
# files = ["exceldata/t1.xlsx", "exceldata/t1.xlsx", "exceldata/t1.xlsx"]# 邮件标题正则匹配规则,满足条件的标题才处理
keys = "查|(sql)|(数据)"# 邮件内每个结果显示的最大表格行数
max_table_line = 20
# 每个csv附近文件的最大行数
max_csv_line = 500

构建可进行sql查询的DAO层

加载所配置的excel表文件数据加载到内存,同时将其注册成为可以查询的sql表

把每个excel文件看到一个数据库,每个excel文件里的工作表看成一张数据库里的表,用_分割,表单对应sql里的表名就是excel文件名_sheetname。

如果只传excel的文件名作为表名,则查询这个excel文件的第一张表。

LoadExcelToSQL.py文件内容如下:

from pandasql import sqldfimport config, os
import pandas as pd# 将excel全部加载到内存
def reloadExcelFile():def handle(xlsx):df = pd.read_excel(xlsx, 0)name = file.rstrip(".xls").rstrip(".xlsx")df_dict[name] = dffor i, sheetname in enumerate(xlsx.sheet_names):if i == 0:df_dict[f"{name}_{sheetname}"] = dfelse:df_dict[f"{name}_{sheetname}"] = pd.read_excel(xlsx, i)print(f"加载{file}中的{sheetname}表单")print("现在要将所配置的excel表文件数据加载到内存,可能耗时较长")df_dict = {}if isinstance(config.files, str):for file in os.listdir(config.files):if file.startswith("~") or not file.endswith(".xlsx"): continuexlsx = pd.ExcelFile(os.path.join(config.files, file))handle(xlsx)elif isinstance(config.files, list):for file in config.files:xlsx = pd.ExcelFile(file)handle(xlsx)print("加载完毕")tablelist = "、".join(df_dict.keys())return lambda sql: sqldf(sql, df_dict), tablelist

编写用于页面渲染的view层

使用jinja2语法渲染html页面

创建一个html模板文件df.html,存放在htmlModule/templates文件夹中

内容如下(省略了CSS样式部分):

<div class="page"><h1>excel sql查询系统</h1>{% for i,sql,table in tables %}<h3>查询{{ i }}</h3><p>sql语句:</p><code>{{ sql }}</code>{{ table }}{% endfor %}<hr /><h2>关于</h2><p>尊敬的<{{ user }}>您好,您位于本系统的白名单,主题中出现(查、sql或数据)关键字时会触发sql查询,会检测每一条以select开头的sql语句,并查询后返回结果。</p><p>每个excel文件名+_+sheet表单名会被注册为sql表名,单excel文件名注册的表对应excel文件的第一个表单。</p><p>目前注册到系统中,可供查询的表有:</p><p>{{ tablelist }}</p><p>所有的表均支持标准sql语法查询。</p><h3>sql语法示例如下:</h3><code>select 级别,count(1) 数量,GROUP_CONCAT(到达号) from t1 group by 级别;<br/>select distinct t2.原到达号,t2.QTY,t2.产品种类,t3.参考信息,t3.SKU from t2 join t3 on t2.原到达号 == t3.参考信息;<br/>select 到达号,级别,操作完成日期 from t1 where 级别==3;<br/>select 到货编号,原到达号,SKV,QTY from t2 where 有效期>'2024' and 有效期<'2025';<br/>select 到达号,区域编号,储存位置,Qty,DEFS,Remark from t2_Sheet2 where Qty between 21 and 40;<br/>select 参考信息,区域编号,SKU,型号,储存位置,Reason,类型,操做员 from t3 where 操作E完成日期 between '2020-01-06' and '2020-01-10';</code>
</div>

创建一个MailSQLResultView.py文件,内容如下:

import loggingimport LoadExcelToSQL
from jinja2 import Environment, PackageLoaderimport configclass MailSQLResultView:def __init__(self):pysqlDao, tablelist = LoadExcelToSQL.reloadExcelFile()self.pysqlDao = pysqlDaoself.tablelist = tablelist# 配置jinja2在本地文件系统的搜索路径env = Environment(loader=PackageLoader('htmlModule'))self.template = env.get_template('df.html')def batch_sql_query(self, sqls) -> (list, list):tables = []csvs = []for i, sql in enumerate(sqls):print(i, sql)try:#  每条查询语句最大查询500条数据data = self.pysqlDao(sql).head(config.max_csv_line)# 每条sql网页最多只展示前10条,完整的500条数据需下载csv附件tables.append((i + 1, sql,f"<p>查询结果:</p>{data.head(config.max_table_line).to_html(index=False, na_rep='', border='0')}<p>"f"<font color='red'>对应的附件:{i + 1}.csv</font></p>"))csvs.append((i + 1, data.to_csv(index=False, na_rep='')))except Exception as e:error = str(e)logging.exception(error)tables.append((i + 1, sql, f"<p>报错信息:</p><code><font color='red'>{error}</font></code>"))return tables, csvsdef getHtmlView(self, user, tables):return self.template.render(user=user, tables=tables, tablelist=self.tablelist)

编写用于接收邮件的Controller

用于接收邮件处理相关的工具类,MailReceiveHandleUtil.py内容如下:

import email
import imaplib
from email.header import decode_header
from email.utils import parseaddrimport configdef login_and_select_INBOX():"登陆imap邮箱,并选择接收收件箱"conn = imaplib.IMAP4(config.imapServer, 143)conn.login(config.user, config.password)# 选择收件箱:conn.select("INBOX")return conndef fetchmsg(conn, i):"抓取指定编号的邮件"t, data = conn.fetch(i, '(RFC822)')msg = email.message_from_string(data[0][1].decode("utf-8"))msg.get_payload(decode=True)return msgdef decode_str(s):"解码函数"value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return valuedef getinfo(msg):"解析出邮件中的发件人,收件人和主题,"result = {}for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = decode_str(value)else:hdr, value = parseaddr(value)name = decode_str(hdr)if name: result['name'] = nameresult[header] = valuereturn resultdef 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 charsetdef parse_msg(msg, contentlist: list):"递归解析邮件中的文本内容存储到contentlist中"if msg.is_multipart():parts = msg.get_payload()for part in parts:parse_msg(part, contentlist)elif msg.get_content_type() == 'text/plain':content = msg.get_payload(decode=True)charset = guess_charset(msg)if charset:content = content.decode(charset)if content:contentlist.append(content)def unescape(s):"还原html转义字符"s = s.replace("&lt;", "<")s = s.replace("&gt;", ">")s = s.replace("&amp;", "&")return s

ImapMailReceiveController.py文件内容如下:

from MailReceiveHandleUtil import *class ImapMailController:def __init__(self, context, receiveType='Recent'):self.context = contextself.receiveType = receiveTypedef startOnceReceive(self):# 登陆邮箱conn = login_and_select_INBOX()# 获取所有未读邮件的序号t, data = conn.search(None, self.receiveType)msgList = data[0].split()for i in msgList:msg = fetchmsg(conn, i)mailMeta = getinfo(msg)contentList = []parse_msg(msg, contentList)mailContent = "".join(contentList)mailContent = unescape(mailContent)# 将解析出来的每一封邮件的元信息和邮件内容交给上下文context对象处理self.context.processMail(mailMeta, mailContent)conn.close()conn.logout()

编写发送邮件相关的工具类

用于构建邮件内容的工具类,MailSendHandleUtil.py文件内容如下:

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMETextdef buildMessageByString(s):message = MIMEMultipart()message.attach(MIMEText(s, 'html', 'utf-8'))return messagedef buildMessageByTables(page, csvs):message = MIMEMultipart()for i, csv in csvs:att = MIMEText(csv, 'base64', 'utf-8')att["Content-Type"] = 'application/octet-stream'att["Content-Disposition"] = f'attachment; filename="{i}.csv"'message.attach(att)message.attach(MIMEText(page, 'html', 'utf-8'))return message

将邮件发送出去的工具类,smtpMailSender.py文件内容如下:

import smtplib
from email.header import Header
from email.utils import formataddr
import configdef sendmail(message, mailMeta):message['From'] = formataddr(["数据查询专员pysql", config.user])  # 括号里的对应发件人邮箱昵称、发件人邮箱账号message['To'] = formataddr([mailMeta.get("name", mailMeta['From']), mailMeta['From']])  # 括号里的对应收件人邮箱昵称、收件人邮箱账号message['Subject'] = Header(f"回复:{mailMeta['Subject']},结果见邮件", 'utf-8')smtpObj = smtplib.SMTP(config.smtpServer, 25)smtpObj.login(config.user, config.password)smtpObj.sendmail(config.user, mailMeta['From'], message.as_string())print("邮件发送成功")smtpObj.close()

编写串通整个流程的Context启动对象

AppContext.py文件内容如下:

import re
import timeimport LoadExcelToSQL
import configfrom ImapMailReceiveController import ImapMailController
from MailSQLResultView import MailSQLResultView
from MailSendHandleUtil import *
from smtpMailSender import sendmailclass AppContext():def __init__(self):self.imapMailController = ImapMailController(self)self.view = MailSQLResultView()def checkInstruct(self, subject):if subject == "reloadExcelFile":pysqlDao, tablelist = LoadExcelToSQL.reloadExcelFile()self.view.pysqlDao = pysqlDaoself.view.tablelist = tablelistreturn "reload Excel File Successful"return Nonedef processMail(self, mailMeta, mailContent):print("收到邮件:", mailMeta, "内容:", mailContent[:20])if mailMeta['From'] not in config.blacklist:print(mailMeta['From'], "不在白名单,不作处理")returnif mailMeta['Subject'].startswith("回复") or mailMeta['Subject'].startswith("Re"):print("该邮件以回复或Re开头,不作处理")returninstructResult = self.checkInstruct(mailMeta['Subject'])if instructResult:print(mailMeta['From'], "执行了", mailMeta['Subject'], "指令")message = buildMessageByString(instructResult)sendmail(message, mailMeta)elif re.search(config.keys, mailMeta['Subject']):print("触发查询关键字,开始处理")sqls = [line for line in mailContent.splitlines() if line.startswith("select")]print(sqls)tables, csvs = self.view.batch_sql_query(sqls)page = self.view.getHtmlView(mailMeta.get("name", mailMeta['From']), tables)message = buildMessageByTables(page, csvs)sendmail(message, mailMeta)def start(self):while 1:self.imapMailController.startOnceReceive()time.sleep(5)app = AppContext()
app.start()

为了让读者大致理解邮件收取的操作,再次演示一下第一版的编写过程:

目录:
imap接收邮件测试编写持续接收未读邮件的程序编写数据处理逻辑回复邮件的程序第一版处理系统编写扩展需求白名单机制邮件文本内容解析可以进行多表处理的逻辑邮件构建器第二版系统完整代码编写

imap接收邮件测试

注意,你的邮箱必须开启IMAP和SMTP服务才能使用,如果使用qq或网易这类邮箱。代码中使用的密码是专用的授权码,而不是你的邮箱登陆密码:

首先建立连接并登陆:

import imaplibmailServer = "imap.163.com"
user = 'xxm@163.com'
password = 'xxx'conn = imaplib.IMAP4(mailServer, 143)
conn.login(user, password)
('OK', [b'LOGIN completed'])

注意:

网易云的邮箱后面操作中可能会报:
imaplib.error: command SEARCH illegal in state AUTH, only allowed in states SELECTED
这是网易为了推自己的客户端,邮箱大师作怪。

解决方案:

使用字母邮箱,不要使用电话号码邮箱。
然后配置一下:

http://config.mail.163.com/settings/imap/index.jsp?uid=YOUR_EMAIL_NAME@163.com

例如你的邮箱地址是xxm@163.com,则地址为:

http://config.mail.163.com/settings/imap/index.jsp?uid=xxm@163.com

选择一个目录,收件箱默认名称是"INBOX",如果是自己新建的文件夹,名称一般会是"INBOX.新建文件夹",不同的邮箱表示方式不一样。
可以运行conn.list()查看所有的文件夹:

conn.list()
('OK',[b'() "/" "INBOX"',b'(\\Drafts) "/" "&g0l6P3ux-"',b'(\\Sent) "/" "&XfJT0ZAB-"',b'(\\Trash) "/" "&XfJSIJZk-"',b'(\\Junk) "/" "&V4NXPpCuTvY-"',b'() "/" "&dcVr0mWHTvZZOQ-"',b'() "/" "&Xn9USpCuTvY-"',b'() "/" "&i6KWBZCuTvY-"'])

选择收件箱:

conn.select("INBOX")
('OK', [b'8'])

然后搜索指定类型的邮件,第二个参数一般选项有:

  • "ALL": 所有邮件

  • "Recent": 未读邮件

  • "Seen": 已读邮件

  • "Answered": 已回复的邮件

  • "Flagged": 被标记为“紧急/特别注意”的邮件

  • "Deleted": 已删除邮件,但python无法看到

  • "Draft": 草稿箱内的邮件,但python无法看到

还可以填写:

conn.search(None, '(SUBJECT "Essh")')

表示找Essh邮件。也可以同时指定多个查询条件,例如(FROM xxxx SUBJECT "aaa")表示来自xxxx主题为aaa.

可参考:http://www.afterlogic.com/mailbee-net/docs/MailBee.ImapMail.Imap.Search_overload_1.html

search第一个参数是charset的意思,填None表示用默认ASCII:

t, data = conn.search(None, 'ALL')
msgList = data[0].split()
msgList
[b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8']

msgList返回了一个邮件序号,从1到N编号。一般情况下,编号越大距离现在越近。

现在以RFC822获取最近一封邮件的邮件格式,再用email.message_from_string转换为message对象:

import email, base64def fetchmsg(i):t, data = conn.fetch(i, '(RFC822)')msg = email.message_from_string(data[0][1].decode("utf-8"))msg.get_payload(decode=True)return msgmsg = fetchmsg(msgList[-1])
print(msg)
Received: from wowangzhouming$163.com ( [223.104.63.49] ) byajax-webmail-wmsvr124 (Coremail) ; Sun, 26 Apr 2020 23:14:27 +0800 (CST)
X-Originating-IP: [223.104.63.49]
Date: Sun, 26 Apr 2020 23:14:27 +0800 (CST)
From: =?GBK?B?0KHQocP3?= <wowangzhouming@163.com>
To: 15074804724@163.com
Subject: test
X-Priority: 3
X-Mailer: Coremail Webmail Server Version XT5.0.10 build 20190724(ac680a23)Copyright (c) 2002-2020 www.mailtech.cn 163com
X-CM-CTRLDATA: qAC2E2Zvb3Rlcl9odG09MTA5OjU2
Content-Type: multipart/alternative;boundary="----=_Part_134809_656158486.1587914067467"
MIME-Version: 1.0
Message-ID: <1e801115.962c.171b70dce0b.Coremail.wowangzhouming@163.com>
X-Coremail-Locale: zh_CN
X-CM-TRANSID: fMGowACHwPtTpaVe7cAjAA--.21747W
X-CM-SenderInfo: pzrzt05j2k03pplqwqqrwthudrp/1tbiSgMS-lPAK-bSWgARsy
X-Coremail-Antispam: 1U5529EdanIXcx71UUUUU7vcSsGvfC2KfnxnUU==------=_Part_134809_656158486.1587914067467
Content-Type: text/plain; charset=GBK
Content-Transfer-Encoding: 7bitno
------=_Part_134809_656158486.1587914067467
Content-Type: text/html; charset=GBK
Content-Transfer-Encoding: 7bit<div style="line-height:1.7;color:#000000;font-size:14px;font-family:Arial"><p style="margin:0;">no</p></div><br><br><span title="neteasefooter"><p>&nbsp;</p></span>
------=_Part_134809_656158486.1587914067467--

经过一番努力已经获得了一堆看不懂的内容,现在访问我们需要的内容:

print(msg['subject'])
print(msg['from'])
print(msg['to'])
test
=?GBK?B?0KHQocP3?= <wowangzhouming@163.com>
15074804724@163.com

返回的东西仍需要解码,现在编写解码函数,并进行解码:

from email.header import decode_headerdef decode_str(s):value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return valuefrom email.utils import parseaddrdef getinfo(msg):result = {}for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = decode_str(value)else:hdr, value = parseaddr(value)name = decode_str(hdr)if name: result['name'] = nameresult[header] = valuereturn resultgetinfo(msg)
{'name': '小小明','From': 'xxm@163.com','To': '15074804724@163.com','Subject': 'test'}
for i in msgList:msg = fetchmsg(i)result = getinfo(msg)print(i, result)
b'1' {'name': '网易邮件中心', 'From': 'mail@service.netease.com', 'To': 'wowangzhouming@163.com', 'Subject': '网易邮箱,那些你知道和不知道的事'}
b'2' {'name': '网易帐号中心 ', 'From': 'passport@service.netease.com', 'To': 'm15074804724@163.com', 'Subject': '网易邮箱帐号异常登录提醒'}
b'3' {'name': '网易帐号中心 ', 'From': 'passport@service.netease.com', 'To': 'm15074804724@163.com', 'Subject': '网易邮箱帐号异常登录提醒'}
b'4' {'name': '微信团队', 'From': 'weixinteam@qq.com', 'To': '15074804724@163.com', 'Subject': '微信小程序长时间未使用将被冻结'}

编写持续接收未读邮件的程序

#!/usr/bin/env python
# coding: utf-8import imaplib
import email, base64, time
from email.header import decode_header
from email.utils import parseaddr# 建立连接并登陆:
def loginAndSelect():mailServer = "imap.163.com"user = 'xxm@163.com'password = 'xxx'conn = imaplib.IMAP4(mailServer, 143)conn.login(user, password)# 选择收件箱:conn.select("INBOX")return conn# 抓取指定编号的邮件
def fetchmsg(conn, i):t, data = conn.fetch(i, '(RFC822)')msg = email.message_from_string(data[0][1].decode("utf-8"))msg.get_payload(decode=True)return msg# 解码函数
def decode_str(s):value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return value# 解析出邮件中的发件人,收件人和主题,
def getinfo(msg):result = {}for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = decode_str(value)else:hdr, value = parseaddr(value)name = decode_str(hdr)if name: result['name'] = nameresult[header] = valuereturn resultdef main():# loginwhile 1:conn = loginAndSelect()#  获取所有未读邮件的序号t, data = conn.search(None, 'Recent')msgList = data[0].split()for i in msgList:msg = fetchmsg(conn, i)result = getinfo(msg)print(i, result)conn.close()conn.logout()#  每10秒检测一次time.sleep(10)main()
b'1' {'name': '网易邮件中心', 'From': 'mail@service.netease.com', 'To': 'wowangzhouming@163.com', 'Subject': '网易邮箱,那些你知道和不知道的事'}
b'2' {'name': '网易帐号中心 ', 'From': 'passport@service.netease.com', 'To': 'm15074804724@163.com', 'Subject': '网易邮箱帐号异常登录提醒'}
b'3' {'name': '网易帐号中心 ', 'From': 'passport@service.netease.com', 'To': 'm15074804724@163.com', 'Subject': '网易邮箱帐号异常登录提醒'}
b'4' {'name': '微信团队', 'From': 'weixinteam@qq.com', 'To': '15074804724@163.com', 'Subject': '微信小程序长时间未使用将被冻结'}

经测试,程序顺利运行成功。

编写数据处理逻辑

import pandas as pddf = pd.read_excel(r"data.xlsx")
df.head()

前面我写了使用SQL操作Pandas以及使用DataFrame的query函数过滤数据一文,其实我们依然可以里面这种类sql的语法,直接将字符串传入query方法。

单值查询:

df.query("操作等级==3")

多值查询与逻辑运算:

df.query("序号 in (4,8) or 操作等级==3")

范围查询:

df.query("实际开始日期>'2018-05-15' and 实际开始日期<'2018-05-16'")
几乎sql语句的where过滤部分都支持。
测试一下生成csv和html表格文本:
data = df.query("序号 in (4,8)")
data.to_csv(index=False)
data.to_html(index=False)

回复邮件的程序

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header# 第三方 SMTP 服务
from email.utils import formataddrmail_host = "smtp.163.com"  # 设置服务器
mail_user = 'xxm@163.com'  # 用户名
mail_pass = 'xxx'  # 口令receiver = '604049322@qq.com'  # 接收邮箱mail_msg = """
<p>Python 邮件发送测试...</p>
<p><a href="http://blog.xiaoxiaoming.xyz">网站链接</a></p>
<p>图片演示:</p>
"""# 创建一个带附件的实例
message = MIMEMultipart()
message['From'] = formataddr(["^_^我是发件人小小明",mail_user])  # 括号里的对应发件人邮箱昵称、发件人邮箱账号
message['To'] = formataddr(["这里写收件人", receiver])  # 括号里的对应收件人邮箱昵称、收件人邮箱账号
message['Subject'] = Header('反馈结果', 'utf-8')
# 邮件正文内容
message.attach(MIMEText(mail_msg, 'html', 'utf-8'))att1 = MIMEText(mail_msg, 'base64', 'utf-8')
att1["Content-Type"] = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="test.txt"'
message.attach(att1)smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25)  # 25 为 SMTP 端口号
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(mail_user, receiver, message.as_string())
print("邮件发送成功")

经测试发邮件的程序也顺利。

第一版完整的处理系统编写

#!/usr/bin/env python
# coding: utf-8import imaplib
import email, base64, time
from email.header import decode_header
from email.utils import parseaddr
import pandas as pdimport smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header# 第三方 SMTP 服务
from email.utils import formataddr# 建立连接并登陆:
def loginAndSelect():imapServer = "imap.163.com"user = 'xxx@163.com'password = 'xxx'conn = imaplib.IMAP4(imapServer, 143)conn.login(user, password)# 选择收件箱:conn.select("INBOX")return conn# 抓取指定编号的邮件
def fetchmsg(conn, i):t, data = conn.fetch(i, '(RFC822)')msg = email.message_from_string(data[0][1].decode("utf-8"))msg.get_payload(decode=True)return msg# 解码函数
def decode_str(s):value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return value# 解析出邮件中的发件人,收件人和主题,
def getinfo(msg):result = {}for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = decode_str(value)else:hdr, value = parseaddr(value)name = decode_str(hdr)if name: result['name'] = nameresult[header] = valuereturn resultdef main():df = pd.read_excel(r"data.xlsx")# loginwhile 1:conn = loginAndSelect()#  获取所有未读邮件的序号t, data = conn.search(None, 'Recent')msgList = data[0].split()for i in msgList:msg = fetchmsg(conn, i)result = getinfo(msg)print(i, result['name'], result['From'], result['Subject'])smtpServer = "smtp.qq.com"user = "604049322@qq.com"password = 'xxx'# 创建一个带附件的实例message = MIMEMultipart()message['From'] = formataddr(["数据回复员", user])  # 括号里的对应发件人邮箱昵称、发件人邮箱账号message['To'] = formataddr([result['name'], result['From']])  # 括号里的对应收件人邮箱昵称、收件人邮箱账号message['Subject'] = Header(f"查询{result['Subject']}的结果", 'utf-8')try:#  执行查询,最大返回1000条数据data = df.query(result['Subject']).head(1000)# 邮件正文内容message.attach(MIMEText(data.to_html(index=False), 'html', 'utf-8'))# 附件内容att1 = MIMEText(data.to_csv(index=False), 'base64', 'utf-8')att1["Content-Type"] = 'application/octet-stream'att1["Content-Disposition"] = 'attachment; filename="result.csv"'message.attach(att1)except Exception as e:mes = f"""查询语法错误,错误信息:{e}请将要查询的内容按照如下规则作为邮件标题,语法示例:查询操作等级等于3的数据:操作等级==3查询序号为4或8的数据:序号 in (4,8)查询某个数据段的数据:实际开始日期>'2018-05-15' and 实际开始日期<'2018-05-16'"""# 邮件正文内容message.attach(MIMEText(mes, 'plain', 'utf-8'))smtpObj = smtplib.SMTP_SSL(smtpServer, 465)smtpObj.login(user, password)smtpObj.sendmail(user, result['From'], message.as_string())print("邮件发送成功")smtpObj.close()conn.close()conn.logout()#  每10秒检测一次time.sleep(10)main()
b'17' ノ记ッ忆ホ wowangzhouming@qq.com Re:查询操作等级==3的结果
邮件发送成功


经测试,程序可以持续保证运行,每10秒检测一下是否存在新邮件,有则进行处理。

发送邮件:

返回结果:

扩展需求

白名单机制邮件文本内容解析可以进行多表处理的逻辑邮件构建器第二版系统完整代码编写

现在将增加白名单机制,并支持一个邮件即可进行多个查询,以前只解释邮件主题,现在将进行复杂的邮件内容解析

白名单机制

白名单机制表示只对指定收件人列表的邮件进行应答,这个白名单,我们保存到一个py文件中作为配置文件,方便后期修改。

干脆发件和收件的邮箱也写到一个配置文件中,创建一个config.py文件,内容如下:

blacklist = ["aaaa@qq.com", "bbbb@163.com", "ccccc@163.com"]imapServer = "imap.163.com"
imapuser = 'aaaa@163.com'
imappassword = 'xxxx'
smtpServer = "smtp.qq.com"
smtpuser = "bbb@qq.com"
smtppassword = 'xxxx'

访问方式:

import configconfig.blacklist
['604049322@qq.com', 'wowangzhouming@163.com', 'wowangzhouming@qq.com']

只需要增加如下代码即可实现白名单机制:

if result['From'] not in config.blacklist:continue

邮件文本内容解析

为了实现能处理邮件中的文本内容,不止是主题,编写了两个解析函数:

# 用于分析邮件内容的编码
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# 递归解析邮件中的文本内容存储到contentlist中
def parse_msg(msg, contentlist: list):# 对与多块还需递归调用if msg.is_multipart():parts = msg.get_payload()for part in parts:parse_msg(part, contentlist)elif msg.get_content_type() == 'text/plain':content = msg.get_payload(decode=True)charset = guess_charset(msg)if charset:content = content.decode(charset)if content:contentlist.append(content)

测试邮件解析功能:

#!/usr/bin/env python
# coding: utf-8import email
import imaplib
from email.header import decode_header
from email.utils import parseaddr# 第三方 SMTP 服务
import config# 建立连接并登陆:
def loginAndSelect():conn = imaplib.IMAP4(config.imapServer, 143)conn.login(config.imapuser, config.imappassword)# 选择收件箱:conn.select("INBOX")return conn# 抓取指定编号的邮件
def fetchmsg(conn, i):t, data = conn.fetch(i, '(RFC822)')msg = email.message_from_string(data[0][1].decode("utf-8"))msg.get_payload(decode=True)return msg# 解码函数
def decode_str(s):value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return value# 解析出邮件中的发件人,收件人和主题,
def getinfo(msg):result = {}for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = decode_str(value)else:hdr, value = parseaddr(value)name = decode_str(hdr)if name: result['name'] = nameresult[header] = valuereturn result# 用于分析邮件内容的编码
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# 递归解析邮件中的文本内容存储到contentlist中
def parse_msg(msg, contentlist: list):# 对与多块还需递归调用if msg.is_multipart():parts = msg.get_payload()for part in parts:parse_msg(part, contentlist)elif msg.get_content_type() == 'text/plain':content = msg.get_payload(decode=True)charset = guess_charset(msg)if charset:content = content.decode(charset)if content:contentlist.append(content)conn = loginAndSelect()
#  获取所有未读邮件的序号
t, data = conn.search(None, 'ALL')
msgList = data[0].split()
for i in msgList:msg = fetchmsg(conn, i)result = getinfo(msg)print(result.get('name', ''), result['From'], result['To'], result['Subject'])contentList = []parse_msg(msg, contentList)content = "".join(contentList)print(content)conn.close()
网易帐号中心  passport@service.netease.com m15074804724@163.com 网易邮箱帐号异常登录提醒拉勾网 lagou@mail.lagoujobs.com 15074804724@163.com [拉勾牛人在线]下一个CTO会不会是你?(AD)拉勾网 lagou@mail.lagoujobs.com 15074804724@163.com 【直播招人】游戏大佬亲身示范,如何3小时稳准狠拿到Offer,预约!和大佬交个朋友>>>(AD)拉勾网 lagou@mail.lagoujobs.com 15074804724@163.com 游戏行业的技术人,如何选择适合发展领域,拿到满意offer?速戳约大佬>>>(AD)拉勾网 lagou@mail.lagoujobs.com 15074804724@163.com 拉勾教育1周年|你的学习、跳槽、升职加薪,我们包了(AD)拉勾教育 lagou@mail.lagoujobs.com 15074804724@163.com 大数据从业者的高薪「潜规则」(AD)小小明 wowangzhouming@163.com 15074804724@163.com test
no
wowangzhouming wowangzhouming@qq.com wowangzhouming@163.com query 重新给我查
t1::序号 in (4,8)
t1::实际开始日期&gt;='2018-05-15' and 实际开始日期<'2018-05-16'
t2:Sheet2:Qty == 15('OK', [b'CLOSE completed'])

经测试邮件中的文本内容均可正常获取。

可以进行多表处理的逻辑

前面的代码只能读取一个文件,而且文件名写死,现在改成可以配置的。

前面的config.py文件再加上:

# excel文件标示所对应的文件路径
filenames = {"t1": "t1.xlsx", "t2": "t2.xlsx", "t3": "t3.xlsx"}
# excel文件标示所对应的sheet名列表(整数索引或表单名称)
sheetnames = {"t1": ["Sheet1"], "t2": ["Sheet1", "Sheet2"], "t3": ["Sheet1"]}
import pandas as pd
import config# 将excel全部加载到内存
df_dict = {}
for file, filepath in config.filenames.items():xlsx = pd.ExcelFile(filepath)for sheet in config.sheetnames[file]:df = pd.read_excel(xlsx, sheet)dfs = df_dict.setdefault(file, {})dfs[sheet] = df# 对应文件中sheet不存在则随机返回一张sheet
def findDataFream(file, sheet=None):if file not in df_dict:return Nonedfs = df_dict[file]if sheet not in dfs:return list(dfs.values())[0]return dfs[sheet]df = findDataFream("t2")
df.head()

解决html转义字符的问题:

def unescape(s):s = s.replace("&lt;", "<")s = s.replace("&gt;", ">")s = s.replace("&amp;", "&")return s
unescape("t1::实际开始日期&gt;='2018-05-15' and 实际开始日期<'2018-05-16'")
"t1::实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16'"

将用户编写的查询规则结构化:

def createQueryDF(content):content = unescape(content)se = pd.Series(content.splitlines())querysdf = se.str.extract(r'(?P<file>.*):(?P<sheet>.*):(?P<query>.*)')querysdf.dropna(subset=['file', 'query'], inplace=True)return querysdfquerysdf = createQueryDF(content)
querysdf

将用户编写的查询规则结构化(详细可见Pandas之子串匹配与提取(Series.str中的extract/extractall/contains/match)):

def createQueryDF(content):content = unescape(content)se = pd.Series(content.splitlines())querysdf = se.str.extract(r'(?P<file>.*):(?P<sheet>.*):(?P<query>.*)')querysdf.dropna(subset=['file', 'query'], inplace=True)return querysdfquerysdf = createQueryDF(content)
querysdf
filesheetquery
0t1
序号 in (4,8)
1t1
实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16'
2t2Sheet2Qty == 15

邮件构建器

将开头的示例文本也写入的配置文件中:
config.py添加如下内容:

sample = f"""最新查询语法示例:
t1::序号 in (4,8)
t1::实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16'
t2:Sheet2:Qty == 15每行表示一个查询规则,每个规则分为三段:文件名、表单名和查询语句,用:分割
一个excel文件中只有一个表单名时,表单名称可以省略。
序号 in (4,8)表示查询序号为4或8的数据,等价写法是 序号==4 or 序号==8
实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16'表示查询日期为2018-05-15这一天的
Qty == 15表示查询Qty等于15的数据"""
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText# 查询并构建邮件内容
def buildMessage(querysdf=None):message = MIMEMultipart()if querysdf is None:message.attach(MIMEText(config.sample, 'html', 'utf-8'))return messageresult = []for i, row in querysdf.iterrows():querystr = ":".join(row.values.tolist())df = findDataFream(row.file, row.sheet)if df is None:result.append(f"<p>查询编号:{i},{querystr},查询结果:</p><font color='red'>《{row.file}.xlsx》文件不存在</font><hr />")continueprint(f"查询:{querystr}", "被查询的表列名为:", df.columns.values)try:#  每条查询语句最大查询100条数据data = df.query(row.query).head(100)result.append(f"<p>查询编号:{i},{querystr},查询结果:</p>")result.append(data.to_html(index=False))result.append("<hr />")# 附件内容att = MIMEText(data.to_csv(index=False), 'base64', 'utf-8')att["Content-Type"] = 'application/octet-stream'att["Content-Disposition"] = f'attachment; filename="{i}.csv"'message.attach(att)except Exception as e:logging.exception(e)mes = str(e)result.append(f"<p>查询编号:{i},{querystr},查询结果:</p><p>语法错误:<font color='red'>{mes}</font></p><hr />")# 邮件正文内容message.attach(MIMEText(config.sample + "<br />".join(result), 'html', 'utf-8'))return messagebuildMessage(querysdf)
查询:t1::序号 in (4,8) 被查询的表列名为: ['序号' '进仓流水号' '到仓库日期' '操作等级' '实际开始日期' '实际完成日期' '计划收货开始日期']
查询:t1::实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16' 被查询的表列名为: ['序号' '进仓流水号' '到仓库日期' '操作等级' '实际开始日期' '实际完成日期' '计划收货开始日期']
查询:t2:Sheet2:Qty == 15 被查询的表列名为: ['报关日期' '返工完成' '反馈日期' 'PostingDate' 'Reference' 'Plant' 'Material' 'Batch''StorageType' 'StorageBin' 'Qty' 'Salesunit' 'SLED/BBD' 'Remark' '实际库位''Reason' 'Responser' '类型' '小组' '灭菌/非灭菌' '重建RPR' '返工编号']<email.mime.multipart.MIMEMultipart at 0xecffa58>

第二版系统完整代码编写

文件config.py内容如下,根据自身情况修改配置文件即可:

blacklist = ["aaaa@qq.com", "bbbb@163.com", "ccccc@163.com"]imapServer = "imap.163.com"
imapuser = 'aaaa@163.com'
imappassword = 'xxxx'
smtpServer = "smtp.qq.com"
smtpuser = "bbb@qq.com"
smtppassword = 'xxxx'# excel文件标示所对应的文件路径
filenames = {"t1": "t1.xlsx", "t2": "t2.xlsx", "t3": "t3.xlsx"}
# excel文件标示所对应的sheet名列表(整数索引或表单名称)
sheetnames = {"t1": [0], "t2": [0, "Sheet2"], "t3": ["Sheet1"]}sample = f"""要查询数据,要求邮件主题必须以query开头,邮件内容为纯文本的查询语句。最新查询语法示例:<pre>
t1::序号 in (4,8)
t1::实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16'
t2:Sheet2:Qty == 15每行表示一个查询规则,每个规则分为三段:文件名、表单名和查询语句,用:分割
一个excel文件中只有一个表单名时,表单名称可以省略。
序号 in (4,8)表示查询序号为4或8的数据,等价写法是 序号==4 or 序号==8
实际开始日期>='2018-05-15' and 实际开始日期<'2018-05-16'表示查询日期为2018-05-15这一天的
Qty == 15表示查询Qty等于15的数据<pre/>
<hr />
"""

app.py完整代码:

#!/usr/bin/env python
# coding: utf-8import email
import imaplib
import logging
import smtplib
import time
from email.header import Header
from email.header import decode_header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr
from email.utils import parseaddrimport pandas as pdimport config# 建立连接并登陆:
def login_and_select_INBOX():conn = imaplib.IMAP4(config.imapServer, 143)conn.login(config.imapuser, config.imappassword)# 选择收件箱:conn.select("INBOX")return conn# 抓取指定编号的邮件
def fetchmsg(conn, i):t, data = conn.fetch(i, '(RFC822)')msg = email.message_from_string(data[0][1].decode("utf-8"))msg.get_payload(decode=True)return msg# 解码函数
def decode_str(s):value, charset = decode_header(s)[0]if charset:value = value.decode(charset)return value# 解析出邮件中的发件人,收件人和主题,
def getinfo(msg):result = {}for header in ['From', 'To', 'Subject']:value = msg.get(header, '')if value:if header == 'Subject':value = decode_str(value)else:hdr, value = parseaddr(value)name = decode_str(hdr)if name: result['name'] = nameresult[header] = valuereturn result# 用于分析邮件内容的编码
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# 递归解析邮件中的文本内容存储到contentlist中
def parse_msg(msg, contentlist: list):# 对与多块还需递归调用if msg.is_multipart():parts = msg.get_payload()for part in parts:parse_msg(part, contentlist)elif msg.get_content_type() == 'text/plain':content = msg.get_payload(decode=True)charset = guess_charset(msg)if charset:content = content.decode(charset)if content:contentlist.append(content)# 将excel全部加载到内存
df_dict = {}
for file, filepath in config.filenames.items():xlsx = pd.ExcelFile(filepath)for sheet in config.sheetnames[file]:df = pd.read_excel(xlsx, sheet)dfs = df_dict.setdefault(file, {})dfs[sheet] = df# 对应文件中sheet不存在则随机返回一张sheet
def findDataFream(file, sheet=None):if file not in df_dict:return Nonedf = df_dict[file]if sheet not in df:return list(df.values())[0]return df[sheet]# 解决html转义字符的问题:
def unescape(s):s = s.replace("&lt;", "<")s = s.replace("&gt;", ">")s = s.replace("&amp;", "&")return sdef createQueryDF(content):content = unescape(content)se = pd.Series(content.splitlines())querysdf = se.str.extract(r'(?P<file>.*):(?P<sheet>.*):(?P<query>.*)')querysdf.dropna(subset=['file', 'query'], inplace=True)return querysdf# 查询并构建邮件内容
def buildMessage(querysdf=None):message = MIMEMultipart()if querysdf is None:message.attach(MIMEText(config.sample, 'html', 'utf-8'))return messageresult = []for i, row in querysdf.iterrows():querystr = ":".join(row.values.tolist())df = findDataFream(row.file, row.sheet)if df is None:result.append(f"<p>查询编号:{i},{querystr},查询结果:</p><font color='red'>《{row.file}.xlsx》文件不存在</font><hr />")continueprint(f"查询:{querystr}", "被查询的表列名为:", df.columns.values)try:#  每条查询语句最大查询100条数据data = df.query(row.query).head(100)result.append(f"<p>查询编号:{i},{querystr},查询结果:</p>")result.append(data.to_html(index=False))result.append("<hr />")# 附件内容att = MIMEText(data.to_csv(index=False), 'base64', 'utf-8')att["Content-Type"] = 'application/octet-stream'att["Content-Disposition"] = f'attachment; filename="{i}.csv"'message.attach(att)except Exception as e:logging.exception(e)mes = str(e)result.append(f"<p>查询编号:{i},{querystr},查询结果:</p><p>语法错误:<font color='red'>{mes}</font></p><hr />")# 邮件正文内容message.attach(MIMEText(config.sample + "<br />".join(result), 'html', 'utf-8'))return messagedef main():while 1:# loginconn = login_and_select_INBOX()#  获取所有未读邮件的序号t, data = conn.search(None, 'Recent')msgList = data[0].split()for i in msgList:msg = fetchmsg(conn, i)result = getinfo(msg)print(result.get('name', ''), result['From'], result['To'], result['Subject'])if result['From'] not in config.blacklist: continueif not result['Subject'].startswith("query"): continuecontentList = []parse_msg(msg, contentList)content = "".join(contentList)if len(content) == 0:continuequerysdf = createQueryDF(content)if len(querysdf) == 0: continuemessage = buildMessage(querysdf)smtpObj = smtplib.SMTP_SSL(config.smtpServer, 465)smtpObj.login(config.smtpuser, config.smtppassword)message['From'] = formataddr(["数据回复员", config.smtpuser])  # 括号里的对应发件人邮箱昵称、发件人邮箱账号message['To'] = formataddr([result.get('name', ''), result['From']])  # 括号里的对应收件人邮箱昵称、收件人邮箱账号message['Subject'] = Header(f"回复:尊敬的<{result.get('name', '')}>您发送的<{result['Subject']}>结果见邮件", 'utf-8')smtpObj.sendmail(config.smtpuser, result['From'], message.as_string())print("邮件发送成功")smtpObj.close()conn.close()conn.logout()#  每1秒检测一次time.sleep(1)main()

END -

你也「在看」吗?????

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/53306.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序员,你真的懂得收发电子邮件吗?(转)

http://www.cnblogs.com/rootq/articles/1320266.html 前言 在几年以前&#xff0c;相信不少朋友都听说过&#xff0c;马云同志创办阿里巴巴的时候&#xff0c;还不会发邮件。也不知道在阿里巴巴上市之后&#xff0c;他学会收发邮件了没有&#xff01;呵呵。我是曾经从内心里“…

用Python发送电子邮件?这也太丝滑了吧(21)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 欢迎和猫妹一起&#xff0c;趣味学Python。 今日主题 猫爸赚钱养家&#xff0c;细想起来真的不容易啊&#xff01; 起早贪黑&#xff0c;都是6点早起做早饭&#xff0c;送…

邮件群发-不仅仅是自己想的那么简单(HTTP发送)

不管是工厂还是外贸公司&#xff0c;或者是SOHO&#xff0c;利用“伪EDM邮件营销”都可以用最小的代价&#xff0c;来获取询盘和订单。 而“伪EDM邮件营销”的核心技术&#xff0c;则是“裙发邮件”。 裙发邮件的作用&#xff1a;以最高效的方式把邮件推送给客户&#xff0c;增…

Anthropic对ChatGPT产品Claude发布,可以对外体验(目前并不支持中文)

Claude是下一代人工智能助手&#xff0c;基于 Anthropic 对训练有用、诚实和无害的人工智能系统的研究。Claude 可通过我们的开发人员控制台中的聊天界面和 API 进行访问&#xff0c;能够执行各种对话和文本处理任务&#xff0c;同时保持高度的可靠性和可预测性。 克劳德可以帮…

落地为王,「大模型」走出发布会

©️深响原创 作者&#xff5c;吴鸿键 大模型&#xff0c;大机遇。舆论喧嚣了几个月&#xff0c;有能力的企业都在抢占生态位。 芯片层&#xff0c;英伟达凭借其GPU优势&#xff0c;成为ChatGPT热潮中最先赚到钱的人&#xff0c;博通、微软、谷歌也都紧盯着芯片机遇&#…

Python 给视频添加背景音乐 | Python工具

目录 前言 环境依赖 代码 总结 前言 本文提供给视频添加背景音乐的python工具&#xff0c;一如既往的实用主义。 环境依赖 ffmpeg环境安装&#xff0c;可以参考我的另一篇文章&#xff1a;windows ffmpeg安装部署_阿良的博客-CSDN博客 本文主要使用到的不是ffmpeg&#x…

微信对话生成器,生成微信聊天记录,聊天记录生成器,制作微信聊天记录,可生成文字、语音、转账、红包,朋友圈装X神器~

软件使用方法 解压压缩包&#xff0c;得到一个文件夹&#xff0c;内容如下 双击【wechat.exe】运行&#xff0c;可以看到程序有两个子菜单&#xff0c;每一个菜单都能对聊天界面进行部分设置 我们进入【对话设置】子菜单&#xff0c;这里可以设置聊天对象的头像&#xff0c;…

AI绘画火爆,以昆仑万维AIGC为例,揭秘AI绘画背后的模型算法

AI绘画火爆&#xff0c;以昆仑万维AIGC为例&#xff0c;揭秘AI绘画背后的模型算法 一、前言 最近AI绘画让人工智能再次走进大众视野。在人工智能发展早起&#xff0c;一直认为人工智能能实现的功能非常有限。通常都是些死板的东西&#xff0c;像是下棋、问答之类的&#xff0…

【zabbix】企业微信告警

本篇基于我的前两章环境续写&#xff1a; 1、【zabbix】docker安装zabbix、yum安装zabbix-agent 2、【zabbix】MySQL模板创建与监控 企业微信部分 一、首先得有个企业微信号 添加部门 应用管理创建应用 我的企业里面找到企业id&#xff0c;和上面两条一起&#xff0c;总共三…

服务器请求微信后台(api.weixin.qq.com)过慢处理

问题描述&#xff1a;服务器请求微信后台&#xff08;api.weixin.qq.com&#xff09;的测试结果&#xff0c;每次请求都需要4&#xff0c;5秒甚至更慢。 2016.12.16 16:31:57 start:1481877117.4148 request:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_t…

转行退路?后端开发人员可选12个职业方向

部分数据来源&#xff1a;ChatGPT 引言 后端开发是当前互联网行业非常热门的职业之一&#xff0c;如果你想要开启全新的职业生涯&#xff0c;也可以选择其他的职业。本文将介绍一些适合后端开发人员转行的就业方向&#xff0c;并从就业市场和就业前景角度进行分析。 1. 区块链…

云炬Android开发笔记 7登陆注册功能开发

阅读目录1.注册UI及验证逻辑实现 1.1 布局 1.2 注册信息的验证和逻辑 2.登录UI及验证逻辑实现 2.1 布局 2.2 登录的逻辑框架 3.服务器数据简单介绍 3.1 服务器的数据 3.2 数据端访问的数据 3.3 打印信息的级别类封装 4.与基于GreenDao的数据库框架设计 4.1 添加依赖和配置 …

让 GPT-4 设计一个分布式缓存系统,它从尝试到被“逼疯”!

整理 | 屠敏 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 比 ChatGPT 背后 GPT-3.5 更为强大的模型 GPT-4&#xff0c;已在上周正式对外发布。在 OpenAI 官方发布的 GPT-4 Developer Livestream&#xff08;https://www.youtube.com/watch?voutcGtbnMuQ&#…

增强型语言模型——走向通用智能的道路?!?

增强型语言模型——走向通用智能的道路&#xff1f;&#xff01;&#xff1f; FesianXu 20230325 at Baidu Search Team 前言 继AlphaGo在2016年横扫了人类棋手之后&#xff0c;2022年末的chatGPT横空出世&#xff0c;再次引爆了全球对人工智能技术的热情。基于语言模型的chat…

新版nonebot,go-cqhttp搭建qq机器人保姆级教程

前言 前段时间QQ进行了更新&#xff0c;所以导致了非手表协议扫码登陆报错的问题&#xff0c;不过好在大佬已经推出rc5版本的go-cqhttp&#xff0c;解决了这一头疼的问题 在开始之前&#xff0c;我需要说明一下&#xff0c;本文章是针对没有经验和基础的用户&#xff0c;所以…

文心一言初次体验,说说感觉~

文心一言&#xff0c;被称为国内版ChatGPT&#xff0c;一直被寄予厚望。 在未出来前&#xff0c;网络上都是各种调侃。 甚至还用ChatGPT来调侃。 但是在发布会的时候&#xff0c;感觉李彦宏关于文心一言讲了很多东西。 但是吧&#xff0c;又感觉啥也没讲&#xff0c;说话底气还…

王垠的过去和现状

王垠的过去和现状 转自&#xff1a;http://blog.csdn.net/simoncoder/article/details/49803827 我曾在2006年冬季接触到王垠的三篇文章&#xff1a;《完全用Linux工作》、《写给支持和反对<完全用Linux工作>的人们》、《清华梦的粉碎--写给清华大学的退学申请》&#…

百度副总裁 AIG总负责人王海峰:决胜AI时代(财新专访实录)

12月5日&#xff0c;百度副总裁、AI技术平台体系(AIG)总负责人、兼百度研究院院长王海峰接受了财新专访。 这里是视频地址&#xff08;复制链接打开&#xff09;&#x1f447; http://video.caixin.com/2017-12-05/101180688.html 王海峰表示&#xff0c;人工智能正在成为这个…

王慧文收购国产AI框架OneFlow,为中国版ChatGPT疯狂抢人抢基建

杨净 发自 凹非寺量子位 | 公众号 QbitAI ChatGPT热潮下&#xff0c;人才已经被抢疯了。 现在&#xff0c;国内首起ChatGPT相关收购已经浮出水面。 据多方媒体消息&#xff0c;美团联创王慧文的光年之外与国产AI框架一流科技&#xff08;Oneflow&#xff09;将达成并购意向。 原…

拉开中兴帷幕 张朝阳再造搜狐

对于搜狐而言&#xff0c;如果能在媒体和视频这两条核心主线上走出一条不同的路径&#xff0c;未来持续增长当指日可期。 一点财经 刘书艳&#xff5c;作者 严 睿&#xff5c;编辑 减亏65.38%&#xff0c;营收同比增长6%&#xff0c;2020年Q1财报略超市场预期的搜狐信心满满&a…