今日头条的收藏页面数据提取方法
郝伟 2022/10/07
本文介绍了一种基于BeautifulSoup的数据分析技术(不是爬虫),通过使用BeautifulSoup模块对今日头条的收藏页面下载后,对HTML中的4类信息进行分析和导出。文章以图文并茂的形式介绍了整个提取过程,并添加了大量文字解释、相关参考资料和源代码。本文对有今日头条数据提取需求的朋友有极大的帮助,欢迎阅读和分享。
经常在手机上刷今日头条,自然免不了收藏一些不错的文章或视频。结果时间久了才发现已经收藏了几百条内容。但是由于手机端只能一条条下拉,查看之前的收藏很不方便,所以就有一个想法,可以从页面中提取自己的收藏的全部内容,并导出成一个CSV文件。在线查了下,今日头条果然是有网页版的,所以答案显然是可行的,本文就具体实现过程进行经验分享。
主要思想是从今日头条我的收藏中提取以下信息 (title, date, type, url) ,具体包括以下过程。
下面分项进行介绍。
原始HTML文件可能通过爬虫提取,但本由于原网站使用了动态的加载技术,爬取有难度,而且由于只需要下载本人的收藏,所以通过手工进行原始html文件下载,具体过程如下。
打开我的收藏
首先打开今日头条网站 https://www.toutiao.com/,登陆以后右上角头像菜单点开收藏,如下所示:

手工拖动右侧滚动条直到最下方
将整个文件保存,会得到1个文件 今日头条.html 和 1个文件夹 今日头条_files。
其中今日头条.html 就是我们要的内容,可以使用记事本打开,能看到里面包括所有的文本内容:

