【最新】获取公众号文章,转成pdf发到自己邮箱

1,242 阅读9分钟

这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

大家好,我是老表!
今天给大家分享如何每天定时爬取公众号文章链接和标题,并将内容转换成PDF,以附件的形式通过邮件发送给自己的小技巧(脚本)。

一、写在前面

这也是一个读者的需求,之前也有读者提到过,拿到需求,先简单分析下,然后百度下,基本解决方法就有了,哈哈哈哈!

最后呈现效果:

本需求主要分为三个部分:

  • 爬取到公众号发布文章的链接和标题

这块,目前网络上有的一些方法有:从搜狗微信上爬取(有个现成的框架wechatsogou[1],不过好像已经好久没人维护了,链接获取功能测试失败)、从微信公众号后台爬取(需要大家注册微信公众号,麻烦),这里我用的方法是直接从第三方数据平台爬取(简单)。

我选择了志军大佬开发的次幂数据平台[2],这里也给大家安利下这个网站,除了一些公众号主可以用,读者朋友也可以在上面查看全网热文以及喜欢的公众号的历史文章等。

  • 将公众号链接内容转换为pdf

这里利用pdfkit这个库,避免直接通过url转换成pdf时出现的无法显示图片问题,我们本次引用了wechatsogou[1]中的get_article_content函数,将url中的代码提取出来转换为html字符串,具体源码我简单看了下,有兴趣的也可以私聊我一起研究下(空闲的时候)。

  • 通过邮件将pdf以附件形式发送

这一步蛮简单的,利用yagmail模块实现邮件发送功能,在开始前,我们需要先在对应邮箱的设置中打开POP3/SMTP服务,这样才能使用,不然会提示没有权限。

二、基本知识概要

  • 爬虫,利用requests发送get请求 requests.get(url,headers=headers)
  • json将json格式字符串转为Python字典格式 json.loads(json_string)
  • 基本反爬和反反爬策略
  • pdfkit将文本内容或者url链接内容转换成pdf
  • wechatsogou搜狗微信爬虫框架
  • 正则表达式re.sub函数和字符串处理函数replace
  • yagmail.SMTP自动发送邮件
  • markdown语法写邮件内容(格式)
  • 字符串传多个参数format函数
  • os模块基本操作,获取当前文件目录、创建文件夹等
  • datetime模块获取当前日期并前推一天
  • sys模块exit()结束当前程序

等。。。

三、开始动手动脑

3.1 本次项目需要导入的库

import requests   # 发送get/post请求,获取网站内容
import wechatsogou   # 微信公众号文章爬虫框架
import json   # json数据处理模块
import datetime   # 日期数据处理模块
import pdfkit  # 可以将文本字符串/链接/文本文件转换成为pdf
import os   # 系统文件管理
import re  # 正则匹配模块
import yagmail  # 邮件发送模块
import sys  # 项目进程管理

3.2 分析数据页面

https://www.cimidata.com/a/EOdxnBO4

访问上方链接进入页面发现不用登录也能看到第一页的数据了,直呼优秀。(看到这里的读者请先将 优秀 二字 打在文末留言区)

数据都在页面上,我们直接利用requests发送一个get请求即可。

import requests

# bid=EOdxnBO4 表示公众号 简说Python,每个公众号都有对应的bid,可以直接搜索查看
url1 = 'https://www.cimidata.com/a/EOdxnBO4'
r = requests.get(url1)
r.text

接下来我们需要解析网页,从其中提取出我们需要的数据:

  • 最新发布文章标题
  • 最新发布文章发布时间
  • 最新发布文章对应链接

解析网页提取数据的方法很多,比较常用的有正则表达式、Xpath、CSS选择器等,网页标签十分规则,我们利用Xpath来提取数据。

from lxml import etree
# 把html的文本内容解析成html对象
html = etree.HTML(r.text)
# xpath 根据标签路径提取数据
html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()')

