给你的我的微信朋友圈数据生成一本电子书吧!

给你的我的微信朋友圈数据生成一本电子书吧!

简介

微信朋友圈保留着你的数据,它留住了美好的回忆,记录了咱们成长的点点滴滴。发朋友圈从某种意义上来说是在记录生活,感觉生活,并从中看到了每一个人每一步的成长。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本项目

相关文章
相关标签/搜索