经测试,BeautifulSoup 可以对复杂的HTML数据进行表示,可使用文件读取函数加载:
soup= BeautifulSoup(open("xx.html", encoding='utf-8'), features="html.parser")
也可直接从文本加载:
soup= BeautifulSoup(html_str, features="html.parser")
其中,features 是解析器,bs4支持Python标准库, lxml HTML解析器, lxml XML解析器 和 html5lib,可以根据不同的输入文件和兼容性进行选择。
使用 soup.find_all() 方法进行数据提取查询,其中重要的语句为两种:
soup.find_all('div')soup.find_all('div', class_='feed-card-footer-time-cmp')class 是关键字,所以要加下划线,其他像 id, herf 等不用加。文件 今日头条.html 包括四类内容,如下所示:
<div class="profile-normal-video-card-wrapper"><div class="profile-article-card-wrapper"><div class="profile-wtt-card-wrapper"><div class="profile-wenda-card-wrapper">每类内容提取方法都不一样,具体参见相应代码
文章和视频类:
title, url: 在第1个标题 a 中
date: 在一对div中
数据样本: data/toutiao_data_20221003.rar --解压–> C:\data\toutiao.html
结果输出: 分别为 .\data\toutiao_20221003.csv
具体实现过程代码参见附录1。
最终提取的为CSV文件,格式如下所示:
title,date,type,url
"程序员必知必会10大基础算法",2020年05月30日,article,https://www.toutiao.com/article/6832468975046099463/
"人脑是一台计算机吗?",2022年10月04日,article,https://www.toutiao.com/article/7149576260891443724/
"金庸告诉我们的:为什么你总和你上司解释不清?",2022年09月29日,article,https://www.toutiao.com/article/7148723751150977574/
"谁在发表中国学者的论文?",2022年09月23日,article,https://www.toutiao.com/article/7146506696658043422/
"80个管理模型超全大合集",2022年09月28日,article,https://www.toutiao.com/article/7148300767613649422/
...(略过若干行)...
"发明专利的撰写方法-技术交底书",2022年04月03日,video,https://www.toutiao.com/video/7082354886947373583/
"机器学习常见算法-支持向量机",2019年11月26日,video,https://www.toutiao.com/video/6761602701307413006/
"秒懂!神经网络(NN)",2022年03月04日,video,https://www.toutiao.com/video/7071051314184061448/
...(略过若干行)...
"嗨绵先生",-,wenda,https://www.toutiao.com/w/1745208802804750/
"LaTeX工作室",-,wenda,https://www.toutiao.com/w/1744810162876480/
"周少说",-,wenda,https://www.toutiao.com/w/1744230970607688/
"向上突围",-,wenda,https://www.toutiao.com/w/1742758287606796/
"爱科学的卫斯理",-,wenda,https://www.toutiao.com/w/1741232193452099/
...(略过若干行)...
"一个程序员的奋斗史",2020年03月03日,user,https://www.toutiao.com/c/user/token/MS4wLjABAAAATpiSTY2wU_yIyk-IrqS5qsxNlNy777ziQ_uNPAi-4fY/?source=mine_home
"梦幻勇敢的春风",2020年01月15日,user,https://www.toutiao.com/c/user/token/MS4wLjABAAAAzIO8Jf2Vdrij2Xdh53UDGT1pp0sEDajlJhOB8qcCqDU/?source=mine_home
"葛小波不见了",2020年07月10日,user,https://www.toutiao.com/c/user/token/MS4wLjABAAAAG3bk07eM_4RuGli0TBU8LksnnZ-dA77vzEAfzo0eRXI/?source=mine_home
"丹淅湖畔",2019年07月30日,user,https://www.toutiao.com/c/user/token/MS4wLjABAAAAPmdeRd-sWe6OSpnKOMTb--LpFQDRRXc2b5OetswN6IM/?source=mine_home
# encoding=utf-8 import os, sys from bs4 import BeautifulSoup from datetime import datetime, timedelta def format_date(date_str): ''' 由于日期有 10月04日, 2022年10月04日,3小时前 等格式所以需要进行格式化''' if date_str.count('年') > 0 and date_str.count('月') > 0 and date_str.count('日') > 0: pass elif date_str.count('年') == 0 and date_str.count('月') > 0 and date_str.count('日') > 0: date_str = '2022年' + date_str elif date_str.count('天前') > 0: date_str = f'{datetime.now() - timedelta(days=int(date_str[0]) + 1):%Y年%m月%d日}' else: date_str = f'{datetime.now():%Y年%m月%d日}' return date_str def process_div(div): ''' 从文章或视频中提取 (title, date_str, url) ''' title, date_str, url = '', format_date(''), '' if div.a.get('title'): # 如果第1个 <a> 存在 title 属性则进行提取 title = div.a.get('title') url = div.a.get('href') for sd in div.find_all('div', class_='feed-card-footer-time-cmp'): date_str = format_date(sd.string) return title, date_str, url # html.parser是解析器,也可是lxml soup= BeautifulSoup(open("c:/data/toutiao.html", encoding='utf-8'), features="html.parser") # 结果列表,每个元素为 (title, date, url, type) ,数据类型均为 str res = [] # 1. 文章列表 divs = soup.find_all('div', class_='profile-article-card-wrapper') for div in divs: title, date_str, url = process_div(div) if len(title) > 0: res.append((title, date_str, 'article', url)) print('total divs:', len(divs)) # 2. 视频列表 <div class="profile-normal-video-card-wrapper"> total divs: 72 divs = soup.find_all('div', class_='profile-normal-video-card-wrapper') for div in divs: title, date_str, url = process_div(div) if len(title) > 0: res.append((title, date_str, 'video', url)) print('total divs:', len(divs)) # 3. 问答列表 <div class="profile-wtt-card-wrapper"> total divs: 77 divs = soup.find_all('div', class_='profile-wtt-card-wrapper') for div in divs: title = div.span.string url = div.a['href'] if len(title) > 0 and len(url) > 0: res.append((title, '-', 'wenda', url)) print('total divs:', len(divs)) # 4. 博主列表 <div class="profile-wenda-card-wrapper"> total divs: 14 divs = find_div(soup, "profile-wenda-card-wrapper") for div in divs: title = div.span.string url = div.a['href'] date_str = '-' r1 = div.find_all('div', class_='feed-card-footer-time-cmp') if len(r1) > 0: date_str = format_date(r1[0].string) if len(title) > 0 and len(url) > 0: res.append((title, date_str, 'user', url)) print('total divs:', len(divs)) # 保存结果到CSV文件 csv_file = os.path.join(os.path.dirname(sys.argv[0]), 'data', 'toutiao_20221003.csv') with open(csv_file,'w', encoding='utf-8') as fp: fp.write('title,date,type,url\n') for row in res: title, date, ctype, url = row title = title.replace('"', "'") fp.write(f'"{title}",{date},{ctype},{url}\n') print(f'Save data to "{csv_file}"')
今日头条的收藏分为文章、视频、问答和博主四类数据,以下分别显示了这四类数据的HTML格式的样本。
<div class="profile-article-card-wrapper">
<div class="feed-card-wrapper feed-card-article-wrapper">
<div class="feed-card-article single-cover">
<div class="feed-card-article-r">
<div class="feed-card-cover">
<a href="https://www.toutiao.com/article/6783165227505549827/" target="_blank" rel="noopener" title="清华大佬告诉你如何使用Python:高效操作文件的三个建议" aria-hidden="false" tabindex="0">
<img src="./路过2020的头条主页 - 今日头条(www.toutiao_files/3114e9b2f8c646f885c4099c42b465ca_720x380_cs.webp" alt="">
</a>
</div>
</div>
<div class="feed-card-article-l">
<a href="https://www.toutiao.com/article/6783165227505549827/" target="_blank" rel="noopener" class="title" aria-label="清华大佬告诉你如何使用Python:高效操作文件的三个建议">清华大佬告诉你如何使用Python:高效操作文件的三个建议</a>
<div class="feed-card-footer-cmp">
<div class="left-tools">
<div class="profile-feed-card-tools-text">1万阅读</div>
<div class="feed-card-footer-comment-cmp">
<a href="https://www.toutiao.com/article/6783165227505549827/#comment" target="_blank" rel="noopener nofollow" aria-label="评论数63">63评论</a>
</div>
<div class="feed-card-footer-time-cmp">2020年01月19日</div>
</div>
<div class="right-tools">
<div tabindex="0" role="button" aria-haspopup="true" aria-expanded="false" style="display: inline-block;">
<div class="profile-feed-card-tools-actions">
<i></i>
<div class="actions-list-wrapper">
<div class="actions-list" role="menu">
<div class="action-item" role="menuitem" tabindex="-1">取消收藏</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="profile-wtt-card-wrapper">
<div class="feed-card-wrapper feed-card-wtt-wrapper">
<div class="feed-card-wtt single-cover">
<div class="feed-card-wtt-r">
<div class="profile-normal-video-card-wrapper">
<div class="profile-normal-video-card">
<div class="feed-card-video-single">
<div class="r-content">
<div class="feed-video-item">
<div class="feed-card-cover">
<a href="https://www.toutiao.com/video/6913739482567016973/" target="_blank" rel="noopener" title="超级赛亚人1到超级赛亚人100全形态,赛亚人是没有极限的" aria-hidden="false" tabindex="0">
<img src="./路过2020的头条主页 - 今日头条(www.toutiao_files/bf98f4c829ec4ffb8fa612e5a69847e8_tplv-tt-svzoom-v3_1280_720.jpeg" alt="">
</a>
<div class="video-placeholder">
<i></i>
<span class="duration">05:38</span>
</div>
</div>
</div>
</div>
<div class="l-content">
<a href="https://www.toutiao.com/video/6913739482567016973/" target="_blank" rel="noopener nofollow" class="title">超级赛亚人1到超级赛亚人100全形态,赛亚人是没有极限的</a>
<div class="footer">
<div class="feed-card-footer-cmp">
<div class="left-tools">
<div class="profile-feed-card-tools-text">24万播放</div>
<div class="feed-card-footer-comment-cmp">
<a href="https://www.toutiao.com/video/6913739482567016973/" target="_blank" rel="noopener nofollow" aria-label="评论数432">432评论</a>
</div>
<div class="feed-card-footer-time-cmp">2021年01月04日</div>
</div>
<div class="right-tools">
<div tabindex="0" role="button" aria-haspopup="true" aria-expanded="false" style="display: inline-block;">
<div class="profile-feed-card-tools-actions">
<i></i>
<div class="actions-list-wrapper">
<div class="actions-list" role="menu">
<div class="action-item" role="menuitem" tabindex="-1">取消收藏</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="profile-wenda-card-wrapper">
<div class="profile-wenda-card single-cover">
<div class="inner">
<div class="user-info">
<a href="https://www.toutiao.com/c/user/token/MS4wLjABAAAAJ0zZHzdJsyMAT7Aia0LrJLOwHaGmW2YZwrJf3jGl4k8/?source=mine_home" rel="noopener nofollow" target="_blank">
<div class="ttp-avatar auth-none">
<img alt="" src="./路过2020的头条主页 - 今日头条(www.toutiao_files/2x_47f74e540c8814cf09ab32f2be7d2865_300x300.image" />
</div>
<span class="name">简道云</span>
</a>
<p>回答了问题</p>
</div>
<a class="title" href="https://www.toutiao.com/answer/7121996324019290383/" rel="noopener" target="_blank">
<h2>怎样才能看出来一个人的工作能力到底强不强?</h2>
</a>
<div class="body">
<div class="wenda-l">
<a href="https://www.toutiao.com/answer/7121996324019290383/" rel="noopener" target="_blank">
<p class="answer-content">在职场中,判断一个人工作能力的强弱,... 关注简道云,获取更多干货!</p>
</a>
<div class="feed-card-footer-cmp">
<div class="left-tools">
<div class="profile-feed-card-tools-text">415万展现</div>
<div class="feed-card-footer-time-cmp">07月20日</div>
</div>
<div class="right-tools">
<div aria-expanded="false" aria-haspopup="true" role="button" style="display: inline-block;" tabindex="0">
<div class="profile-feed-card-tools-actions">
<i></i>
<div class="actions-list-wrapper">
<div class="actions-list" role="menu">
<div class="action-item" role="menuitem" tabindex="-1">取消收藏</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="wenda-r">
<div class="feed-card-cover">
<a aria-hidden="true" href="https://www.toutiao.com/answer/7121996324019290383/" rel="noopener" tabindex="-1" target="_blank">
<img alt="" src="./路过2020的头条主页 - 今日头条(www.toutiao_files/6695021ee0ed4e49a5c6bfd8cf1ee7de_960x0.jpeg" />
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="profile-wtt-card-wrapper">
<div class="feed-card-wrapper feed-card-wtt-wrapper">
<div class="feed-card-wtt single-cover">
<div class="feed-card-wtt-r">
<div class="feed-card-cover">
<a href="https://www.toutiao.com/w/1668342094456832/" target="_blank" rel="noopener" aria-hidden="true" tabindex="-1">
<img src="./路过2020的头条主页 - 今日头条(www.toutiao_files/5a630fc8626f45cc910ad6a521038c4c_tplv-shrink_1024_682.jpeg" alt="">
</a>
</div>
</div>
<div class="feed-card-wtt-l">
<div class="feed-card-wtt-header">
<div class="feed-card-wtt-user-info">
<a href="https://www.toutiao.com/c/user/token/MS4wLjABAAAA1T-mU7aJrG1qOayYLTBQrAfBZL05nDXu0YgtbivY8UFln8ufsgxyO8bUA-pwI_45/?source=undefined" target="_blank" rel="noopener nofollow" title="非著名摄影爱好者">
<img aria-hidden="true" src="./路过2020的头条主页 - 今日头条(www.toutiao_files/6322a7d1d4238236b3c055927073c744">
<span>非著名摄影爱好者</span>
</a>
</div>
<i class="verified-icon" style="width: 16px; height: 16px; background-image: url("//p3.toutiaoimg.com/origin/pgc-image/b13dded4c4e948e293e217f95e8565b4");"></i>
<div class="time">2020年06月02日</div>
<div class="dot">·</div>
<div class="user-auth-desc" title="优质摄影领域创作者">优质摄影领域创作者</div>
</div>
<p class="content">
<a href="https://www.toutiao.com/w/1668342094456832/" target="_blank" rel="noopener">1988年的北京街头纪实。摄影:约根森 </a>
</p>
</div>
<div class="feed-card-wtt-footer">
<div class="feed-card-footer-cmp">
<div class="left-tools">
<div class="feed-card-footer-share-cmp">
<div class="ttp-interact-share">
<div class="share-btn" tabindex="0" role="button" aria-haspopup="true" aria-expanded="false">分享</div>
<ul class="share-tools panel-right panel-right-top" role="menu">
<li>
<div class="ttp-interact-item wtt icon-wtt" role="menuitem" tabindex="-1">转发到头条</div>
</li>
<li>
<div class="ttp-interact-item copy icon-copy" role="menuitem" tabindex="-1">复制链接</div>
</li>
<li>
<div class="ttp-interact-wx-wrapper">
<div aria-label="微信扫码分享" class="ttp-interact-item wx icon-wx" role="menuitem" tabindex="-1">微信</div>
<div class="qrcode-panel" style="display: none;">
<div id="_qrcode"></div>
<span>微信扫码分享</span>
</div>
</div>
</li>
<li>
<div class="ttp-interact-item weibo icon-weibo" role="menuitem" tabindex="-1">新浪微博</div>
</li>
<li>
<div class="ttp-interact-item qzone icon-qzone" role="menuitem" tabindex="-1">QQ空间</div>
</li>
</ul>
</div>
</div>
<div class="feed-card-footer-comment-cmp style-2">
<a href="https://www.toutiao.com/w/1668342094456832/#comment" target="_blank" rel="noopener nofollow" aria-label="评论数47">
<i></i>47
</a>
</div>
<div class="feed-card-footer-like-cmp">
<button type="button" aria-label="402点赞" aria-pressed="true" class="inner liked">
<i></i>
<span>402</span>
</button>
</div>
</div>
<div class="right-tools">
<div class="profile-feed-card-tools-text">48万展现</div>
<div tabindex="0" role="button" aria-haspopup="true" aria-expanded="false" style="display: inline-block;">
<div class="profile-feed-card-tools-actions">
<i></i>
<div class="actions-list-wrapper">
<div class="actions-list" role="menu">
<div class="action-item" role="menuitem" tabindex="-1">取消收藏</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>