給你的我的微信朋友圈數據生成一本電子書吧!

給你的我的微信朋友圈數據生成一本電子書吧!

簡介

微信朋友圈保留着你的數據,它留住了美好的回憶,記錄了咱們成長的點點滴滴。發朋友圈從某種意義上來說是在記錄生活,感覺生活,並從中看到了每一個人每一步的成長。python

這麼一份珍貴的記憶,何不將它保存下來呢?只需一杯咖啡的時間,便可一鍵打印你的朋友圈。它能夠是紙質書,也能夠是電子書,能夠長久保存,比洗照片好,又有時間足跡記憶。git

  • 這本書,能夠用來:
  • 送給孩子的生日禮物
  • 送給伴侶的生日禮物
  • 送給將來的本身
  • ……

如今,你能夠選擇打印電子書或者紙質書。打印紙質書的話,能夠找第三方機構花錢購買;打印電子書的話,咱們徹底能夠本身動手生成,這能夠省下一筆不小的開支github

部分截圖

在開始寫代碼思路以前,咱們先看看最終生成的效果。web

電子書效果


紙質書效果

代碼思路

獲取微信書連接

看完效果圖以後,開始進入代碼編寫部分。首先,因爲朋友圈數據的隱私性較高,手動獲取的話,須要使用root的安卓手機進行解密或對pc端備份的聊天記錄數據庫進行解密,這對大部分人來講難度較大。因此咱們採起的思路是基於現有的數據進行打印電子書。chrome

目前,已經有第三方服務支持導出朋友圈數據,微信公衆號【出書啦】就提供了這樣一種服務。這種服務很大可能性是基於安卓模擬器進行自動化採起操做的,具體就不詳細講了。數據庫

首先,關注該公衆號,而後開始製做微信書。該過程爲小編添加你爲好友,而後你將朋友圈開放給他看,等一會後採集完畢後,小編會發給你一個專屬連接,這個連接裏面的內容就是你的我的朋友圈數據。json

生成電子書

有了這個連接後,咱們開始對該頁面的內容進行打印。瀏覽器

整個過程基於selenium自動化操做,若是你有了解過selenium的話,那麼其實該過程是很簡單的。bash

首先,引導用戶輸入微信書連接,咱們採用在瀏覽器彈出一個輸入文本框的形式讓用戶輸入數據。
首先,在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本項目

相關文章
相關標籤/搜索