nodejs 查看下载文件路径_Python + selenium + Chrome 模拟登陆QQ邮箱,批量下载附件,本地重命名

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

---

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

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

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

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


如何安装

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

MAC

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

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

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

/bin/bash -c "$(curl -fsSL )"

2. 安装 python3 + selenium + chromedriver

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

brew install chromedriver
brew install selenium
brew install prettytable

Windows

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

1. Python 3python.org/

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

2. WebDriver for Chromesites.google.com/a/chro

注1:根据自己Chrome当前的版本号,下载对应版本号的chromedriver。
比如你浏览器版本号是是,就下载版本。
以后有更新,也是需要重新下载对应版本的chromedriver,否则会报错。

注2:如何查看Chrome当前版本号。
右上角 - 设置 - 关于Google Chrome 

注3:如何安装
下载好之后,把  放到随意一个文件夹,然后复制当前这个文件夹的路径。
通常我喜欢把这类工具专门放到一个叫bin的文件夹里。比如D:\Program\bin

按下Win键,输入 path ,会看到一个「编辑系统环境变量」,按下回车就能打开它。
右下角有个「环境变量」的按钮,打开。
在下面的「系统变量」列表里,找到「Path」的一行,双击编辑。
右上角有「新建」的按钮,点新建。它会在列表后面新建一行空的,就把刚才的路径粘贴进去就可以了。

3. Nodejsnodejs.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中,将文件保存在任意位置,随便取个名比如。另外建议不要放桌面,因为后面的脚本会生成一些临时文件,有些占位置。

当你要运行脚本时,只需要在IDLE中按下F5,就可以运行了。

1 调整每页显示邮件数量

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

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

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

3 邮箱文件夹

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

4 找到对应的文件夹ID

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

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

更新时间:2020/10/22

