批量下载QQ邮箱附件,下载完后修改文件重命名 。MAC用户也可使用。

---

因为工作原因,需要处理QQ邮箱上来自各地网友的投稿附件。数量比较多(上千份),如果手动一个一个下载非常麻烦。。。

而且有些发来的附件命名也不规范,下载下来之后还需要手动去重命名,否则放一起就分不清谁是谁了。还会出现大量重复的命名文件。 这种非常机械化的重复操作,我想写个脚本批量下载QQ邮箱附件。

再网上搜了下资料,基本都是通过POP3来下载,但是这个邮箱并不是自己的,只是临时注册用来接收邮箱的小号,而对方也不希望开通手机认证。

于是临时研究了一下 Python + selenium + Chrome 来模拟手动爬虫~

如何安装

需要先安装几个必要的东西。使用终端安装程序可能需要设置代理,有ss的同学可以开启全局,否则会比较慢。

MAC

如果你是MAC用户。操作相对简单一些:

1. 安装Homebrew https://brew.sh/index_zh-cn

简单的说,就是把下面这段指令复制粘贴到终端(Terminal) 窗口

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

2. 安装 python3 + selenium + chromedriver

简单的说,就是把下面这段指令复制粘贴到终端(Terminal) 窗口

brew install chromedriver
brew install selenium
brew install prettytable

Windows

如果你是Windows用户。你懂的,大家都是这么过来的:

1. Python 3https://www.python.org/

下载最新版即可,跟着引导安装,点页面中那两个带着小盾牌图标的大按钮。
Install Now
Disable path length limit

2. WebDriver for Chromehttps://sites.google.com/a/chromium.org/chromedriver/downloads

注1:根据自己Chrome当前的版本号,下载对应版本号的chromedriver。
比如你浏览器版本号是是80.0,就下载80.0版本。
以后有更新,也是需要重新下载对应版本的chromedriver,否则会报错。注2:如何查看Chrome当前版本号。
右上角 - 设置 - 关于Google Chrome 注3:如何安装
下载好之后,把 chromedriver.exe 放到随意一个文件夹,然后复制当前这个文件夹的路径。
通常我喜欢把这类工具专门放到一个叫bin的文件夹里。比如D:Programbin按下Win键,输入 path ,会看到一个「编辑系统环境变量」,按下回车就能打开它。
右下角有个「环境变量」的按钮,打开。
在下面的「系统变量」列表里,找到「Path」的一行,双击编辑。
右上角有「新建」的按钮,点新建。它会在列表后面新建一行空的,就把刚才的路径粘贴进去就可以了。

3. Nodejshttps://nodejs.org/zh-cn/

下载最新版即可,跟着引导安装,一直点下一步。

PIP

NodeJs安装完成后,按下Win + R,输入 cmd。然后按Ctrl + Shift + 回车键。
以管理员权限进入命令行。接着输入下方的指令

python -m pip install --upgrade pip
pip install selenium
pip install prettytable

繁琐的前置工作完成了。接着可以正式开始咯。


如何使用

前面你已经安装了Python,会发现开始菜单列表多了一个叫IDLE的编辑器。打开Shell窗口后在菜单左上角选择新建文件。【File - New File】
然后把下方的代码复制粘贴到IDLE中,将文件保存在任意位置,随便取个名比如QQmail.py。另外建议不要放桌面,因为后面的脚本会生成一些临时文件,有些占位置。
当你要运行脚本时,只需要在IDLE中按下F5,就可以运行了。

1 调整每页显示邮件数量

邮箱默认只显示25条邮件,需要在邮箱设置里,调整每页显示100封邮件。脚本中也提供了一个自动设置邮件显示数量的开关,默认是关闭的,有需要可以在DEBUG分类下手动开启。

2 调整邮件列表视图(默认模式)

由于我没有开启会话模式去测试,而是使用了默认模式去执行脚本。把邮件列表试图切换回默认的标准模式。在默认模式中,不会合并相同的收件人。

3 邮箱文件夹

把你想要下载的邮件移动到指定文件夹里,方便区分。

4 找到对应的文件夹ID

邮箱主页左侧面板‘我的文件夹’栏目中找到你刚才创建的文件夹,然后右键-新窗口打开。在浏览器地址栏找到网页链接中的几个参数: sid folderid page

https://mail.qq.com/cgi-bin/frame_html?t=frame_html&sid={ A }&url=/cgi-bin/mail_list?folderid={ B }%26page={ C }


更新时间:2020/10/16