输出:
[' 入门Pandas不可不知的技巧',
 ' 搞定,爬取公众号文章转换成PDF,自动邮件发送给自己!',
 ' PyPy为什么能让Python原地起飞,速度比C还快!',
 ...
 ' 给大家分享一个私藏已久的Python神器!']

这样我们就轻松的获取到了公众号简说Python近期推文的标题,接下来我们还要获取每篇文章对应的 发布时间和链接。

在此之前我们先来说说代码中的Xpath路径是如何获取到的,检索原理是什么?

在页面中,我们按住F12,调出浏览器的开发者工具,点击左上角的元素选择器,然后随便选择页面中的一个文章标题,选中后,开发者工具中的Elements会自动跳动到对应的页面源码处,如下图所示:

选中源码中对应内容,右键,选择Copy中的Copy XPath,即可复制出对应元素对应的XPath路径。

那如何获取到所有标题对应的XPath路径呢?需要一个个通过XPath路径来获取对应内容吗?

手动一个个复制肯定不切实际,其实很简单,我们先按上面方法在复制一个标题的XPath路径。

//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div[3]/div/h4/a

//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div[4]/div/h4/a

我们来观察下,会很快发现两个路径的区别在于倒数第二个div标签后面的数字不同,获取所有标题内容的方法就是直接给一个通用的XPath路径,直接去掉倒数第二个div后的中括号和数字即可,除此外还需要在路径后加上/text(),这样就可以提取出对应的内容了。

//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()

同理我们可以获取到发布时间和对应文章链接的XPath通用路径,并获取对应数据,将之前代码中的get_data函数代码改为下面的即可,其他内容不需要修改。

'''
1、从次幂数据获取公众号最新的推文链接和标题
'''
def get_data(publish_date):
    # 每日获取前一天的数据 publish_date 格式:2022-01-23
    # bid=EOdxnBO4 表示公众号 简说Python,每个公众号都有对应的bid,可以直接搜索查看
    url1 = 'https://www.cimidata.com/a/EOdxnBO4'
    r = requests.get(url1)
    # 把html的文本内容解析成html对象
    html = etree.HTML(r.text)
    # xpath 根据标签路径提取数据
    title = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/text()')  # 标题
    publish_time = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/p[2]/@title')  # 发布时间
    title_url = html.xpath('//*[@id="wrapper"]/div/div[2]/div[1]/div[2]/div/div[1]/div/div/h4/a/@href')  # 文章链接
    
    # 对数据进行简单处理,选取最新发布的数据
    data = []
    for i in range(len(publish_time)):
        if publish_date in publish_time[i]:
            article = {}
            article['content_url'] = title_url[i]
            article['title'] = title[i]
            data.append(article)
    return data

3.3 将公众号链接内容转换为pdf

'''
2、for循环遍历,将每篇文章转化为pdf
'''
# 转化url为pdf时,调用wechatsogou中的get_article_content函数,将url中的代码提取出来转换为html字符串
# 这里先初始化一个WechatSogouAPI对象
ws_api = wechatsogou.WechatSogouAPI(captcha_break_time=3) 

def url_to_pdf(url, title, targetPath, publish_date):
    '''
    使用pdfkit生成pdf文件
    :param url: 文章url
    :param title: 文章标题
    :param targetPath: 存储pdf文件的路径
    :param publish_date: 文章发布日期,作为pdf文件名开头(标识)
    '''
    try:
        content_info = ws_api.get_article_content(url)
    except:
        return False
    # 处理后的html
    html = f'''
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{title}</title>
    </head>
    <body>
    <h2 style="text-align: center;font-weight: 400;">{title}</h2>
    {content_info['content_html']}
    </body>
    </html>
    '''
    # html字符串转换为pdf
    filename = publish_date + '-' + title
    # 部分文章标题含特殊字符,不能作为文件名
    # 去除标题中的特殊字符 win / \ : * " < > | ? mac :  
    # 先用正则去除基本的特殊字符,python中反斜线很烦,最后用replace函数去除
    filename = re.sub('[/:*"<>|?]','',filename).replace('\\','')
    pdfkit.from_string(html, targetPath + os.path.sep + filename + '.pdf')
    return filename  # 返回存储路径,后面邮件发送附件需要