# -*- coding: UTF-8 -*-fromseleniumimportwebdriverfromselenium.webdriver.common.keysimportKeysfromselenium.webdriver.common.action_chainsimportActionChainsimportprettytableasptimporttime,os,re,codecs,random#===============================================================================# * 声明#===============================================================================# 作者:XHXIAIEIN# 更新:2020/10/22# 主页: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="132465798"PASSWORD="132465798"#...............................................................................#  邮箱文件夹ID#...............................................................................#  展开左侧面板[我的文件夹]列表,找到你想下载的文件夹,右键-新窗口打开。#  在浏览器地址栏找到folderid #  { A }%26page=0#...............................................................................FOLDER_ID=123#...............................................................................# 附件下载到哪个文件夹。#...............................................................................# # 若文件夹不存在,会自动创建。但仅处理1层路径。## 注1:如需在当前脚本所在的路径创建目录,直接写名称,如:"QQMail"## 注2:Win用户文件夹路径用 '\\' 作为分隔符。如:"d:\\email\\download\\"#      Mac用户用 '/' 分隔。如:"~/Downloads/2020/"## 注3:文件夹路径必须以分隔符结尾。#...............................................................................DOWNLOAD_FOLDER='D:\\Downloads\\'#...............................................................................# 配置 Web Driver#...............................................................................options=webdriver.ChromeOptions()prefs={"":DOWNLOAD_FOLDER}options.add_experimental_option("prefs",prefs)options.add_argument("--window-size=900,1000")options.add_argument("--user-agent='Mozilla/ (Windows NT 10.0; Win64; x64) AppleWebKit/ (KHTML, like Gecko) Chrome/.3987.132 Safari/ Edg/.361.66'")options.add_argument("--blink-settings=imagesEnabled=false")# 禁止网页显示图片#...............................................................................# MAC的用户,有2个地方需要修改。#...............................................................................# Windowsoptions.add_argument("--user-data-dir=selenium")chrome=webdriver.Chrome(options=options)# MAC# ("--user-data-dir selenium")# chrome = ('/usr/local/bin/chromedriver', options=options)#...............................................................................#  若自动登陆失败,可尝试手动登录。#...............................................................................#  #  为什么会失败? #  登陆时出现了验证码滑块以及手机验证。但浏览器已经禁止显示图片。#  #  解决方案1:#  临时注释掉上方的 “[禁止网页显示图片]” 声明。#  ("--blink-settings=imagesEnabled=false")#  禁用后,再次启动脚本,手动登陆并勾选记住密码,下次自动登陆。#  登陆成功后,下次运行脚本就可以直接进入邮箱主页了。#  如果尝试1次不成功,可以多登陆几次。# #  解决方案2:#  可以使用SID手动登录。#  手动登陆后,从浏览器地址栏中找到这个参数: sid #  { xxxxxx }&r={ 这个可以无视 }#  这串随机字符偶尔也会以符号作为结尾,复制的时候需要注意。###  解决方案3:#  可以利用Cookies登陆。#  如果你某个浏览器已经可以不需要填写密码自动登陆。#  按下F12,打开 DevTools 窗口。上面切换到 Application 标签,左边找到 Cookies #  展开,打开第一个 ,找到 pcache 字段#  复制 pcache 以及它后面的值。以相同的方式复制到另一个浏览器就可以自动登陆了。#  步骤1:添加 Cookies 的方式是双击空白处,即可添加新字段,填入 pcache#  步骤2:将它的值粘贴进入。#  步骤3:刷新页面,就自动进入主页了。#...............................................................................URLTEMP_SID=""#...............................................................................#  如果懒得找文件夹ID,也可以用索引序号[替换]文件夹ID。#  若不使用索引编号,请设置为 -1#  注:置顶的文件夹不在此列表中#...............................................................................FOLDER_INDEX=-1# 首页收件箱的索引序号是0#...............................................................................#  高级选项#...............................................................................# 等待页面加载元素时长。# 减小数值可加快处理速度,但更容易翻车。你也可以尝试,真的很快乐。implicitly_wait_time=5# 是否自动设置【每页显示100封邮件】以及【邮件列表视图为标准模式】can_setting_mail=False# 是否只登陆到邮箱主页,不做任何事just_login_mail=False# 是否只打印主题列表,不打开邮件just_print_mail=False# 是否只打印附件列表,不下载附件just_print_file=False#是否将不含附件的主题设置为星标is_star_nofile=True#是否将不含附件的主题,导出eml文件is_dleml_nofile=False#是否在控制台输出数据can_print_folder_table=Falsecan_print_title_table=Truecan_print_files_table=True#下载结束后跳转到首页is_ended_jump_home=True#生成邮件列表csv文件is_export_titlelist_csv=True#生成附件列表csv文件is_export_filelist_csv=True#...............................................................................# 指定下载计划。#...............................................................................#  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}#...............................................................................# 邮件主题,关键词过滤#...............................................................................# 白名单关键词。只搜索邮件主题中包含任意一个关键词的邮件。# 示例:title_whitelist_keys = ['反馈','回复']title_whitelist_keys=['']# 黑名单关键词。忽略邮件主题中包含任意一个关键词的邮件。# 示例:title_blacklist_keys = ['发信方已撤回邮件','QQ会员业务通知邮件']title_blacklist_keys=['发信方已撤回邮件']# 文件 扩展名黑名单# 示例:r'(txt)|(psd)|(ai)|(docx)|(pdf)|(psb)|(cdr)|(sketch)'file_extension_blacklist=r'(exe)|(vb)''''
#===============================================================================
#                  "请 勿 跨 过 这 块 区 域 修 改 内 容"
#===============================================================================
'''#...............................................................................# GLOBAL VAR#...............................................................................count_download_email={"count":0,"lastMailID":""}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}#...............................................................................# 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#...............................................................................defp1(text):try:print(text)except:print("控制台打印了一些内容,但因为字符中含有某些特殊符号,无法显示。")# 打开链接defget_url(url):chrome.get(url)chrome.implicitly_wait(implicitly_wait_time)# 检查SID是否存在,若存在则跳过自动填写账号密码登陆deftest_sid_Valid():returnbool(URLTEMP_SID)# 检查页面元素是否存在deftest_id_Valid(name):try:returnchrome.find_element_by_id(name)!='null'except:returnFalse# 检查class元素是否存在。如果指定了min,则检查class元素的数量。deftest_class_Valid(name,min=0):try:returnlen(chrome.find_elements_by_class_name(name))>minexcept:pass;#print("无法获取class元素: {} \n".format(name))# 检查页面元素ID是否存在。如果存在则返回元素deftest_id_Valid_get(name):try:e=chrome.find_element_by_id(name);chrome.execute_script("()");returneexcept:returnFalse# 检查页面元素Class是否存在。如果存在则返回元素deftest_class_Valid_get(name):try:e=chrome.find_elements_by_class_name(name);chrome.execute_script("()");returneexcept:returnFalse# 检查iframe框架是否存在,如果存在将焦点跳转到该iframedeftest_frame_Valid(name):try:e=test_id_Valid_get(name);chrome.switch_to.frame(e);returneexcept:returnFalse# 检查列表是否为空deftest_list_Valid(i):returnbool(i)andi!=['']# 如果字符串中含有某个关键词,返回Truedefcheck_key_in_name(str,key):returnall([iinstrforiinkey])# 时间戳转换时间deftimeStamp(t):returntime.strftime("%Y-%m-%d %H:%M:%S",time.localtime(float(t/1000)))# 检查文件夹路径是否存在(仅检查1层)defcheck_download_Valid():ifnotos.path.exists(DOWNLOAD_FOLDER):print_folder_exists();os.mkdir(DOWNLOAD_FOLDER)# 反转字典, value to keydefswapDict(d):result={}fork,vind.items():for_kinv:result.setdefault(_k,{})result[_k][k]=d[k][_k]returnresult# 将Table导出为csv文件defptable_to_csv(table,filename):raw=table.get_string()data=[tuple(filter(None,map(str.strip,splitline)))forlineinraw.splitlines()forsplitlinein[line.split('|')]iflen(splitline)>1]withcodecs.open(filename,'w','utf_8_sig')asf:fordindata:f.write('{}\n'.format(','.join(d)))#...............................................................................#  LOGIN#...............................................................................# 更新tokendefupdate_token():token=config['TOKEN']token["sid"]=URLTEMP_SIDifbool(URLTEMP_SID)elsechrome.current_url.split("sid=")[1].split("&")[0]token["folderid"]=data_folders_list[FOLDER_INDEX]["id"]ifFOLDER_INDEX>0else0ifFOLDER_INDEX==0elseFOLDER_IDifFOLDER_ID>0else0token["page"]=0# 自动登录defauto_login():p1("尝试自动登录")chrome.switch_to.default_content()test_frame_Valid("login_frame")iftest_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()iftest_id_Valid("mainFrameContainer"):p1("登录成功")time.sleep(2)iftest_id_Valid("tcaptcha_iframe"):trigger_once=Truewhilebool(test_id_Valid("tcaptcha_iframe")):iftrigger_once:print("等待用户手动完成拼图认证...");trigger_once=False;time.sleep(3)iftest_id_Valid("login_frame"):trigger_once=Truewhilebool(test_id_Valid("login_frame")):iftrigger_once:print("等待用户完成登陆验证...");trigger_once=False;time.sleep(3)iftest_id_Valid("mainFrame"):update_token()print("登陆完成!")# 修改邮箱每页显示数量100defsetting_email():p1("修改邮箱设置:[邮件列表每页显示数量]和[邮件列表视图]")get_url("{}&url=/cgi-bin/setting1?fun=list".format(config['TOKEN']["sid"]))test_frame_Valid("mainFrame")# 修改邮箱每页显示数量100e=test_class_Valid_get("btn_select_limiting")[1]e.click()e.send_keys(Keys.DOWN);time.sleep()e.send_keys(Keys.DOWN);time.sleep()e.send_keys(Keys.ENTER);time.sleep()# 修改邮箱邮件列表视图为标准模式,而不是会话模式test_frame_Valid("mainFrame");ActionChains(chrome).click(test_id_Valid_get("listmode1")).perform()# 保存test_frame_Valid("mainFrame");chrome.execute_script("('sendbtn').click();")# 舒服了。。乖乖得写总是点不了保存按钮。#...............................................................................#  GET FOLDER LIST#...............................................................................# 获取文件夹列表defget_folder_list():ifnottest_id_Valid("leftPanel")andnottest_id_Valid("folders"):error_load_folder()n,d=0,[]elements=iter(chrome.find_element_by_id("personalfolders").find_elements_by_tag_name("li"))foriteminelements: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])globaldata_folders_dictdata_folders_dict=dict(d)print_folder_table()#...............................................................................# GET EMAIL LIST#...............................................................................# 进入文件夹defopen_next_page():token=config['TOKEN']get_url("/cgi-bin/mail_list?folderid={}&page={}&sid={}&nocheckframe=true".format(token['folderid'],token['page'],token['sid']))get_folder_info()get_email_title()# 获取文件夹页数信息defget_folder_info():test_frame_Valid("mainFrame")token=config['TOKEN']token['page']+=1config["PAGES"]["step"]+=1config["PAGES"]["isNotFistPage"]=1iftoken['page']!=1else0config["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('(').strip(');'))iftoken['page']>Pages_Task["start"]>0:returnos.system("cls")# 获取文件夹的邮件列表defget_email_title():data1,data2=[],[]# 基础信息elements=iter(chrome.find_elements_by_css_selector('input[name="mailid"]'))forindex,einenumerate(elements):ifindex<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"))foreinelements:data2.append({"title":e.get_attribute('innerHTML').replace('&nbsp;','')})# 合并两个list# 在这里处理白名单、黑名单。以及 TASK TITLEfora,binzip(data1,data2):a.update(b)iftest_list_Valid(title_blacklist_keys)andcheck_key_in_name(a['title'],title_blacklist_keys):data_title_blacklist.append(a)eliftest_list_Valid(title_whitelist_keys)andnotcheck_key_in_name(a['title'],title_whitelist_keys):data_title_whitelist.append(a)else:ifint(a['index'])>Title_Task["end"]>0:breakifconfig["TITLE"]["step"]>=Title_Task["step"]>0:breakifTitle_Task["start"]>int(a['index'])>0:continueconfig["TITLE"]["step"]+=1data_email_titlelist.append(a)# 检查翻页open_next_page()ifcheck_page_can_next()elsecheck_task_end_type()#...............................................................................# NEXT PAGE#...............................................................................#检查是否需要翻页defcheck_page_can_next():ifnotPages_Task['autoNext']ornotconfig["PAGES"]["iscanNext"]:returnFalseifconfig['TOKEN']['page']>Pages_Task["end"]>0:config['PTASK']='e';returnFalseifconfig["PAGES"]["step"]>Pages_Task["step"]>0:config['PTASK']='s';returnFalseifconfig['TITLE']['index']>Title_Task["end"]>0:config['TTASK']='e';returnFalseifconfig['TITLE']['step']>=Title_Task["step"]>0:config['TTASK']='s';returnFalsereturnTrue#检查时以哪种方式结束翻页的defcheck_task_end_type():p,t=config['PTASK'],config['TTASK']tp,tt,cp,cd=Pages_Task,Title_Task,config["PAGES"],config["TITLE"]ifnottp['autoNext']and(tp["step"]>0ortt["step"]>0ortp["end"]>0ortt["end"])>0:stop_by_page_next()tp["start"]=tp["start"]iftp["start"]>0else1tt["start"]=tt["start"]iftt["start"]>0else1tp["end"]=tp["end"]iftp["end"]>0elsecp["max"]tt["end"]=tt["end"]iftt["end"]>0elselen(data_email_titlelist)tp["step"]=tp["step"]iftp["step"]>0elsecp["step"]tt["step"]=tt["step"]iftt["step"]>0elsecd["step"]stop_by_page_end()ifp=='e'elsestop_by_page_step()stop_by_title_end()ift=='e'elsestop_by_title_step()ifcan_print_title_table:print_title_table()#...............................................................................#  GET EMAIL FILES#...............................................................................# 打开邮件defopen_email():titlelist=data_email_titlelist.__iter__()max=len(data_email_titlelist)check_download_Valid()#检查下载路径是否存在whileTrue:ifcount_download_email["count"]>=max:download_end();breakemail=next(titlelist)count_download_email["count"]+=1time.sleep(random.uniform(,))chrome.get("/cgi-bin/frame_html?t=newwin_frame&sid={}&url=/cgi-bin/readmail?t=readmail%26mailid={}%26mode=pre".format(config['TOKEN']["sid"],email['mailid']))chrome.implicitly_wait(2)time.sleep(1)test_frame_Valid("mainFrame")check_frame_timeout(email['index'])# 您请求的频率太快,请稍后再试elements1=test_class_Valid_get("ico_big")elements2=test_class_Valid_get("down_big")#附件列表iflen(elements1)<=0:p1("{} 没有邮件".format(email['index']))data_email_nofilelist.append(email)#下载eml文件ifis_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()#设置为星标ifis_star_nofile:mark_star=test_id_Valid_get("img_star");iftest_class_Valid('qm_ico_flagoff'):mark_star.send_keys(Keys.SPACE);continue;#过期附件iflen(elements2)<=0:p1("{} 没有下载按钮,可能已过期".format(email['index']))#下载eml文件ifis_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()#设置为星标ifis_star_nofile:mark_star=test_id_Valid_get("img_star");iftest_class_Valid('qm_ico_flagoff'):mark_star.send_keys(Keys.SPACE);continue;#整理附件信息,并下载附件forfinelements1: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://"+a.get_attribute('down')})attach.update({'viewmode':a.get_attribute('viewmode')})attach.update({'index':int(a.get_attribute('idx')or0)})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)file_extension=attach['filename'].split(".")[-1].lower()ifre.findall(file_extension_blacklist,file_extension):print("{} 跳过 {} ".format(email['index'],file_extension));continue;p1("{}{}".format(email['index'],attach['filename'],email['email']))ifjust_print_file:continuedownlnk=elements2[attach['index']].find_elements_by_link_text('下载')[0]ActionChains(chrome).click(downlnk).perform()p1("")# 检测“您请求的频率太快,请稍后再试”defcheck_frame_timeout(index):pause=test_class_Valid("errorIcon")ortest_id_Valid("msg_txt")ornottest_id_Valid("contentDiv")ifpause:p1("\n{} 正在队列中等待...请稍等".format(index))wait=0whiletest_id_Valid("msg_txt")ortest_class_Valid("errorIcon"):ifwait==0:time.sleep()elifwait==1:time.sleep(2)elifwait==2:time.sleep(3)elifwait==3:time.sleep(5);chrome.refresh()elifwait%4==0:time.sleep(3);chrome.refresh()else:time.sleep(1)test_frame_Valid("mainFrame")ifnottest_id_Valid("msg_txt")ortest_class_Valid("errorIcon"):breakelse:wait+=1test_frame_Valid("mainFrame")pause=Falsep1("{} 等待结束,任务继续。\n".format(index))#...............................................................................# Print Console Log#...............................................................................deferror_qlogin():p1("登录失败。请尝试登录QQ客户端后重试")deferror_load_page():p1("打开文件夹失败。")deferror_load_title():p1("获取邮件列表失败。")deferror_load_folder():p1("获取文件夹列表失败。")deferror_seting_email():p1("设置失败。")defstop_by_title_step():p1("[TITLE STEP]从第{}封邮件开始,读取{}封邮件后结束。".format(Title_Task['start'],Title_Task['step']))defstop_by_page_step():p1("[PAGES STEP]从第{}页开始,读取{}页后结束。".format(Pages_Task['start'],Pages_Task['step']))defstop_by_page_next():p1("[PAGES NEXT]由于关闭了自动翻页,提前结束。")defstop_by_page_end():p1("[Task PAGE]从第{}页开始,在第{}页结束。".format(Pages_Task['start'],Pages_Task['end']))defstop_by_title_end():p1("[Task TITLE]从第{}封邮件开始,在第{}封邮件结束。".format(Title_Task['start'],Title_Task['end']))defprint_folder_exists():p1("文件夹不存在。正在自动创建文件夹....")defprint_download_end():p1("结束了。{}/{}".format(count_download_email["count"],len(data_email_titlelist)))#...............................................................................# Print TABLE#...............................................................................# 打印文件夹表格defprint_folder_table():ifnotcan_print_folder_table:returntb0=pt.PrettyTable()tb0.field_names=["序","id","文件夹"]tb0.align="l"tb0.reversesort=Trueforaindata_folders_list:tb0.add_row(["%02d"%a['index'],a['id'],a['name']])p1(tb0)# 打印邮件标题表格defprint_title_table():# 黑名单ifbool(data_title_blacklist):tb1=table_title_blacklistforain(data_title_blacklist):tb1.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb1);p1("设置了黑名单关键词,以上{}封邮件不包含在最终列表中。\n\n".format(len(data_title_blacklist)))# 白名单ifbool(data_title_whitelist):tb2=table_title_whitelistforain(data_title_whitelist):tb2.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb2);p1("设置了白名单关键词,以上{}封邮件不包含在最终列表中。\n\n".format(len(data_title_whitelist)))# 最终列表ifbool(data_email_titlelist):tb=table_email_titlelistforain(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)))# 打印附件列表defprint_files_table():# 没有附件的邮件列表ifbool(data_email_nofilelist):tb1=table_email_nofilelistforain(data_email_nofilelist):tb1.add_row([a['index'],a['name'],a['title'],a['email'],timeStamp(int(a['timestamp'])),a['page']])p1(tb1);p1("有{}封邮件没有附件的主题,已标记为星标邮件。\n\n".format(len(data_email_nofilelist)))# 附件列表ifbool(data_email_fileslist):tb=table_email_fileslistforain(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#...............................................................................defdownload_end():ifcan_print_files_table:print_files_table()ifis_export_filelist_csv:ptable_to_csv(table_email_fileslist,DOWNLOAD_FOLDER+r"_导出附件列表_"+now+r".csv")ifis_ended_jump_home:get_url("/")print_download_end()#...............................................................................#  Main#...............................................................................if__name__=='__main__':now=time.strftime("%Y%m%d_%H%M%S",time.localtime(time.time()))get_url("/")os.system('cls')# 检查是否已经登陆ifnottest_id_Valid("login_frame")andtest_sid_Valid():auto_login()print(f'sid: { ("sid=")[1].split("&")[0] }')#获取文件夹列表ifnotbool(data_folders_list):get_folder_list()ifnotbool(config['TOKEN']["sid"]):update_token()# 是否更改邮箱每页显示数量、邮件列表视图ifcan_setting_mail:setting_email()# 进入文件夹,获取邮件列表ifjust_login_mail:p1("\nsid: \n{}".format(config['TOKEN']["sid"]));p1("\n如果要继续打开文件夹")os.system("PAUSE")os.system('cls')open_next_page()# 打开邮件,获取附件列表ifjust_print_mail:ifis_export_titlelist_csv:ptable_to_csv(table_email_titlelist,DOWNLOAD_FOLDER+r"_导出邮件列表"+now+r".csv")p1("如果要继续下载附件")os.system("PAUSE")os.system('cls')open_email()

特性

特性
- 自定义附件的下载路径
- 自动翻页
- 填写账号密码,自动登录。
- 若自动登录失败,可手动登录,再根据SID密钥记住登录。
- 自定义邮件标题的白名单或黑名单关键词,过滤某些邮件名。
- 自定义附件扩展名黑名单关键词,不下载某类文件。
- 自定义从第几封邮件开始,第几封邮件结束,处理多少封邮件后结束。
- 自定义从文件夹第几页开始,到第几页结束,只处理多少页的任务计划。
- 可以自动修改每页显示数量为100封,需手动开启。
- 如果邮件没有包含附件,可以导出eml文件,需手动开启。
- 如果邮件没有包含附件,会自动打星标。
- 打开邮箱时,可以输出所有文件夹列表,方便到文件夹ID。
- 脚本结束后会生成csv文件,包含所有附件列表信息。
- 脚本结束后会生成csv文件,包含所有附件列表信息。
- 如果浏览器请求速度过快,脚本会自动刷新页面,等待恢复正常


可能出现的BUG
1. 如果网络不稳定。附件的预览图如果没有加载出来,脚本可能会卡住。
(已修复。解决方案:以不加载图片的模式启动浏览器)

2. 如果窗口太小,可能获取不到页面元素,然后报错。
(已修复。解决方案:在启动浏览器时调整窗口大小至合适的值,主要是高度)

3. 如果开车的速度过快,会被系统拦下车。提示:【您请求的频率太快,请稍后再试】
(已修复。当出现提示窗口时,脚本自动会等待10秒,并自动反复刷新,直到恢复暂停的地方继续下载)

4. 如果电脑打开了QQ,登陆页面会出现手机扫码登陆的提示,导致自动填写账号失败。
(已修复。自动检测是否出现扫码提示,并切换到账号密码登陆。)

5. 因不明原因,有几率在登录阶段需要安全验证,滑动拼图滑块。导致无法自动登录
(已修复。解决方案有些麻烦。需手动登录,并在脚本处手动关闭[拦截浏览器显示图片]配置,登陆后需手动记录SID密钥。关闭脚本再次运行脚本,即可自动登录。)

6. 收件人含有特殊符号,导致脚本奔溃。
(事实上,我并没有对字符串做处理,只是将他们输出在控制台而已。报错是因为编码问题,你可以将打印到控制台中的方法全部禁用掉,直接下载附件就行)

7. 本地重命名的批处理脚本,如果附件有重复的文件,后面的相同的文件不会被重命名。
(没想好。临时解决方案:根据输出的记录,搜索文件名,找到发件人昵称或者邮箱,手动重命名)


踩坑历史
1. 附件收藏中的"全部附件",并不是想象中真的把全部附件整合在一起,偶尔还是会漏掉一些。
 (已解决。换成进入邮件主题模拟手动下载附件)

2. 在下载过程中,中途收到了新的邮件,列表顺序会出现错误。
 (不要下载收件箱的邮件,建议将他们移动到文件夹里下载,并关闭该文件夹的收件规则,下载期间的别乱动。)

附赠脚本

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

importosimportshutilimportfnmatchdeffind_key(key,path):forninos.listdir(os.getcwd()):iffnmatch.fnmatch(n,key):print('{}{}'.format(key,n))shutil.move(n,path)defcheckfile():all_md5={}filedir=os.walk(os.getcwd())foriinfiledir:fortlieini[2]:ifmd5sum(tlie)inall_md5.values():print('- {}'.format(tlie))shutil.move(tlie,'md5')#(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] 处的[禁用网页显示图片]。(即注释掉这句代码)
 # ("--blink-settings=imagesEnabled=false")
 
2. 重新运行脚本,手动填写账号密码登陆,勾选记住密码。

3. 登陆后从浏览器地址栏中找到这个参数: sid 
   https://{ xxxxxx }&r={ 这个可以无视 }

4. 将sid的这串字符串密钥,填写到 URLTEMP_SID = xxx 的位置,并重新打开[禁用网页显示图片]

5. 重新运行脚本。

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

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

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

解决方案:

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


每个附件都会跳转到单独的页面下载。(如果10个附件就是要打开10个页面) 当然,如果163的附件已经过期,就无法下载了。

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于涉及到服务器文件的遍历,需要使用服务器端的代码。以下是使用Node.js实现服务器文件遍历的示例代码: ```javascript const fs = require('fs'); // 定义遍历函数 function traverseFolder(folderPath) { fs.readdir(folderPath, function(err, files) { if (err) { console.error(err); return; } files.forEach(function(file) { const fullPath = folderPath + '/' + file; fs.stat(fullPath, function(err, stats) { if (err) { console.error(err); return; } if (stats.isDirectory()) { // 如果是文件夹,递归进入 traverseFolder(fullPath); } else { // 如果是文件,可以做相应的处理 console.log(fullPath); } }); }); }); } // 调用遍历函数 traverseFolder('/path/to/your/folder'); ``` 上述代码中,`traverseFolder`函数用于遍历指定路径下的所有文件文件夹。对于每个文件夹,递归进入;对于每个文件,可以做相应的处理,例如打印路径。 此外,还需要在Node.js中使用HTTP模块搭建一个简单的Web服务器,以便在浏览器中访问文件列表。以下是示例代码: ```javascript const http = require('http'); const fs = require('fs'); const path = require('path'); // 定义遍历函数 function traverseFolder(folderPath, callback) { fs.readdir(folderPath, function(err, files) { if (err) { callback(err); return; } const results = []; files.forEach(function(file) { const fullPath = folderPath + '/' + file; fs.stat(fullPath, function(err, stats) { if (err) { callback(err); return; } const item = { name: file, path: fullPath, isDirectory: stats.isDirectory() }; if (stats.isDirectory()) { // 如果是文件夹,递归进入 traverseFolder(fullPath, function(err, children) { if (err) { callback(err); return; } item.children = children; results.push(item); if (results.length === files.length) { callback(null, results); } }); } else { // 如果是文件,直接添加到结果数组中 results.push(item); if (results.length === files.length) { callback(null, results); } } }); }); }); } // 创建HTTP服务器 const server = http.createServer(function(req, res) { // 获取当前路径 const currentPath = '.' + req.url; // 如果是文件,直接返回文件内容 if (fs.existsSync(currentPath) && fs.statSync(currentPath).isFile()) { fs.readFile(currentPath, function(err, data) { if (err) { res.writeHead(500); res.end(); return; } res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end(data); }); return; } // 如果是文件夹,返回文件列表 traverseFolder(currentPath, function(err, results) { if (err) { res.writeHead(500); res.end(); return; } // 生成HTML代码,展示文件列表 let html = '<ul>'; results.forEach(function(item) { html += '<li>'; if (item.isDirectory) { html += '<a href="' + item.path + '">' + item.name + '</a>'; if (item.children) { html += '<ul>'; item.children.forEach(function(child) { html += '<li><a href="' + child.path + '">' + child.name + '</a></li>'; }); html += '</ul>'; } } else { html += item.name; } html += '</li>'; }); html += '</ul>'; res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); }); }); // 启动服务器 server.listen(8080, function() { console.log('Server is listening on port 8080'); }); ``` 上述代码中,`traverseFolder`函数的作用与之前的示例代码相同,但这里将结果包装为一个对象数组,以便在浏览器中进行展示。同,返回的结果是一个异步回调函数的参数,而非直接输出到控制台。 在HTTP服务器中,首先判断当前请求的路径是否是一个文件,如果是,则直接返回文件内容。否则,调用`traverseFolder`函数获取文件列表,并生成HTML代码,向浏览器输出。需要注意的是,这里使用了`fs.existsSync`和`fs.statSync`函数判断当前路径是否是一个文件,以避免遍历整个文件夹。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值