因為工作原因,需要處理QQ郵箱上來自各地網友的投稿附件。數量比較多,如果手動一個一個下載很麻煩,而且有些發來的附件命名也不規範,下載下來之後還需要手動去重命名,否則放一起就分不清誰是誰了。這種非常機械化的重複操作,我想寫個腳本批量下載QQ郵箱附件。

於是臨時研究了一下 Python + selenium + Chrome 來節省很多時間~

0 安裝包

Python 3

Welcome to Python.org

WebDriver for Chrome

sites.google.com/a/chro

--------------------------------------------

1 手動輸入賬號密碼登陸郵箱

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("user-data-dir=selenium")

chrome = webdriver.Chrome(options=options)

url = https://mail.qq.com/
chrome.get(url)

print(登陸成功!)
chrome.implicitly_wait(2)

第一次啟動腳本會進入QQ郵箱的登陸頁面,需要手動登陸一次。勾選下次自動登陸,然後就可以關掉瀏覽器了。下一次再啟動腳本,就會自動登陸直接進入郵箱主頁了。

註:通過運行腳本啟動的瀏覽器窗口,同時只能啟動一個。若重複啟動腳本將會打開空頁面,需要關閉上一個窗口重新運行腳本。

嗯,解決了登陸問題,就可以來抓取附件。把你想要下載的郵件移動到文件夾裡面,然後讓腳本進入文件夾裡面自動下載。如果數量比較多,建議在郵箱設置-常規裡面,調整每頁顯示100封郵件

完整代碼:

# encoding:utf-8
import os
import urllib
import _thread
import time
from selenium import webdriver

#......................................................
# 自定義參數
#......................................................
# 登陸郵箱後,找到你想打開的文件夾,右鍵新窗口打開,在瀏覽器地址欄可以看到以下地址:
# https://mail.qq.com/cgi-bin/frame_html?t=frame_html&sid={ A }&url=/cgi-bin/mail_list?folderid={ B }%26page={ C }
#
# 需要自定義的參數已在上方鏈接以 A B C 標記出
# A - token[sid] 相當於臨時身份認證。這串密鑰似乎會每天更新,每次使用時需要重新填寫。
# B - token[folderid] 文件夾的ID
# C - token[page] 郵件列表頁數
#......................................................
token={sid:R6-CnEyUYkXOuAy, folderid:135, page:0 }

#......................................................
# title[index] 第index封郵件
# title[start] 從第start封郵件開始
# title[end] 到第end封郵件結束,-1為默認列表長度
# title[step] 讀取郵件次數超過step時結束,-1為默認列表長度
#......................................................
title={index:0, start:1, end:-1, step:5}

# 附件下載到哪個文件夾(需提前新建文件夾,不然會報錯)
download_here=D:\XHXIAIEIN\Downloads\psd

#......................................................
# 用來測試的一些參數
#......................................................

# 開啟DEBUG模式後,只輸出列表數據,不下載任何附件。(0: 關閉 | 1: 開啟)
DEBUG=0

# 用來計數測試用的,總感覺有時會漏掉一些文件。
test={fileindex: 0, filecount: 0, downloadtimes: 0}

# readmail 郵件列表:包含郵件id(value)、時間戳(totime)、發件人郵箱(fa)、發件人昵稱(fn)
# filemail 附件列表:包含郵箱、郵件主題、發件人昵稱、附件名
readmail,filemail=[],[]

#......................................................
# 配置Web Driver
#......................................................
prefs={"download.default_directory":download_here}
options=webdriver.ChromeOptions()
options.add_argument("user-data-dir=selenium")
options.add_argument("Referrer-Policy=no-referrer")
options.add_experimental_option("prefs",prefs)
chrome=webdriver.Chrome(options=options)

url_qqmail=https://mail.qq.com/
url_folder=url_qqmail+cgi-bin/mail_list?sid={}&folderid={}&page={}.format(token[sid],token[folderid],token[page])

# 啟動Web Driver
print(" ")
print(" Chrome啟動")
chrome.get(url_qqmail)
chrome.implicitly_wait(3)

