[toc]
給你的個人微信朋友圈資料生成一本電子書吧!
簡介
微信朋友圈保留著你的資料,它留住了美好的回憶,記錄了我們成長的點點滴滴。發朋友圈從某種意義上來講是在記錄生活,感受生活,並從中看到了每個人每一步的成長。
這麼一份珍貴的記憶,何不將它儲存下來呢?只需一杯咖啡的時間,即可一鍵列印你的朋友圈。它可以是紙質書,也可以是電子書,可以長久儲存,比洗照片好,又有時間足跡記憶。
- 這本書,可以用來:
- 送給孩子的生日禮物
- 送給伴侶的生日禮物
- 送給未來的自己
- ……
現在,你可以選擇列印電子書或者紙質書。列印紙質書的話,可以找第三方機構花錢購買;列印電子書的話,我們完全可以自己動手生成,這可以省下一筆不小的開支。
部分截圖
在開始寫程式碼思路之前,我們先看看最終生成的效果。
電子書效果
紙質書效果
程式碼思路
獲取微信書連結
看完效果圖之後,開始進入程式碼編寫部分。首先,由於朋友圈資料的隱私性較高,手動獲取的話,需要使用root的安卓手機進行解密或對pc端備份的聊天記錄資料庫進行解密,這對大部分人來說難度較大。所以我們採取的思路是基於現有的資料進行列印電子書。
目前,已經有第三方服務支援匯出朋友圈資料,微信公眾號【出書啦】就提供了這樣一種服務。這種服務很大可能性是基於安卓模擬器進行自動化採取操作的,具體就不詳細講了。
首先,關注該公眾號,然後開始製作微信書。該過程為小編新增你為好友,然後你將朋友圈開放給他看,等一會後採集完畢後,小編會發給你一個專屬連結,這個連結裡面的內容就是你的個人朋友圈資料。
生成電子書
有了這個連結後,我們開始對該頁面的內容進行列印。
整個過程基於selenium自動化操作,如果你有了解過selenium的話,那麼其實該過程是很簡單的。
首先,引導使用者輸入微信書連結,我們採用在瀏覽器彈出一個輸入文字框的形式讓使用者輸入資料。
首先,在selenium中執行js程式碼,js程式碼中完成彈出輸入文字框的功能。
輸入微信書連結
# 以網頁輸入文字框形式提示使用者輸入url地址
def input_url():
# js指令碼
random_id = [str(random.randint(0, 9)) for i in range(0,10)]
random_id = "".join(random_id)
random_id = 'id_input_target_url_' + random_id
js = """
// 彈出文字輸入框,輸入微信書的完整連結地址
target_url = prompt("請輸入微信書的完整連結地址","https://");
// 動態建立一個input元素
input_target_url = document.createElement("input");
// 為其設定id,以便在程式中能夠獲取到它的值
input_target_url.id = "id_input_target_url";
// 插入到當前網頁中
document.getElementsByTagName("body")[0].appendChild(input_target_url);
// 設定不可見
document.getElementById("id_input_target_url").style.display = 'none';
// 設定value為target_url的值
document.getElementById("id_input_target_url").value = target_url
"""
js = js.replace('id_input_target_url', random_id)
# 執行以上js指令碼
driver.execute_script(js)
上述js程式碼的具體步驟為:彈出一個輸入文字框,建立一個動態元素,隨機命名該元素的id,並將這個動態元素插入到當前頁面中,使得可以在python中通過selenium獲取到輸入文字框的內容。
接著,在selenium中檢測是否存在該彈框,如果不存在則獲取該彈框的內容,並進行後續步驟,該過程程式碼如下:
# 執行以上js指令碼
driver.execute_script(js)
# 判斷彈出框是否存在
while(True):
try:
# 檢測是否存在彈出框
alert = driver.switch_to.alert
time.sleep(0.5)
except:
# 如果拋異常,說明當前頁面不存在彈出框,即使用者點選了取消或者確定
break
# 獲取使用者輸入的連結地址
target_url = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.ID, random_id)))
value = target_url.get_attribute('value')
# 刪除空格
value = value.strip()
至此,value
的值即為彈出框返回的內容。(你可能會問,直接另value=微信書連結不就可以了嗎?事實上確實可以 >_<|||,但是採用上述方式會有一個良好的互動效果,同時可以加深一下對selenium的瞭解程度^_^)
設定瀏覽器引數
當使用者輸入連結完畢後,開始對瀏覽器進行初始化設定。首先設定chromedriver
路徑,可輸入絕對路徑或者相對路徑,./表示當前目錄下。不同系統和不同chrome版本需要下載不同的chromedriver,請下載合適自己的版本,chromedriver下載地址http://chromedriver.chromium.org/
接著,設定自動列印成pdf,這樣就可以預設列印成pdf了,省得我們手動列印,該步驟程式碼如下:
appState = {
# 新增儲存為pdf選項
"recentDestinations": [
{
"id": "Save as PDF",
"origin": "local",
"account":""
}
],
# 選擇儲存為pdf選項
"selectedDestinationId": "Save as PDF",
# 版本2
"version": 2,
# 不顯示頁首頁尾
"isHeaderFooterEnabled": False
}
同時,設定自動列印模式,該步驟程式碼如下:
profile = {
# 列印前置引數
'printing.print_preview_sticky_settings.appState': json.dumps(appState),
# 預設下載、列印儲存路徑
'savefile.default_directory': os.getcwd()
}
通過這兩步,就實現了全自動列印效果。
分析網頁元素
接下來到了最關鍵的步驟,即分析網頁元素。這個步驟我們可以順便學習下基本的css,js知識。
首先,按F12開啟網頁除錯工具,對頁面上不必要的元素進行隱藏
我們可以看到,頂部的導航欄可能會影響列印效果,所以,我們將它隱藏。在除錯工具中,選擇Copy Selector,得到返回的資料為body > header
,通過selenium隱藏該元素的程式碼如下:
# 隱藏導航欄,防止影響截圖效果
js = 'document.querySelector("body > header").style.display="none";'
driver.execute_script(js)
我們又發現,當前頁面顯示的資料只包含某個月朋友圈的資料,而不是所有朋友圈資料,那麼如何顯示出所有朋友圈資料呢?通過分析可知,當點選“下一月”按鈕後,會有新的元素顯示,而原來的元素被隱藏,而被隱藏的元素就是前面月份的資料。所以我們只要遍歷到最後一個月後,把前面所有元素顯示出來再列印就OK了。那麼,如何判斷是最後一個月呢?我們通過分析又可知,當不是最後一個月時,“下一月”的class名為next-month
,而當在最後一月時,“下一月”的class名為next-month disable
,因此我們可以檢測它的class名進而知道是否處於最後一個月。該步驟程式碼如下:
# 判斷當下一月控制元件的class name 是否為next-month disable,如果是,則說明翻到最後一月了
page_source = driver.page_source
# 每一個element代表每一頁,將每一頁中style的display屬性改成block,即可見狀態
for index, element in enumerate(element_left_list):
# ..在xpath中表示上一級的元素,也就是父元素
parent_element = element.find_element_by_xpath('..')
# 獲取這個父元素的完整id
parent_element_id = parent_element.get_attribute('id')
# 將該父元素更改為可見狀態
js = 'document.getElementById("{}").style.display="block";'.format(parent_element_id)
driver.execute_script(js)
但是,這樣會出現一個問題,即使我們成功列印了,但是我們不難保證頁面上的元素全部載入完成了,所以可能導致列印後某些元素沒有顯示出來,導致不是非常好看。因此,需要判斷何時載入結束。
通過分析我們得知,當網頁元素沒載入完畢時,會有一個“loading”提示,當網頁元素載入完畢後,該元素隱藏起來了。因此,我們可以判斷該元素是否隱藏來得知當前頁面元素是否載入完畢。該部分程式碼如下:
# 等待當前頁面所有資料載入完畢,正常情況下資料載入完畢後,這個‘載入中’元素會隱藏起來
while (True):
loading_status = WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'div.j-save-popup.save-popup')))
if (loading_status.is_displayed() == False):
break
可是,我們又發現,及時等待網頁元素載入完畢了,還是有部分圖片沒有顯示出來。
這就納悶了,是為什麼呢?通過分析我們又得知,這些圖片處於載入狀態的時候,class名為lazy-img
,通過字面意思,我們大概可以猜得出它是懶載入的意思,也就是使用者滑動頁面到那裡時才進行載入,以便節省伺服器壓力。
所以我們可以通過滑動到每一個class名為lazy-img
的元素,使得它進行載入。那麼?一個合適的方法就是,通過js定位到該元素,直到所有class名為lazy-img
的元素不存在。
while(True):
try:
lazy_img = driver.find_elements_by_css_selector('img.lazy-img')
js = 'document.getElementsByClassName("lazy-img")[0].scrollIntoView();'
driver.execute_script(js)
time.sleep(3)
except:
# 找不到控制元件img.lazy-img,所以退出迴圈
break
其中,document.getElementsByClassName("lazy-img")[0]
指的是document.getElementsByClassName("lazy-img")
的第一個元素,scrollIntoView()
指的是滾動到該元素的位置
列印電子書
通過上述步驟,我們已經成功地隱藏部分可能會影響外觀的元素,同時也顯示所有所需的元素,接下來,就差列印部分了。可以直接通過js程式碼喚起瀏覽器列印功能,並且,之前我們已經設定為自動列印pdf格式了,所以它將自動列印為pdf。但是,列印到哪裡呢?這裡需要設定下瀏覽器預設儲存位置,儲存的位置為當前目錄。該步驟程式碼如下:
# 預設下載、列印儲存路徑
'savefile.default_directory': os.getcwd()
# 呼叫chrome列印功能
driver.execute_script('window.print();')
列印完成後,設定退出瀏覽器driver.quit()
經過測試,該電子書為超清版本,大小約16MB,所以質量還算不錯的。
如何執行
# 跳轉到當前目錄
cd 目錄名
# 先解除安裝依賴庫
pip uninstall -y -r requirement.txt
# 再重新安裝依賴庫
pip install -r requirement.txt
# 開始執行
python main.py
補充
完整版原始碼存放在github上,有需要的可以下載
專案持續更新,歡迎您star本專案