# -*- coding: UTF-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import prettytable as pt
import time,os,asyncio#===============================================================================
# * 声明
#===============================================================================
# 作者:XHXIAIEIN
# 更新:2020/10/16
# 主页:https://github.com/XHXIAIEIN/Auto-Download-QQEmail-File
#===============================================================================#===============================================================================
# * 如何安装
#===============================================================================#...............................................................................
#  1.WebDriver for Chrome
#...............................................................................
#  使用前检查Chrome与chromedriver版本是否一致,若Chrome有更新,请更新chromedriver
#  https://sites.google.com/a/chromium.org/chromedriver/downloads
#...............................................................................#...............................................................................
#  2.需要安装几个必要的工具库。
#...............................................................................
#  Windows用户,安装NodeJs后,在cmd输入以下指令:
#  python -m pip install --upgrade pip
#  pip install selenium
#  pip install prettytable
#...............................................................................
#  MAC用户,安装brew后,在终端输入以下指令:
#  brew install chromedriver
#  brew install selenium
#  brew install prettytable
#...............................................................................'''
#===============================================================================
#  自定义参数
#===============================================================================
'''#...............................................................................
#  QQ账号 (放心填,没人能看到)
#...............................................................................QQNUMBER="123456789"
PASSWORD="123456789"#...............................................................................
#  邮箱文件夹ID
#...............................................................................
#  展开左侧面板[我的文件夹]列表,找到你想下载的文件夹,右键-新窗口打开。
#  在浏览器地址栏找到folderid
#  mail.qq.com/cgi-bin/frame_html?t=frame_html&sid=x&url=/cgi-bin/mail_list?folderid={ ??? }%26page=0
#...............................................................................
FOLDER_ID = 123#...............................................................................
# 附件下载到哪个文件夹。
#...............................................................................
#
# 若文件夹不存在,会自动创建。但仅处理1层路径。
#
# 注1:如需在当前脚本所在的路径创建目录,直接写名称,如:"QQMail"
#
# 注2:Win用户文件夹路径用 '' 作为分隔符。如:"d:emaildownload"
#      Mac用户用 '/' 分隔。如:"~/Downloads/2020/"
#
# 注3:文件夹路径必须以分隔符结尾。
#...............................................................................DOWNLOAD_FOLDER='D:Downloads'#...............................................................................
# 配置 Web Driver
#...............................................................................options = webdriver.ChromeOptions()
prefs={"download.default_directory":DOWNLOAD_FOLDER}
options.add_experimental_option("prefs",prefs)
options.add_argument("--window-size=900,1000")
options.add_argument("--user-agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36 Edg/80.0.361.66'")
options.add_argument("--blink-settings=imagesEnabled=false")  # 禁止网页显示图片#...............................................................................
# MAC的用户,有2个地方需要修改。
#...............................................................................# Windows
options.add_argument("--user-data-dir=selenium")
chrome = webdriver.Chrome(options=options)# MAC
# options.add_argument("--user-data-dir selenium")
# chrome = webdriver.Chrome('/usr/local/bin/chromedriver', options=options)#...............................................................................
#  若自动登陆失败,可尝试手动登录。
#...............................................................................
#
#  为什么会失败?
#  登陆时出现了验证码滑块以及手机验证。但浏览器已经禁止显示图片。
#
#  解决方案1:
#  临时注释掉上方 “[禁止网页显示图片]” 的声明。
#  options.add_argument("--blink-settings=imagesEnabled=false")
#  禁用后,再次启动脚本,手动登陆并勾选记住密码,下次自动登陆。
#  登陆成功后,下次运行脚本就可以直接进入邮箱主页了。
#  如果尝试1次不成功,可以多登陆几次。
#
#  解决方案2:
#  可以使用SID手动登录。
#  手动登陆后,从浏览器地址栏中找到这个参数: sid
#  mail.qq.com/cgi-bin/frame_html?sid={ xxxxxx }&r={ 这个可以无视 }
#  这串随机字符偶尔也会以符号作为结尾,复制的时候需要注意。
#...............................................................................
URLTEMP_SID = ""#...............................................................................
#  如果懒得找文件夹ID,也可以用索引序号[替换]文件夹ID。
#  若不使用索引编号,请设置为 -1
#  注:置顶的文件夹不在此列表中
#...............................................................................
FOLDER_INDEX = -1   # 首页收件箱的索引序号是0#...............................................................................
#  高级选项
#...............................................................................# 等待页面加载元素时长。
implicitly_wait_time = 0.08# 只登陆到邮箱主页,不做任何事
just_login_mail = False# 只打印主题列表,不打开邮件
just_print_mail = False# 只打印附件列表,不下载附件
just_print_file = False# 是否将不含附件的主题设置为星标
can_star_nofile = True# 是否在控制台输出数据
can_print_folder_table = True
can_print_title_table = True
can_print_files_table = True# 是否将不含附件的主题,导出eml文件
can_dleml_nofile = False# 下载结束后跳转到首页
can_ended_jump_home = True#生成附件列表csv文件
can_export_filelist_csv = False#生成邮件列表csv文件
can_export_titlelist_csv = True#...............................................................................
# 邮件主题,关键词过滤
#...............................................................................# 白名单关键词。只搜索邮件主题中包含任意一个关键词的邮件。
# 示例:title_whitelist_keys = ['反馈','回复']
title_whitelist_keys = ['']# 黑名单关键词。忽略邮件主题中包含任意一个关键词的邮件。
# 示例:title_blacklist_keys = ['发信方已撤回邮件','QQ会员业务通知邮件']
title_blacklist_keys = ['发信方已撤回邮件']#...............................................................................
# 指定下载计划。
#  start: 从列表第n个开始。(包含n,即列表第一个就是n。)默认值:1
#  end:   在列表第n个结束。(包含n,即列表最后一个是n。)默认值:-1
#  step:  从开始计算,累计n个结束。(即列表最终有n个。若index大于end或max,提前结束step。)默认值:-1
#...............................................................................# 邮件列表
Title_Task = { 'start':1, 'step':-1, 'end':-1 }# 翻页规则
Pages_Task = { 'start':1, 'step':-1, 'end':-1, 'autoNext': True }'''
#===============================================================================
#                  "请 勿 跨 过 这 块 区 域 修 改 内 容"
#===============================================================================
'''#...............................................................................
# GLOBAL VAR
#...............................................................................config = {}
config['PTASK'] = ''
config['TTASK'] = ''
config['TOKEN'] = {'sid':"",  'folderid':0, 'page':0}
config['PAGES'] = {'index':0, 'max': 0, 'step':0, 'iscanNext':False, 'isNotFistPage':0}
config['TITLE'] = {'index':0, 'max': 0, 'step':0, 'isFileDownload': False}
count_download_email = {"count":0, "lastMailID": ""}#...............................................................................
# Data
#...............................................................................data_folders_list = []
data_folders_dict = []
data_title_blacklist = []
data_title_whitelist = []
data_email_titlelist = []
data_email_fileslist = []
data_email_nofilelist = []#...............................................................................
# Table
#...............................................................................table_title_whitelist = pt.PrettyTable()
table_title_whitelist.field_names = ["序","发件人","没有包含白名单关键词的主题","邮箱","时间","页"]
table_title_whitelist.align = "l"table_title_blacklist = pt.PrettyTable()
table_title_blacklist.field_names = ["序","发件人","包含了黑名单关键词的主题","邮箱","时间","页"]
table_title_blacklist.align = "l"table_email_titlelist = pt.PrettyTable()
table_email_titlelist.field_names = ["序","发件人","主题","邮箱","时间","页"]
table_email_titlelist.align = "l"table_email_nofilelist = pt.PrettyTable()
table_email_nofilelist.field_names = ["序","发件人","没有附件的主题","邮箱","时间","页"]
table_email_nofilelist.align = "l"table_email_fileslist = pt.PrettyTable()
table_email_fileslist.field_names = ["标序","页","发件人","文件名","主题","邮箱","大小","类型","时间","附序"]
table_email_fileslist.align = "l"#...............................................................................
# Tool
#...............................................................................def p1(text):try:print(text)except: print("控制台打印了一些内容,但因为字符中含有某些特殊符号,无法显示。")# 打开链接
def get_url(url):chrome.get(url)chrome.implicitly_wait(implicitly_wait_time)# 检查SID是否存在,若存在则跳过自动填写账号密码登陆
def test_sid_Valid():return bool(URLTEMP_SID)# 检查页面元素是否存在
def test_id_Valid(name):try: return chrome.find_element_by_id(name) != 'null'except: return False# 检查页面元素Class是否存在
def test_class_Valid(name):try:e=chrome.find_elements_by_class_name(name);chrome.execute_script("window.stop()"); return len(e) > 0 except: return False# 检查页面元素ID是否存在。如果存在则返回元素
def test_id_Valid_get(name):try: e=chrome.find_element_by_id(name); chrome.execute_script("window.stop()"); return eexcept:return False# 检查页面元素Class是否存在。如果存在则返回元素
def test_class_Valid_get(name):try:e=chrome.find_elements_by_class_name(name); chrome.execute_script("window.stop()"); return eexcept:return False# 检查iframe框架是否存在,如果存在将焦点跳转到该iframe
def test_frame_Valid(name):try: e=test_id_Valid_get(name); chrome.switch_to.frame(e); return eexcept:return False# 检查列表是否为空
def test_list_Valid(i):return bool(i) and i != ['']# 如果字符串中含有某个关键词,返回True
def check_key_in_name(str, key):return all([i in str for i in key])# 时间戳转换时间
def timeStamp(t):return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(t/1000)))# 检查文件夹路径是否存在(仅检查1层)
def check_download_Valid():if not os.path.exists(DOWNLOAD_FOLDER):print_folder_exists();os.mkdir(DOWNLOAD_FOLDER)# 反转字典, value to key
def swapDict(d):result = {}for k, v in d.items():for _k in v:result.setdefault(_k, {})result[_k][k] = d[k][_k]return result# 将Table导出为csv文件
def ptable_to_csv(table, filename):raw = table.get_string()data = [tuple(filter(None, map(str.strip, splitline)))for line in raw.splitlines()for splitline in [line.split('|')] if len(splitline) > 1]with codecs.open(filename,'w','utf_8_sig') as f: for d in data: f.write('{}n'.format(','.join(d)))#...............................................................................
# function
#...............................................................................# 检查id元素是否存在
def test_id_Valid(name):try: return chrome.find_element_by_id(name)except: pass; # print("无法获id元素: {} n".format(name))# 检查class元素是否存在。如果指定了min,则检查class元素的数量。
def test_class_Valid(name, min=0):try: return len(chrome.find_elements_by_class_name(name)) > minexcept: pass; #print("无法获取class元素: {} n".format(name))# 检查iframe框架是否存在,如果存在将焦点跳转到该iframe
def test_frame_Valid(name):try: e = test_id_Valid(name)chrome.switch_to.frame(e)return eexcept:print("无法获取iframe元素: {} n".format(name))# 更新token
def update_token():token = {}token["sid"]      =  URLTEMP_SID if bool(URLTEMP_SID) else chrome.current_url.split("sid=")[1].split("&")[0]token["folderid"] =  FOLDER_IDtoken["page"]     =  0#...............................................................................
#  GET FOLDER LIST
#...............................................................................# 获取文件夹列表
def get_folder_list():if not test_id_Valid("leftPanel") and not test_id_Valid("folders"): error_load_folder()n,d=0,[]elements = iter(chrome.find_element_by_id("personalfolders").find_elements_by_tag_name("li"))for item in elements:n += 1a = item.find_elements_by_tag_name("a")[0]aid = a.get_attribute('id').split('_')[1]atl = a.get_attribute('title')data_folders_list.append({'index':n, 'id': int(aid), 'name': atl})d.append([aid,atl])global data_folders_dictdata_folders_dict = dict(d)print_folder_table()#...............................................................................
# GET EMAIL LIST
#...............................................................................# 进入文件夹
def open_next_page():token = config['TOKEN']get_url("https://mail.qq.com/cgi-bin/mail_list?folderid={}&page={}&sid={}&nocheckframe=true".format(token['folderid'],token['page'],token['sid']))get_folder_info()get_email_title()# 获取文件夹页数信息
def get_folder_info():test_frame_Valid("mainFrame")token = config['TOKEN']token['page'] += 1config["PAGES"]["step"] += 1config["PAGES"]["isNotFistPage"] = 1 if token['page'] != 1 else 0config["PAGES"]["iscanNext"] = test_id_Valid("nextpage")config["PAGES"]["index"] = token['page']config["PAGES"]["max"] = eval(test_class_Valid_get("right")[1].find_elements_by_tag_name("script")[0+config["PAGES"]["isNotFistPage"]].get_attribute('innerHTML').strip('document.write(').strip(');'))if token['page'] > Pages_Task["start"] > 0: returnos.system("cls")# 获取文件夹的邮件列表
def get_email_title():data1,data2=[],[]# 基础信息elements=iter(chrome.find_elements_by_css_selector('input[name="mailid"]'))for index,e in enumerate(elements):if index < 1: continueconfig["TITLE"]["max"] += 1mail={}mail.update({"page":"{}".format(config["PAGES"]["step"])})mail.update({"index":"{:03d}".format(config["TITLE"]["max"])})mail.update({"email":e.get_attribute("fa")})mail.update({"name":e.get_attribute("fn")})mail.update({'timestamp':e.get_attribute("totime")})mail.update({"mailid":e.get_attribute("value")})data1.append(mail)# 邮件标题elements = iter(test_class_Valid_get("tt"))for e in elements: data2.append({"title": e.get_attribute('innerHTML').replace('&nbsp;','')})# 合并两个list# 在这里处理白名单、黑名单。以及 TASK TITLEfor a,b in zip(data1, data2):a.update(b)if test_list_Valid(title_blacklist_keys) and check_key_in_name(a['title'],title_blacklist_keys): data_title_blacklist.append(a)elif test_list_Valid(title_whitelist_keys) and not check_key_in_name(a['title'],title_whitelist_keys): data_title_whitelist.append(a)else:if int(a['index']) > Title_Task["end"] > 0:breakif config["TITLE"]["step"] >= Title_Task["step"] > 0:breakif Title_Task["start"] > int(a['index']) > 0 : continueconfig["TITLE"]["step"]+=1data_email_titlelist.append(a)# 检查翻页open_next_page() if check_page_can_next() else check_task_end_type()#...............................................................................
# NEXT PAGE
#...............................................................................#检查是否需要翻页
def check_page_can_next():if not Pages_Task['autoNext'] or not config["PAGES"]["iscanNext"]: return Falseif config['TOKEN']['page'] > Pages_Task["end"] > 0: config['PTASK']='e';return Falseif config["PAGES"]["step"] > Pages_Task["step"] > 0: config['PTASK']='s';return Falseif config['TITLE']['index'] > Title_Task["end"] > 0: config['TTASK']='e';return Falseif config['TITLE']['step'] >= Title_Task["step"] > 0: config['TTASK']='s';return Falsereturn True#检查时以哪种方式结束翻页的
def check_task_end_type():p,t = config['PTASK'],config['TTASK']tp,tt,cp,cd = Pages_Task,Title_Task,config["PAGES"],config["TITLE"]if not tp['autoNext'] and (tp["step"] > 0 or tt["step"] > 0 or tp["end"] > 0 or tt["end"]) > 0: stop_by_page_next()tp["start"] = tp["start"] if tp["start"] > 0 else 1tt["start"] = tt["start"] if tt["start"] > 0 else 1tp["end"] = tp["end"] if tp["end"] > 0 else cp["max"]tt["end"] = tt["end"] if tt["end"] > 0 else len(data_email_titlelist)tp["step"] = tp["step"] if tp["step"] > 0 else cp["step"]tt["step"] = tt["step"] if tt["step"] > 0 else cd["step"]stop_by_page_end() if p == 'e' else stop_by_page_step()stop_by_title_end() if t == 'e' else stop_by_title_step()if can_print_title_table : print_title_table()#...............................................................................
#  GET EMAIL FILES
#...............................................................................# 打开邮件
def open_email():titlelist = data_email_titlelist.__iter__()max = len(data_email_titlelist)check_download_Valid()  #检查下载路径是否存在while True:try:if count_download_email["count"] >= max: download_end(); breakemail = next(titlelist)count_download_email["count"] += 1get_url("https://mail.qq.com/cgi-bin/frame_html?t=newwin_frame&sid={}&url=/cgi-bin/readmail?t=readmail%26mailid={}%26mode=pre".format(config['TOKEN']["sid"],email['mailid']))test_frame_Valid("mainFrame")check_frame_timeout(email['index']) # 您请求的频率太快,请稍后再试elements=test_class_Valid_get("ico_big")elements2=test_class_Valid_get("down_big")check_frame_timeout(email['index'])#附件列表if len(elements) <= 0:try:p1("{} 没有邮件".format(email['index']))data_email_nofilelist.append(email)#下载eml文件if can_dleml_nofile:ActionChains(chrome).click(test_id_Valid_get("aSwitchOption")).perform(); ActionChains(chrome).click(test_id_Valid_get("trOption").find_elements_by_tag_name("a")[2]).perform();#设置为星标if can_star_nofile:mark_star=test_id_Valid_get("img_star");if test_class_Valid('qm_ico_flagoff'): mark_star.send_keys(Keys.SPACE); continueexcept:check_frame_timeout(email['index'])check_frame_timeout(email['index'])#整理附件信息,并下载附件for f in elements:try: a = f.find_elements_by_tag_name('a')[0]attach={}attach.update({'filename': a.get_attribute('filename')})attach.update({'filebyte': int(a.get_attribute('filebyte'))})attach.update({'filedown': "https5://mail.qq.com" + a.get_attribute('down')})attach.update({'viewmode': a.get_attribute('viewmode')})attach.update({'index': int(a.get_attribute('idx') or 0)})attach.update({'ti': email['index']})attach.update({'tn': email['name']})attach.update({'tt': email['title']})attach.update({'page': email['page']})attach.update({'email': email['email']})attach.update({'timestamp': email['timestamp']})data_email_fileslist.append(attach)p1("{} {}".format(email['index'],attach['filename'],email['email']))if just_print_file: continuedownlnk = elements2[attach['index']].find_elements_by_link_text('下载')[0]ActionChains(chrome).click(downlnk).perform()except:check_frame_timeout(email['index'])p1("")except:check_frame_timeout(email['index'])# 检测“您请求的频率太快,请稍后再试”
def check_frame_timeout(index):pause = test_class_Valid("errorIcon") or test_id_Valid("msg_txt") or not test_id_Valid("contentDiv")if pause:p1("n{} 正在队列中等待...请稍等30秒".format(index))wait = 0while test_id_Valid("msg_txt"):if wait == 0: time.sleep(3)elif wait == 1: time.sleep(2)elif wait == 2: time.sleep(5);chrome.refresh()elif wait%3 == 0: time.sleep(3);chrome.refresh()else:time.sleep(1)test_frame_Valid("mainFrame")if not test_id_Valid("msg_txt"): breakelse: wait+=1test_frame_Valid("mainFrame")pause = Falsep1("{} 等待结束,任务继续。n".format(index))#...............................................................................
# Print Console Log
#...............................................................................def error_qlogin(): p1("登录失败。请尝试登录QQ客户端后重试")
def error_load_page(): p1("打开文件夹失败。")
def error_load_title(): p1("获取邮件列表失败。")
def error_load_folder(): p1("获取文件夹列表失败。")
def error_seting_email(): p1("设置失败。")def stop_by_title_step(): p1("[TITLE STEP]从第{}封邮件开始,读取{}封邮件后结束。".format(Title_Task['start'],Title_Task['step']))
def stop_by_page_step(): p1("[PAGES STEP]从第{}页开始,读取{}页后结束。".format(Pages_Task['start'],Pages_Task['step']))
def stop_by_page_next(): p1("[PAGES NEXT]由于关闭了自动翻页,提前结束。")
def stop_by_page_end(): p1("[Task PAGE]从第{}页开始,在第{}页结束。".format(Pages_Task['start'],Pages_Task['end']))
def stop_by_title_end(): p1("[Task TITLE]从第{}封邮件开始,在第{}封邮件结束。".format(Title_Task['start'],Title_Task['end']))def print_folder_exists(): p1("文件夹不存在。正在自动创建文件夹....")
def print_download_end(): p1("结束了。{}/{}".format(count_download_email["count"], len(data_email_titlelist)))#...............................................................................
# Print TABLE
#...............................................................................# 打印文件夹表格
def print_folder_table():if not can_print_folder_table: returntb0 = pt.PrettyTable()tb0.field_names = ["序", "id", "文件夹"]tb0.align = "l"tb0.reversesort=Truefor a in data_folders_list: tb0.add_row(["%02d"%a['index'], a['id'], a['name']])p1(tb0)# 打印邮件标题表格
def print_title_table():# 黑名单if bool(data_title_blacklist):tb1 = table_title_blacklistfor a in (data_title_blacklist): tb1.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb1); p1("设置了黑名单关键词,以上{}封邮件不包含在最终列表中。nn".format(len(data_title_blacklist)))# 白名单if bool(data_title_whitelist):tb2 = table_title_whitelistfor a in (data_title_whitelist): tb2.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb2); p1("设置了白名单关键词,以上{}封邮件不包含在最终列表中。nn".format(len(data_title_whitelist)))# 最终列表if bool(data_email_titlelist):tb = table_email_titlelistfor a in (data_email_titlelist): tb.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb);p1("最终列表共有{}封邮件。n".format(len(data_email_titlelist)))# 打印附件列表
def print_files_table():# 没有附件的邮件列表if bool(data_email_nofilelist):tb1 = table_email_nofilelistfor a in (data_email_nofilelist): tb1.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb1); p1("有{}封邮件没有附件的主题,已标记为星标邮件。nn".format(len(data_email_nofilelist)))# 附件列表if bool(data_email_fileslist):tb = table_email_fileslistfor a in (data_email_fileslist): tb.add_row([a['ti'],a['page'],a['tn'],a['filename'],a['tt'],a['email'],a['filebyte'],a['viewmode'],timeStamp(int(a['timestamp'])),a['index']])p1(tb);p1("当前的附件列表,已统计{}个文件。n".format(len(data_email_fileslist)))#...............................................................................
# Ended Event
#...............................................................................def download_end():if can_print_files_table: print_files_table()if is_export_filelist_csv: ptable_to_csv(table_email_fileslist, DOWNLOAD_FOLDER+ r"_导出附件列表_" + now + r".csv")if is_ended_jump_home: get_url("https://mail.qq.com/")print_download_end()#...............................................................................
# Ended Event
#...............................................................................def download_end():if can_print_files_table: print_files_table()if can_export_filelist_csv: ptable_to_csv(table_email_fileslist, DOWNLOAD_FOLDER+ r"_导出附件列表_" + now + r".csv")if can_ended_jump_home: get_url("https://mail.qq.com/")print_download_end()#...............................................................................
# login
#...............................................................................# 更新token
def update_token():token = config['TOKEN']token["sid"]      =  URLTEMP_SID if bool(URLTEMP_SID) else chrome.current_url.split("sid=")[1].split("&")[0]token["folderid"] =  data_folders_list[FOLDER_INDEX]["id"] if FOLDER_INDEX > 0 else 0 if FOLDER_INDEX == 0 else FOLDER_ID if FOLDER_ID > 0 else 0token["page"]     =  0async def auto_login():test_frame_Valid("login_frame")if test_class_Valid('face', 1): test_id_Valid("switcher_plogin").click()test_id_Valid("u").clear()test_id_Valid("u").send_keys(QQNUMBER)test_id_Valid("p").clear()test_id_Valid("p").send_keys(PASSWORD)test_id_Valid("p_low_login_enable").click()test_id_Valid("login_button").click()await asyncio.sleep(2)if test_id_Valid("tcaptcha_iframe"):trigger_once = Truewhile bool(test_id_Valid("tcaptcha_iframe")):if trigger_once: print("等待用户手动完成拼图认证..."); trigger_once = False;await asyncio.sleep(3)if test_id_Valid("login_frame"):trigger_once = Truewhile bool(test_id_Valid("login_frame")):if trigger_once: print("等待用户完成登陆验证..."); trigger_once = False;await asyncio.sleep(3)if test_id_Valid("mainFrame"): update_token()print("登陆完成!")#...............................................................................
# main
#...............................................................................async def main():chrome.get("https://mail.qq.com")chrome.implicitly_wait(2)# 是否需要登陆if not test_id_Valid("mainFrame"): await asyncio.create_task(auto_login())print(f'sid: { chrome.current_url.split("sid=")[1].split("&")[0] }')#获取文件夹列表if not bool(data_folders_list): get_folder_list()if not bool(config['TOKEN']["sid"]): update_token()# 进入文件夹,获取邮件列表if just_login_mail: p1("nsid: n{}".format(config['TOKEN']["sid"])); p1("n如果要继续打开文件夹")os.system("PAUSE")os.system('cls')open_next_page()# 打开邮件,获取附件列表if just_print_mail:if can_export_titlelist_csv: ptable_to_csv(table_email_titlelist, DOWNLOAD_FOLDER+ r"_导出邮件列表" + now + r".csv")p1("如果要继续下载附件")os.system("PAUSE")os.system('cls')open_email()asyncio.run(main())
print("运行结束")

特性

特性
- 自定义附件的下载路径
- 自动翻页
- 填写账号密码,自动登录。
- 若自动登录失败,可手动登录,再根据SID密钥记住登录。
- 自定义邮件标题的白名单或黑名单关键词,过滤某些邮件名。
- 自定义从第几封邮件开始,第几封邮件结束,处理多少封邮件后结束。
- 自定义从文件夹第几页开始,到第几页结束,只处理多少页的任务计划。
- 可以自动修改每页显示数量为100封,需手动开启。
- 如果邮件没有包含附件,可以导出eml文件,需手动开启。
- 如果邮件没有包含附件,会自动打星标。
- 打开邮箱时,可以输出所有文件夹列表,方便到文件夹ID。
- 脚本结束后会生成csv文件,包含所有附件列表信息。
- 脚本结束后会生成csv文件,包含所有附件列表信息。
- 如果浏览器请求速度过快,脚本会自动刷新页面,等待恢复正常可能出现的BUG
1. 如果网络不稳定。附件的预览图如果没有加载出来,脚本可能会卡住。
(已修复。解决方案:以不加载图片的模式启动浏览器)2. 如果窗口太小,可能获取不到页面元素,然后报错。
(已修复。解决方案:在启动浏览器时调整窗口大小至合适的值,主要是高度)3. 如果开车的速度过快,会被系统拦下车。提示:【您请求的频率太快,请稍后再试】
(已修复。当出现提示窗口时,脚本自动会等待10秒,并自动反复刷新,直到恢复暂停的地方继续下载)4. 如果电脑打开了QQ,登陆页面会出现手机扫码登陆的提示,导致自动填写账号失败。
(已修复。自动检测是否出现扫码提示,并切换到账号密码登陆。)5. 因不明原因,有几率在登录阶段需要安全验证,滑动拼图滑块。导致无法自动登录
(已修复。解决方案有些麻烦。需手动登录,并在脚本处手动关闭[拦截浏览器显示图片]配置,登陆后需手动记录SID密钥。关闭脚本再次运行脚本,即可自动登录。)6. 收件人含有特殊符号,导致脚本奔溃。
(事实上,我并没有对字符串做处理,只是将他们输出在控制台而已。报错是因为编码问题,你可以将打印到控制台中的方法全部禁用掉,直接下载附件就行)7. 本地重命名的批处理脚本,如果附件有重复的文件,后面的相同的文件不会被重命名。
(没想好。临时解决方案:根据输出的记录,搜索文件名,找到发件人昵称或者邮箱,手动重命名)踩坑历史
1. 附件收藏中的"全部附件",并不是想象中真的把全部附件整合在一起,偶尔还是会漏掉一些。(已解决。换成进入邮件主题模拟手动下载附件)2. 在下载过程中,中途收到了新的邮件,列表顺序会出现错误。(不要下载收件箱的邮件,建议将他们移动到文件夹里下载,并关闭该文件夹的收件规则,下载期间的别乱动。)


附赠脚本

读取文件夹里的文件,匹配文件名关键词,然后移动到相应的文件夹里。

import os
import shutil
import fnmatchdef find_key(key,path):for n in os.listdir(os.getcwd()):if fnmatch.fnmatch(n, key):print('{}:{}'.format(key,n))shutil.move(n,path)def checkfile():all_md5 = {}filedir = os.walk(os.getcwd())for i in filedir:for tlie in i[2]:if md5sum(tlie) in all_md5.values():print('- {}'.format(tlie))shutil.move(tlie,'md5')#os.remove(tlie)else:all_md5[tlie] = md5sum(tlie)if __name__ == '__main__':# 新建文件夹# 提前新建好需要分类的文件夹os.mkdir('psd')os.mkdir('图片')os.mkdir('反馈')os.mkdir('VIP')# 匹配关键词# 文件格式来过滤:比如将.jpg的文件移动到‘图片’文件夹。find_key('*.psd','psd')find_key('*.PSD','psd')find_key('*.jpg','图片')find_key('*.png','图片')# 关键词过滤:比如将含有‘知乎’的文件名移动到'反馈'目录find_key('*知乎*.*','反馈')find_key('*会员*.*','VIP')

END


常见问题解决方案:

  1. 运行脚本后马上闪退。(明明前几天还可以用的)
控制台报错信息:
Message: session not created: This version of ChromeDriver only supports Chrome version 80

最后一句提示很明显了,说明 chromedriver 的版本和浏览器的版本不一致。
去官网重新下载一个新版chromedriver,替换掉之前的

2. 运行脚本过程中,页面提示"您请求的频率太快,请稍后再试"

目前脚本会自动处理"您请求的频率太快,请稍后再试" 的情况,您只需要耐心等待即可。 大约等待1-2分钟,脚本会自动刷新页面,并从上次中断的地方继续。

3. 在邮箱账号登陆页面,发生错误:

控制台报错信息:
Message: element not interactable

这句报错信息为[网页元素无法被交互],说明触发了安全验证,需要滑块解锁。

解决方案:1. 临时关闭脚本[配置Web Driver] 处的[禁用网页显示图片]。(即注释掉这句代码)# options.add_argument("--blink-settings=imagesEnabled=false")2. 重新运行脚本,手动填写账号密码登陆,勾选记住密码。3. 登陆后从浏览器地址栏中找到这个参数: sid https://mail.qq.com/cgi-bin/frame_html?sid={ xxxxxx }&r={ 这个可以无视 }4. 将sid的这串字符串密钥,填写到 URLTEMP_SID = xxx 的位置,并重新打开[禁用网页显示图片]5. 重新运行脚本。

4. 运行脚本过程中,页面提示"503"

5. 无法下载163邮箱发来的附件

毕竟这是QQ邮箱,怎么会允许其他邮箱发来附件呢?因此你看到其他邮箱的附件下载地址,都是被屏蔽的。

b49223f1-f712-eb11-8da9-e4434bdf6706.png

解决方案:

脚本会将不包含QQ邮箱附件的邮件,标记星标。
你可以进入星标邮件列表,找到163的邮件,[右键-预览] 即可看到下载地址。

b69223f1-f712-eb11-8da9-e4434bdf6706.png

每个附件都会跳转到单独的页面下载。(如果10个附件就是要打开10个页面)

b79223f1-f712-eb11-8da9-e4434bdf6706.png

或者你可以进入[邮箱设置 - 收件规则],将 [@http://163.com] 发件域的邮件移动到文件夹中,方便找。

ba9223f1-f712-eb11-8da9-e4434bdf6706.png

当然,最简单的办法就是,自动回复提醒发件人,让他使用QQ邮箱重新投稿。:)

chrome控制台如何把vw显示成px_Python + selenium + Chrome 模拟登陆QQ邮箱,批量下载附件,本地重命名相关推荐

  1. chrome控制台如何把vw显示成px_Chrome 开发者工具的11 个高级使用技巧

    作者 | bitfish  译者 | 王坤祥 策划 | 蔡芳芳 来源 | 前端之巅 本文作者从开发者角度出发,介绍了几个关于 Chrome 开发者工具的高级用法.熟练使用这些高级用法可以大大地提高你的 ...

  2. chrome控制台如何把vw显示成px_罗技lua怎么做到在脚本控制台显示中文的? - 『悬赏问答区』 - 吾爱破解 - LCG...

    本帖最后由 浅望 于 2020-10-24 22:57 编辑 之前看了@Ganlv 大佬的一系列lua解密教程,然后最近在悬赏求助区看到了一个帖子悬赏求助帖 下载这个帖子里的宏文件,运行发现加了时间锁 ...

  3. chrome控制台如何把vw显示成px_chrome用不好,BUG改到老,这些chrome 浏览器使用技巧你需要掌握...

    前端工程师大部分工作成果需要在浏览器中查看,使用 chrome 浏览器的频率非常高.更好更有效率地使用 chrome,将 chrome 配置成趁手的浏览器,将极大提升编程效率.现将chrome浏览器的 ...

  4. chrome控制台如何把vw显示成px_【CSS】rem,em,px的区别和使用场景

    前端潮咖 点击上面蓝字,关注我们! 关注 关注前端潮咖,每日精选好文 作者:大前端小菜鸟 来源:cnblogs.com/hyns/p/12380944.html 作rem布局原理深度理解(以及em/v ...

  5. chrome控制台如何把vw显示成px_你可能不知道的chrome调试技巧

    本文是对常用的chrome调试技巧进行总结整理,如果你没有深入了解过chrome调试工具,此处总有你不知道的惊喜! 从 Chrome 说起 对于大部分人来说,Chrome 可能只是个浏览器,但是对于开 ...

  6. chrome控制台如何把vw显示成px_【CSS】679 rem,em,px的区别和使用场景

    作者:大前端小菜鸟来源: cnblogs.com/hyns/p/12380944.html 作rem布局原理深度理解(以及em/vw/vh) 一.前言 我们h5项目终端适配采用的是淘宝那套<Fl ...

  7. chrome frame节点 取_爬虫3-下(利用Selenium + Chrome Driver模拟用户操作浏览器)

    一.前言 前面利用request的方法爬取页面数据的操作,今天用另外一种方法:利用Selenium + Chrome Driver模拟用户操作浏览器,来爬取数据. 在此之前需要做一些准备工作:安装se ...

  8. java中文本框显示在命令按钮后面_Maya中Pymel写个带界面的重命名工具(一)

    创建窗口 pymel.core.windows.window() 此命令用来创建一个窗口,但不会显示窗口 显示窗口 pymel.core.windows.showWindow() 此命令用来显示指定窗 ...

  9. img绝对路径图片显示_使用python爬虫去风景图片网站批量下载图片

    使用python爬虫(requests,BeautifulSoup)去风景图片网站批量下载图片 1.写代码背景: 今天闲来无事,想弄点图片放到电脑,方便以后使用,故去百度查找一些风景图片网站,发现图片 ...

  10. qq邮箱显示服务器连接错误,为什么我的QQ邮箱显示网络错误

    解决步骤是: 1.首先给自己发一封邮件,如果发信正常并且能够收到邮件,说明一切正常,没有问题. 2.检查邮件过滤设置.在邮箱页面右上角点击"设置",打开设置页面,点击"过 ...

最新文章

  1. 关于Outlook筛选的问题
  2. LINUX CentOS7安装字体库
  3. 计算机专业考研可以转专业不,学术可以转专业吗考研
  4. 【网工必备】网络端口号大全......
  5. omct问题之-webapps下多出的ROOT目录
  6. 使用diskpart命令修复U盘分区
  7. python是谁维护的_Python 库从开发到维护
  8. 如何用python批量下载数据_使用Python批量下载数据
  9. 如何使用busybox编译和生成最简linux根文件系统(rootfs)
  10. 巧用Java8中的Stream,让集合操作6到飞起!!!
  11. 【clickhouse】clickhouse 分区表
  12. django图片上传到oss_从攻防角度看oss安全(二)
  13. 万分之二用百分之怎么表示_怎么腐熟猪粪做有机肥
  14. 函数指针作为函数參数,实现冒泡排序的升序排序和降序排序
  15. 卡尔曼滤波(Kalman filtering)算法学习小记
  16. Docker离线安装
  17. 山东大学——结算中的贸易单据
  18. 女神节:向女性程序员致敬!
  19. 更新 TKK 失败,请检查网络连接 报错处理
  20. nvm介绍及常用命令

热门文章

  1. 高盛报告:长远看中国网游前景不如门户
  2. linux游戏掌机,一起来猎奇:窄众平板/游戏掌机推荐
  3. Circular Coloring
  4. 推导全部勾股数方法(转)
  5. 恋爱测试题测男生软件,男友求生欲测试题大全
  6. 计算机专业实训指导老师评语,计算机教师指导评语
  7. Valine-实现QQ邮箱识别生成头像地址(完美解决头像问题)
  8. HttpServletResponse
  9. RobotStudio知识你知多少?
  10. MSSQL_8 操作结果集