# 進入文件夾
chrome.get(url_folder)
chrome.switch_to.frame(chrome.find_element_by_id("mainFrame"))
print(" ---- 進入文件夾: ",chrome.find_element_by_xpath(//*[@id="qqmail_mailcontainer"]/div[1]).text.strip(管理"我的文件夾").strip())
chrome.implicitly_wait(3)

#......................................................
# 開始處理郵件
#......................................................
print(" 獲取郵件列表")
elements=chrome.find_elements_by_css_selector(input[name="mailid"])

# 獲取郵件列表
for e in elements:
if title[index] >= title[start]:
# check = (false, true)[a == -1]
check_end = (title[index] <= title[end],title[index] <= len(elements))[title[end] == -1]
check_step = (title[index]-title[start] < title[step],True)[title[step] == -1]

if check_end and check_step:
sender={}
sender.update({timestamp: e.get_attribute(totime)})
sender.update({name: e.get_attribute(fn)})
sender.update({email: e.get_attribute(fa)})
sender.update({id: e.get_attribute(value)})
sender.update({index: title[index]})
readmail.append(sender)
print( ├─{} {}.format(title[index],sender[name]))
time.sleep(0.1)

title[index]+=1

time.sleep(1.5)
os.system(cls)
print(" ")
print( 郵件主題({}).format(title[index]-1))
print(" 開始處理郵件")

# 獲取每封郵件的附件列表
for key in readmail:
url=url_qqmail+cgi-bin/frame_html?sid={}&url=/cgi-bin/readmail?mailid={}.format(token[sid],key[id])
url=urllib.parse.unquote(url)
chrome.implicitly_wait(3)
chrome.get(url)
chrome.switch_to.default_content()
chrome.switch_to.frame(chrome.find_element_by_id("mainFrame"))

elements=chrome.find_elements_by_class_name("name_big")
print( ├─{} {}.format(key[index],key[name]))

for f in elements:
attach={}
attach.update({title: chrome.find_element_by_id("subject").text})
attach.update({name: key[name]})
attach.update({email: key[email]})
attach.update({filename: f.find_element_by_css_selector(span:nth-child(1)).text})
filemail.append(attach)
test[filecount]+=1
print(" │ ├─{}".format(attach[filename]))

if DEBUG != 1:
os.chdir(download_here)
cmd=open("_ren.bat","a")
cmd.seek(0)
cmd.truncate()
cmd.write("@echo off")

for key in filemail:
cmd.write(ren "{}" "{}-{}".format(key[filename], key[email], key[filename]))
cmd.write("
")

cmd.write("del _ren.bat")
cmd.close()

if DEBUG != 1:
elements=chrome.find_elements_by_link_text(下載)
for e in elements:
e.click()
test[downloadtimes]+=1
time.sleep(2)

print( └─filecount:{} downloadtimes:{}.format(test[filecount],test[downloadtimes]))


以下內容為黑歷史:

2 進入附件收藏

踩坑補充:(附件收藏的全部附件,居然不是「全部附件」,有些郵件的附件會漏掉!!!被TX坑了!!所以才更換為上方的方案)

# 進入附件收藏
chrome.find_element_by_xpath(//*[@id="folder_attach"]).click()

chrome.switch_to.default_content()
chrome.switch_to.frame(chrome.find_element_by_xpath(//*[@id="mainFrame"]))

# 查看全部附件
chrome.find_element_by_xpath(//*[@id="empty_tips"]/p/a).click()

對了對了!這裡還踩到了一個坑!!

左邊菜單欄中的「附件收藏」和中間的「全部附件」其實在兩個不同的iframe里。

需要切換通過 switch_to_frame 來切換框架,否則會出現 NoSuchElementException 的錯誤!

進了全部附件,就可以看到所有的附件列表了.

接下來要抓取每個附件的資料

--------------------------------------------

3 獲取附件列表

# 獲取附件列表
list = chrome.find_element_by_xpath(//*[@id="list"]/ul)
items = list.find_elements_by_tag_name("li")

--------------------------------------------

4 整理附件信息

for item in items:
rank = item.get_attribute(rank)
email = item.find_elements_by_tag_name("span")[1].get_attribute(title)
sender = item.get_attribute(sender)
filename = item.get_attribute(filename)
fileextenal = item.get_attribute(fileextenal)
filebyte = item.get_attribute(filebyte)

拿到了數據,接下來就是要把附件都下載下來。

--------------------------------------------

5 批量下載

# 寫在開頭 options 下邊
download_here = E:\Downloads\email

prefs = {"download.default_directory" : download_here}
options.add_experimental_option("prefs",prefs)

# for item in items:
# ...

# ...

downurl = item.get_attribute(downurl)
chrome.execute_script("window.open(%s);"%downurl)

# 下載前等0.1秒,防止抽風
time.sleep(0.1)

這樣只抓取到1頁的內容,要下載其他頁就得設置一下翻頁了。

--------------------------------------------

6 翻頁下載

target_page = 2

while target_page > 1:
next = list.find_element_by_xpath(//*[@id="div_foot_right"]/a[3])
print("跳轉頁面")
next.click()
target_page-=1
time.sleep(1)

對了對了!這裡還踩到了一個坑!!

因為附件信息儲存在前面的<li>。翻頁之後,需要刷新一下<li>,才能拿到翻頁後的。

文件都下載下來了,但是命名都不規範。需要用 cmd ren "old" "new" 命令來重命名。

--------------------------------------------

7 保存批處理命令

import os

# 創建批處理腳本
os.chdir(localDownloads)
cmd = open("_ren.bat","a")
cmd.truncate()

cmd.write("@echo off")

# for item in items:
# ...
cmd.write(ren "{}" "{}-{}".format(filename, email, filename))
cmd.write("
")

cmd.write("del _ren.bat")
cmd.close()

--------------------------------------------

8 過濾主題關鍵詞

import re

# 過濾郵件主題的關鍵詞(黑名單模式,看到某個詞就跳過)
ignore_keys = [不想看到的關鍵詞A,關鍵詞B]

#...
#...

# 整理附件信息
right_item = len(items)

for item in items:
can_download = True
fromname = item.get_attribute(fromname)
filename = item.get_attribute(filename)

for key in ignore_keys:
if re.search(key, fromname):
print(--過濾"{}", 主題:{}.format(key,fromname))
can_download = False
right_item-=1
break

for key in ignore_keys:
if re.search(key, filename):
print(--過濾"{}", 附件:{}.format(key,filename))
can_download = False
right_item-=1
break

if can_download:
...

嗯,這樣就可以悠悠閑閑聽著歌,翹著腿,喝著咖啡,解決所有的問題了~


完整代碼

#coding:utf-8

import re
import os
import time
from selenium import webdriver

#....
# 自定義參數

url = https://mail.qq.com/

# 附件下載到哪個文件夾(先提前建好,不然會報錯)
download_here = D:\Downloads\email

# 下載哪一頁的附件(是的,每次只能下載1頁)
target_page = 1

# 過濾郵件主題的關鍵詞(黑名單模式,看到某個詞就跳過)
ignore_keys = [不想看到的關鍵詞A,關鍵詞B]

#....
# 配置Web Driver

prefs = {"download.default_directory" : download_here}
options = webdriver.ChromeOptions()

options.add_argument("user-data-dir=selenium")
options.add_argument(lang=zh_CN.UTF-8)
options.add_argument(user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36")
options.add_experimental_option("prefs",prefs)

chrome = webdriver.Chrome(options=options)

# 啟動Web Driver
chrome.get(url)
print("Chrome啟動")

chrome.implicitly_wait(3)

# 進入附件收藏
chrome.find_element_by_xpath(//*[@id="folder_attach"]).click()
print("進入附件收藏")

chrome.implicitly_wait(3)
chrome.switch_to.default_content()
chrome.switch_to.frame(chrome.find_element_by_xpath(//*[@id="mainFrame"]))

# 查看全部附件
chrome.find_element_by_xpath(//*[@id="empty_tips"]/p/a).click()
chrome.implicitly_wait(3)

# 跳轉到指定的頁面
while target_page > 1:
list = chrome.find_element_by_xpath(//*[@id="list"]/ul)
next = list.find_element_by_xpath(//*[@id="div_foot_right"]/a[3])
next.click()
print("跳轉頁面: ",target_page)
target_page-=1
time.sleep(1)

# 獲取附件列表
list = chrome.find_element_by_xpath(//*[@id="list"]/ul)
items = list.find_elements_by_tag_name("li")
print("獲取附件列表(%s)"%len(items))

# 創建批處理腳本
os.chdir(download_here)
cmd = open("_ren.bat","a")
cmd.seek(0)
cmd.truncate()
cmd.write("@echo off")
print("創建批處理腳本")

#....
# 整理附件信息,下載附件
print("開始整理附件:")
right_item = len(items)

for item in items:

fromname = item.get_attribute(fromname)
filename = item.get_attribute(filename)
can_download = True

for key in ignore_keys:
if re.search(key, fromname):
print(--過濾"{}", 主題:{}.format(key,fromname))
can_download = False
right_item-=1
break

for key in ignore_keys:
if re.search(key, filename):
print(--過濾"{}", 附件:{}.format(key,filename))
can_download = False
right_item-=1
break

if can_download:
rank = item.get_attribute(rank)
email = item.find_elements_by_tag_name("span")[1].get_attribute(title)
sender = item.get_attribute(sender)
filename = item.get_attribute(filename)
fileextenal = item.get_attribute(fileextenal)
filebyte = item.get_attribute(filebyte)
print("+",filename)
cmd.write(ren "{}" "{}-{}".format(filename, email, filename))
cmd.write("
")

downurl = item.get_attribute(downurl)
chrome.execute_script("window.open(%s);"%downurl)
time.sleep(0.1)

cmd.write("del _ren.bat")
cmd.close()
print("----")
print("OK(%s)"%right_item)
print("等附件全部下載好,去啟動重命名腳本吧。")

等待全部下載完畢後,可以到下載目錄裡面運行 _run.bat 就會自動在文件名前面補上發信人的QQ郵箱了。

...

編輯中

未完待續


這裡,踩坑黑歷史,被拋棄的BUG (x

車禍現場 ?

# 第一次用的時候,用item_dict輸出每條信息來測試用的
# for item in items:
# item_dict = {
# rank: item.get_attribute(rank),
# email: item.find_elements_by_tag_name("span")[1].get_attribute(title),
# filename: item.get_attribute(filename),
# sender: item.get_attribute(sender),
# }
# print(item_dict)

車禍現場 ?

# 第二次,想著是不是該寫個字典把它們都裝起來呢?
# attach_list = []
#
# for item in items:
# item_dict = {}
# item_dict.update({rank: item.get_attribute(rank)})
# item_dict.update({email: item.find_elements_by_tag_name("span")[1].get_attribute(title)})
# item_dict.update({sender: item.get_attribute(sender)})
# item_dict.update({filename: item.get_attribute(filename)})
# item_dict.update({fileextenal: item.get_attribute(fileextenal)})
# item_dict.update({filebyte:item.get_attribute(filebyte)})
# attach_list.append(item_dict)

車禍現場 ?

# 寫到這,我又不是要統計數據,只是想下載而已...我為啥不把他們直接寫變數...搞啥字典啊

# cmd.write(ren "{}" "{}-{}".format(item_dict[filename], item_dict[email], item_dict[filename]))
# cmd.write("
")

車禍現場 ?

# 模擬點擊下載按鈕
# ERROR 只能下載一個文件

# item_id = item.get_attribute(id)
# downlink = item.find_element_by_xpath("//*[@id=%s]/div/div/div[2]/div[2]/div[2]/a[1]" %item_id)
# downlink.click()

推薦閱讀:

相关文章