这里为了解决pdfkit直接转换url成为pdf会出现图片无法显示问题,参考了博客园xuzifan[3]提供的思路,利用wechatsogou中的get_article_content函数,将url中的代码提取出来转换为html字符串,然后将html字符串转换为pdf,完美解决。

这里需要注意的是:使用pdfkit需要提前访问wkhtmltopdf下载地址[4]下载并安装好wkhtmltopdf。如果是mac蛮简单的,直接下载安装即可,如果是windows需要下载安装后将安装路径添加到系统环境变量中或者修改转换pdf代码(如下)。

# 添加config
config=pdfkit.configuration(wkhtmltopdf='你的wkhtmltopdf安装路径'))   
pdfkit.from_string(html, targetPath + os.path.sep + filename + '.pdf', configuration=config)

3.4 通过邮件将pdf以附件形式发送

'''
3、通过邮件将新生成的文件发送到自己的邮箱
'''
def send_email(user_name, email, gzh_data):
    yag = yagmail.SMTP(user='你的邮箱',password='你的POP3/SMTP服务密钥',host='smtp.163.com')
    contents = ['亲爱的 '+user_name+' 你好:<br>',
                '公众号 {0} {1}发布了{2}篇推文,推文标题分别为:<br>'.format(gzh_data['gzh_name'], gzh_data['publish_date'], len(gzh_data['save_path'])),
                '<br>'.join(gzh_data['save_path']),
                '<br>文章详细信息可以查看附件pdf内容,有问题可以在公众号%s联系作者提问。<br>'%gzh_data['gzh_name'],
                '<br><br><p align="right">公众号-%s</p>'%gzh_data['gzh_name']
                ]
    # 在邮件内容后,添加上附件路径(蛮简单实现动态添加附件,直接拼接两个列表即可哈哈哈哈)
    contents = contents + [targetPath + os.path.sep + i + '.pdf' for i in gzh_data['save_path']]
    yag.send(email, '请查看'+gzh_name+publish_date+'推文内容', contents)

yagmail可以实现简单的自动发送邮件功能,我也拿来做过批量发送,蛮好用的,还支持markdown语法对邮件内容进行排版,蛮好的。

yag = yagmail.SMTP(user='你的邮箱',password='你的POP3/SMTP服务密钥',host='smtp.163.com')

这里需要注意的是,代码里的user就是你的邮箱地址,比如1234567@163.com,password不是你的邮箱密码,而是你在邮箱页面开启POP3/SMTP服务后,系统给到的一个码,下面以163邮箱为例子给大家介绍如何获取这个password

1)在网站登录对应的邮箱;

2)点击设置按钮,然后选择POP3/SMTP/IMAP,进入到相关设置页面。

3)点击POP3/SMTP服务后的 开启 按钮,然后验证下即可,完成后会有弹框提示,授权密码就是我们要的password

3.5 定时每天自动执行程序

如果你和我一样,有一台服务器,那么你可以使用宝塔面板设置计划任务,来实现程序每天定时执行。详细操作方法,大家可以看我之前分享的文章

Linux里的“宝塔”,真正的宝塔!详细教程

随便说说

本文全部代码已开源,后面会进一步优化,制作一个可视化操作界面GUI程序,也欢迎大家评论提需求,记得关注+点赞支持。

项目开源地址,持续更新:github.com/XksA-me/wec…

点赞 在看 留言 转发 ,四连支持,原创不易。好的,那么下期见,我是爱猫爱技术,更爱思思的老表⁽⁽ଘ( ˙꒳˙ )ଓ⁾⁾

参考链接