【Python学习笔记】获取微信公众号合集标题

背景:

家人最近很爱看查理芒格的文章,希望能做一个文章目录合集,把所有的公众号文章标题保存下来。我想之前学过一些BeautifulSoup提取网页信息的知识,就动手做了一些测试。

正文:

1.手动滚动网页+BeautifulSoup解析HTML获取合集标题:

这个微信公众号的合集页面比较特殊,是个动态加载的网页。传送门
https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIxOTQwODMzNA==&action=getalbum&album_id=3754847637367488525&subscene=1&scenenote=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxOTQwODMzNA%3D%3D%26mid%3D2247489196%26idx%3D1%26sn%3D0560e5d10a818aa8d4804e3c459ed76c%26chksm%3D961ed99e8ee33e1d4fa8b36670a26db3d767ed512ab4acd63b960f81769bfde4d59dc59a05c8%26mpshare%3D1%26srcid%3D0330gKaCngywRfLcXfuugmto%26sharer_shareinfo%3D4ecaed5f49b2aa034f310d9f0bdacb86%26sharer_shareinfo_first%3D4ecaed5f49b2aa034f310d9f0bdacb86%26from%3Dsinglemessage%26scene%3D1%26subscene%3D10000%26sessionid%3D1743227851%26clicktime%3D1743310347%26enterid%3D1743310347%26ascene%3D1%26fasttmpl_type%3D0%26fasttmpl_fullversion%3D7663006-zh_CN-zip%26fasttmpl_flag%3D0%26realreporttime%3D1743310347338%26devicetype%3Dandroid-35%26version%3D28003936%26nettype%3DWIFI%26abtest_cookie%3DAAACAA%253D%253D%26lang%3Dzh_CN%26countrycode%3DCN%26exportkey%3Dn_ChQIAhIQmwA5OQm0zgqjxuurLyJuYBL2AQIE97dBBAEAAAAAAMHsN%252BlmkpIAAAAOpnltbLcz9gKNyK89dVj0GKluV1DPcNwvWJY61S3edeHH0S18aYGkjXv%252B36hLUVT%252FFcOBZHsgLx3NBJnNOM69V8%252B7RMb%252FGCGxXhW0RqfzmRyaZ3%252BiBGXvnPsS4mBeB3SbSO9ECADkeOln7AjMXNSlM%252F737GzqA9Hcea6N%252BJZdyncZf%252BjXlerflGaJIJ0miLDn19GSJ2%252FeHInEUiTMFcV2Mbvb%252BywUnJNsEiuu3QNhngiPLbkcXBfpXjjKqEEcrXRQOAjxAT0BFGQsD6Hptrv5aI0VoX91eC8%252FnDU5DESfqQ%253D%253D%26pass_ticket%3DlvE5hX83IvY2ryzuNsc6hc4rKFgM9YwVmACGIICYu2QmaFKGcg1Jmg2uc6X0kAkG%26wx_header%3D3&nolastread=1#wechat_redirect
在这里插入图片描述
在这里插入图片描述如果不会或者不想用自动化的话,就简单粗暴,人工把页面拖拽到最底下,确保所有标题都加载完了,然后右键单击网页——另存为——把HTML文件保存下来。
在这里插入图片描述
这里的保存选项很重要,必须选择【网页,全部】,也就是会有HTML代码+一个图片等资源文件夹下载到电脑上。
如果【网页(单个文件)】格式会变成mhtml,这个解析起来会比较麻烦,不要选择。
如果选了【网页,仅HTML】,那么HTML文件里没有动态加载的标题,获得的内容不全。
blog.csdnimg.cn/direct/72473b9b751c48a487b9ab35e246742c.png)

保存好后,打开看是个html文件就很有希望了,为了稳妥起见,可以打开这个文件,随便搜索一个位于原网页很底下的标题,看看是否确实都包括在html文件里面了。
在这里插入图片描述
在这里插入图片描述
这个html文件可以用记事本打开都行。
在这里插入图片描述
然后,在网页里找到我们需要获取的标题在HTML文件里所在的结构,如类名等。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们要获取的序号和标题所在的HTML结构就找到了。
在这里插入图片描述
在右侧代码上右键单击,复制元素。
在这里插入图片描述
就会得到:

<div id="album__item-title_2247486571_1" class="weui-mask-ellipsis__text">                      <span>5. </span>                      <span class="album__item-title-wrp">查理芒格:避免被人妒忌的最佳方法是做到名副其实</span>                    </div>

最后,只需要在AI的帮助下写一段Python代码,读入这个html文件,然后调用BeautifulSoup等库正常解析HTML文件就行了。

代码详细逻辑我就不赘述了,我当时想到这个思路,是觉得既然HTML都能拿记事本打开,那么本质上这个任务就是《在一个文本文件里面匹配字符串并组合》,只不过HTML拿元素的方式比普通txt要特殊一些而已,需要按照类名或者正则表达式等方法,在HTML文件里面提取内容,按序号排序,拼接为一个dataframe,最后输出为csv。

如果确实看不明白的,可以粘贴给AI问下。甚至这段代码就是AI写的,我看大致逻辑是正确的+确实功能如期实现了,也没有去深究了。

import os
import pandas as pd
from bs4 import BeautifulSoup
import re  # 导入正则表达式模块

# 1. 解析HTML文件
def parse_html(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        html_content = file.read()
    return BeautifulSoup(html_content, 'html.parser')

# 2. 提取内容
def extract_data(soup):
    items = []
    divs = soup.find_all('div', class_='weui-mask-ellipsis__text', id=lambda x: x and x.startswith('album__item-title_'))
    
    for div in divs:
        spans = div.find_all('span')
        if len(spans) >= 2:
            number = spans[0].text.strip()
            title = spans[1].text.strip()
            items.append(f"{
      
      number} {
      
      title}")
    
    return items

# 3. 去重
def deduplicate(items):
    return list(set(items))

# 4. 按序号排序
def sort_by_number(items):
    # 使用正则表达式提取序号并转换为整数
    def extract_number(item):
        match = re.match(r'^(\d+)\.', item)
        if match:
            return int(match.group(1))
        return 0  # 如果没有匹配到序号,默认返回0
    
    return sorted(items, key=extract_number)

# 5. 保存到Excel
def save_to_excel(data, output_file):
    df = pd.DataFrame(data, columns=['内容'])
    df.to_excel(output_file, index=False)
    print(f"数据已保存到 {
      
      output_file}")

# 主函数
def main():
    html_file = './#查理芒格.html'  # 替换为你的HTML文件路径
    output_excel = './output.xlsx'  # 输出的Excel文件名
    
    if not os.path.exists(html_file):
        print(f"文件 {
      
      html_file} 不存在!")
        return
    
    soup = parse_html(html_file)
    items = extract_data(soup)
    unique_items = deduplicate(items)
    sorted_items = sort_by_number(unique_items)  # 按序号排序
    
    if sorted_items:
        save_to_excel(sorted_items, output_excel)
    else:
        print("未找到符合条件的内容。")

if __name__ == "__main__":
    main()

执行程序后,会把解析好的标题放进一个指定目录的CSV文件,并且按序号从小到大排序,效果如图:
在这里插入图片描述

1.html存成了mhtml文件怎么办?

如果之前的步骤没能选对,存成了mhtml文件,或者有些坑爹的浏览器,比如QQ浏览器,默认就是存mhtml文件,那么就需要转为html文件。
方法很多,我让AI生成了一些,可以参考:(其实最简单的解决方法是用Chrome浏览器,本来这个浏览器也很好用)
在这里插入图片描述
AI写的mhtml转html的Python脚本我单独粘出来,方便大家复制:(其实能实现这个功能的库有很多,我问了不同的AI,每个写的代码都不重样的)

from bs4 import BeautifulSoup

def mhtml_to_html(mhtml_file, output_file):
    with open(mhtml_file, 'r', encoding='utf-8') as file:
        soup = BeautifulSoup(file, 'html.parser')
        html_content = soup.prettify()
    with open(output_file, 'w', encoding='utf-8') as outfile:
        outfile.write(html_content)

# 示例调用
mhtml_to_html('example.mhtml', 'output.html')

2.尝试更换网页链接获取公众号标题:

如果有一页就加载完的微信公众号合集,不需要滚动翻页的话,可以用下面这段代码,只需更改url链接,设置一下csv文件输出位置,即可获得任意微信公众号链接的文章标题。
例如:下面这个合集都占不完一页展示的。
在这里插入图片描述

import os
import pandas as pd
from bs4 import BeautifulSoup
import requests
import re

# 1. 获取网页内容
def fetch_webpage(url):
    headers = {
    
    
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()  # 检查请求是否成功
        return response.text
    except requests.exceptions.RequestException as e:
        print(f"请求失败:{
      
      e}")
        return None

# 2. 解析HTML内容
def parse_html(html_content):
    return BeautifulSoup(html_content, 'html.parser')

# 3. 提取内容
def extract_data(soup):
    items = []
    divs = soup.find_all('div', class_='weui-mask-ellipsis__text', id=lambda x: x and x.startswith('album__item-title_'))
    
    for div in divs:
        spans = div.find_all('span')
        if len(spans) >= 2:
            number = spans[0].text.strip()
            title = spans[1].text.strip()
            items.append(f"{
      
      number} {
      
      title}")
    
    return items

# 4. 去重
def deduplicate(items):
    return list(set(items))

# 5. 按序号排序
def sort_by_number(items):
    def extract_number(item):
        match = re.match(r'^(\d+)\.', item)
        if match:
            return int(match.group(1))
        return 0
    
    return sorted(items, key=extract_number)

# 6. 保存到Excel
def save_to_excel(data, output_file):
    df = pd.DataFrame(data, columns=['内容'])
    df.to_excel(output_file, index=False)
    print(f"数据已保存到 {
      
      output_file}")

# 主函数
def main():
    url = 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkyNTQ1MjU0Nw==&action=getalbum&album_id=3765889640343846918&subscene=186&scenenote=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzkyNTQ1MjU0Nw%3D%3D%26mid%3D2247485552%26idx%3D1%26sn%3Db07941e544acbdc11a74380f04ed43bd%26chksm%3Dc1c7181cf6b0910ae13d1399a467142bc0bb1e2aa70efa9047c024c99d5090ac287488262e7b%26scene%3D186%26subscene%3D90%26exptype%3D%26key%3Ddaf9bdc5abc4e8d0f96a9cfbf0cf7f254e780af028163f0c8779dbd5685a565b43eb00014ba898e6514e3b31b1e3e70908ce514276e242670aec8221274b9ec2c5d78e9bf5c56114204e286ba65fb7df6da69e2e3b8678d0f780fe0006db30db01cc94afd240042e5b8d094ddee81337a3dd59b64d2e570b38a1e79352bf92a2%26ascene%3D0%26uin%3DMzcxNDAzMDc0Mw%253D%253D%26devicetype%3DWindows%2B10%2Bx64%26version%3D63090c33%26lang%3Dzh_CN%26countrycode%3DCN%26exportkey%3Dn_ChQIAhIQ9TG8xUAHHuPFzYLw%252F3VHJhLmAQIE97dBBAEAAAAAAEciKHD3iykAAAAOpnltbLcz9gKNyK89dVj0%252Fwgj%252FMV7tcUr4D8DElEICju4lAR8xT9BaptK1S97sgcQcgKZKlFR43%252F3w%252Fl80so5VScX4fdBJjwTzXuxA%252FrAf3XLGQMEyj8jsYdksCrZgd%252F4HgE4I99jf0UjdCR9K7dbETfMOx7hpzV2NKBvWCBTP2nLEd7IwTY7MRyEEdeQEQJ6Bj9EfGEea9Rd2R%252F0krkIxLn2YAq2YvNPKwwes7NtV5j97i3i2bRmRid0MS0ybD9if5bQQPxjtne0cXarkCMQ%26acctmode%3D0%26pass_ticket%3DY9vd6a2uwBev2HsJkrQvH3V8rlPJ%252BDiYV3QvsQsDryA9tIkfnXZFgKUHP1YTLId%252B%26wx_header%3D1&nolastread=1&sessionid=-444382924#wechat_redirect'  # 替换为目标网页的URL
    output_excel = 'C:/data/QMT/output.xlsx'  # 输出的Excel文件名
    
    html_content = fetch_webpage(url)
    if not html_content:
        print("无法获取网页内容。")
        return
    
    soup = parse_html(html_content)
    items = extract_data(soup)
    unique_items = deduplicate(items)
    sorted_items = sort_by_number(unique_items)
    
    if sorted_items:
        save_to_excel(sorted_items, output_excel)
    else:
        print("未找到符合条件的内容。")

if __name__ == "__main__":
    main()

我手动滚动到网页底部,保存HTML解析实现后,突然想试试不同的公众号合集,是不是获取同一个HTML样式,之前那套代码逻辑是否还能用,然后这么尝试后发现果然都是一样的标签。

3.给定链接+Selenium自动滚动获取公众号标题:

需要同时满足能自动滚动到页面底端+给url就可以运行,无需手动存文件的话,可以用Selenium库代替人的机械操作。
运行Selenium需要安装对应库,并且需要下载一个适配浏览器的驱动,会有一些要安装环境的步骤。
完成前置流程后,执行下面这段代码,会自动打开Chrome浏览器,把微信公众号合集滚动到页面底端,然后提取HTML来获得我们需要的信息。

扫描二维码关注公众号,回复: 17619957 查看本文章
import os
import pandas as pd
from bs4 import BeautifulSoup
import re
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time

# 使用 Selenium 获取网页内容并自动滚动到最底端
def fetch_webpage_with_selenium(url):
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
    driver.get(url)
    
    scroll_pause_time = 2  # 每次滚动后等待的秒数
    last_height = driver.execute_script("return document.body.scrollHeight") #通过执行 JavaScript 代码 document.body.scrollHeight 来获取当前页面的总高度。这个高度会随着页面加载更多内容而变化。
    
    while True:
        # 向下滚动到底部
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #将浏览器滚动条滚动到页面底部
        time.sleep(scroll_pause_time) #给页面足够的时间加载新内容
        new_height = driver.execute_script("return document.body.scrollHeight") #滚动后再次获取页面高度 new_height,并与之前保存的 last_height 进行比较:
        if new_height == last_height:
            break  # 页面高度不变,说明已滚动到底部
        last_height = new_height 

    html_content = driver.page_source
    driver.quit()
    return html_content

# 解析HTML内容
def parse_html(html_content):
    return BeautifulSoup(html_content, 'html.parser')

# 提取指定的标题内容
def extract_data(soup):
    items = []
    # 找到所有class为weui-mask-ellipsis__text且id以album__item-title_开头的div
    divs = soup.find_all('div', class_='weui-mask-ellipsis__text', id=lambda x: x and x.startswith('album__item-title_'))
    
    for div in divs:
        spans = div.find_all('span')
        if len(spans) >= 2:
            number = spans[0].text.strip()
            title = spans[1].text.strip()
            items.append(f"{
      
      number} {
      
      title}")
    
    return items

# 去重且保留原始顺序
def deduplicate(items):
    seen = set()
    unique_items = []
    for item in items:
        if item not in seen:
            unique_items.append(item)
            seen.add(item)
    return unique_items

# 按序号排序
def sort_by_number(items):
    def extract_number(item):
        # 如果标题以数字开头,如 "1. 标题内容",则提取数字部分
        match = re.match(r'^(\d+)', item)
        if match:
            return int(match.group(1))
        return 0
    return sorted(items, key=extract_number)

# 保存数据到 CSV 文件,保存在代码所在目录
def save_to_csv(data, output_file):
    df = pd.DataFrame(data, columns=['内容'])
    df.to_csv(output_file, index=False, encoding='utf-8-sig')
    print(f"数据已保存到 {
      
      output_file}")

# 主函数
def main():
    # 微信公众号页面的 URL
    url = 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIxOTQwODMzNA==&action=getalbum&album_id=3754847637367488525&subscene=1&scenenote=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxOTQwODMzNA%3D%3D%26mid%3D2247489196%26idx%3D1%26sn%3D0560e5d10a818aa8d4804e3c459ed76c%26chksm%3D961ed99e8ee33e1d4fa8b36670a26db3d767ed512ab4acd63b960f81769bfde4d59dc59a05c8%26mpshare%3D1%26srcid%3D0330gKaCngywRfLcXfuugmto%26sharer_shareinfo%3D4ecaed5f49b2aa034f310d9f0bdacb86%26sharer_shareinfo_first%3D4ecaed5f49b2aa034f310d9f0bdacb86%26from%3Dsinglemessage%26scene%3D1%26subscene%3D10000%26sessionid%3D1743227851%26clicktime%3D1743310347%26enterid%3D1743310347%26ascene%3D1%26fasttmpl_type%3D0%26fasttmpl_fullversion%3D7663006-zh_CN-zip%26fasttmpl_flag%3D0%26realreporttime%3D1743310347338%26devicetype%3Dandroid-35%26version%3D28003936%26nettype%3DWIFI%26abtest_cookie%3DAAACAA%253D%253D%26lang%3Dzh_CN%26countrycode%3DCN%26exportkey%3Dn_ChQIAhIQmwA5OQm0zgqjxuurLyJuYBL2AQIE97dBBAEAAAAAAMHsN%252BlmkpIAAAAOpnltbLcz9gKNyK89dVj0GKluV1DPcNwvWJY61S3edeHH0S18aYGkjXv%252B36hLUVT%252FFcOBZHsgLx3NBJnNOM69V8%252B7RMb%252FGCGxXhW0RqfzmRyaZ3%252BiBGXvnPsS4mBeB3SbSO9ECADkeOln7AjMXNSlM%252F737GzqA9Hcea6N%252BJZdyncZf%252BjXlerflGaJIJ0miLDn19GSJ2%252FeHInEUiTMFcV2Mbvb%252BywUnJNsEiuu3QNhngiPLbkcXBfpXjjKqEEcrXRQOAjxAT0BFGQsD6Hptrv5aI0VoX91eC8%252FnDU5DESfqQ%253D%253D%26pass_ticket%3DlvE5hX83IvY2ryzuNsc6hc4rKFgM9YwVmACGIICYu2QmaFKGcg1Jmg2uc6X0kAkG%26wx_header%3D3&nolastread=1#wechat_redirect'
    
    # 输出的 CSV 文件名(保存在与代码同一级目录)
    output_csv = 'output.csv'

    # 获取页面内容(自动滚动加载所有内容)
    html_content = fetch_webpage_with_selenium(url)
    if not html_content:
        print("无法获取网页内容。")
        return

    # 解析 HTML 内容
    soup = parse_html(html_content)

    # 提取标题内容
    items = extract_data(soup)
    if not items:
        print("未找到符合条件的内容。")
        return

    # 过滤掉仅为"0"的项
    items = [item for item in items if item.strip() != "0"]

    # 去重并按序号排序
    unique_items = deduplicate(items)
    sorted_items = sort_by_number(unique_items)

    # 保存到 CSV 文件
    save_to_csv(sorted_items, output_csv)

if __name__ == "__main__":
    main()

运行效果:
在这里插入图片描述

1.代码运行时的控制台输出:

这个代码运行的时候,可能控制台会输出一些东西:
在这里插入图片描述
让AI解释了一下,问题不大,如果不需要,可以执行如下代码关闭输出:

from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument("--disable-dev-tools")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.Python里面居然可以执行JavaScript 代码:

这段代码有个我很新奇的地方,就是居然可以把JS代码写在Python里面给程序执行。
例如:

while True:
        # 向下滚动到底部
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #将浏览器滚动条滚动到页面底部
        time.sleep(scroll_pause_time) #给页面足够的时间加载新内容
        new_height = driver.execute_script("return document.body.scrollHeight") #滚动后再次获取页面高度 new_height,并与之前保存的 last_height 进行比较:
        if new_height == last_height:
            break  # 页面高度不变,说明已滚动到底部
        last_height = new_height 

这个execute_script(“return document.body.scrollHeight”) 就是执行了JS代码。
同样,我找AI询问了解了一下原理,实际上是Selenium把这段JS代码传递给浏览器执行了。
在这里插入图片描述
这个是我觉得我在探索过程中发现的最大亮点,太酷啦!!

4.关于Selenium库的一些资料:

我对Selenium库了解也不深,可以给大家推荐的我看的俩入门视频:

1.了解什么是Selenium:传送门

https://www.bilibili.com/video/BV1Sk4y1y7DN/?spm_id_from=333.337.search-card.all.click&vd_source=cdfd0a0810bcc0bcdbcf373dafdf6a82
在这里插入图片描述

2.通过一个实操案例,直观感受Selenium能做什么:传送门

https://www.bilibili.com/video/BV1As4y1v7Zh/?spm_id_from=333.337.search-card.all.click&vd_source=cdfd0a0810bcc0bcdbcf373dafdf6a82
在这里插入图片描述

3.如何安装Selenium?

当时我装的时候参考了不少大佬码友的教程,我这里放一些对我有帮助的。
难点集中在以下几个点:1.不知道Python库名称,2.不知道去哪下载驱动,3.不知道把驱动放哪个目录。

1.下载哪个Python库?

https://www.cnblogs.com/nizi-ver1/p/18574291 传送门
不熟悉这个库的同学可能会有点迷惑,没关系,我找到了。

pip install webdriver-manager

在这里插入图片描述

2.去哪下载驱动?

有专门的网站供下载驱动,记得先看好自己Chrome或者其他浏览器对应的版本。
在这里插入图片描述
https://blog.csdn.net/weixin_41956627/article/details/139173428 传送门
在这里插入图片描述

3.把驱动放哪个目录?

找Python安装目录的方法有很多,cmd窗口输入where python、开始菜单栏有Python快捷方式的可以”查看文件所在位置“、也可以上网查默认安装目录等。
下面是一些我找到的帖子,试了没效果的话自己再搜搜,或者问AI。

https://blog.csdn.net/weixin_45131680/article/details/146508623?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-146508623-blog-80531155.235v43control&spm=1001.2101.3001.4242.1&utm_relevant_index=2 传送门
在这里插入图片描述https://www.bilibili.com/opus/831599612342042665 传送门
这个帖子也是个Selenium操作打开B站的实操小案例,新手可以研究下。
在这里插入图片描述