目录
引言
RUC蒟蒻一枚,同学需要今日头条的评论数据,因此我在上次的工作中做了一些拓展。
先展示爬取成果:
在上一篇文章今日头条爬虫实战(代码+知识点讲解)-CSDN博客,我们爬取了今日头条的一些基本数据,如标题、正文、点赞数等等,这些内容按F12可以直接在html中呈现,非常容易获取。但是当你想爬取一些评论的时候,你会发现直接爬取的html中没有需要的评论信息。
想一下我们是怎么看到所有评论的呢,见下图:
我们是点击了“查看全部535评论”这个按钮过后,网页才会动态加载所有评论信息,此时你才能按F12后看到相应评论。这是因为现代Web采取了一种叫AJAX的技术,为了更好地帮助你理解和构造请求,下面对这种技术做一个简要介绍。
AJAX简介
AJAX(Asynchronous JavaScript And XML),是一种使用现有技术组合(主要是 JavaScript 和 XMLHttpRequest 对象)在后台与服务器交换数据并更新部分网页内容,而无需重新加载整个页面的技术。
异步 (Asynchronous)是AJAX的灵魂所在。意味着浏览器在向服务器发送请求后,不会傻傻地等待响应,而是可以继续执行其他脚本、响应用户操作。当服务器的响应数据返回时,浏览器再通过JavaScript来处理这些数据并更新页面的特定部分。
当用户在页面上执行某个操作(如点击按钮、滚动页面、在搜索框中输入文字等),JavaScript 代码创建一个用于与服务器通信的核心对象,负责向服务器发送请求,服务器接收到请求并生成和返回响应数据。JavaScript 使用获取到的数据,通过 DOM操作方法,只更新网页中需要变化的那一小部分内容。整个页面的其他部分保持不变。
那就有办法了!我们只需要模拟AJAX发送一份请求,那怎么构造这个请求呢。
查看AJAX发送的请求从而在代码中构造请求
以本次爬取评论数据为例(这里楼主偷了懒,大部分用dpsk生成的,所以一股子AI味)
识别数据源: 使用浏览器的开发者工具,切换到 "Network"标签页,清空所有请求,点击“查看全部535评论”,此时会出现新的请求。
筛选XHR/Fetch请求: 在Network标签页中,筛选 XHR 或
Fetch
类型的请求。这些就是浏览器发出的AJAX请求。分析请求: 找到包含你所需数据的那个请求。查看它的:
· URL: 这是你需要用爬虫程序模拟请求的真实数据地址。通常包含关键参数。
· 请求方法 (Method):GET
或POST
。
· 请求头 (Headers): 特别是User-Agent
,Referer
,Cookie
, 有时还有Authorization
或特定的X-
头,这些可能需要在爬虫中设置才能成功请求。
· 请求参数 (Query String Parameters / Payload): 对于GET
请求,参数在URL的“?
”后面;对于POST
请求,参数在请求体里。这些参数告诉服务器你要什么数据,是构造爬虫请求的关键。
· 响应预览 (Preview/Response): 查看服务器返回的数据格式(通常是JSON),确认它包含你需要的目标信息。模拟请求: 在你的爬虫程序中:
使用
requests.get()
或requests.post()
向步骤3中找到的真实数据URL发起请求。在请求中设置必要的请求头(模拟浏览器行为)。
对于
GET
请求,将参数构造成URL的查询字符串。对于
POST
请求,将参数构造成字典或JSON字符串,作为data
或json
参数传递给requests.post()
。
解析响应: 服务器返回的响应通常是JSON格式。使用
response.json()
方法将其解析成Python字典或列表,然后像操作普通字典/列表一样提取你需要的数据。处理分页/动态参数: AJAX加载的内容通常分页或需要特定参数(如时间戳、token)。你需要观察多个请求,找出参数变化的规律,在爬虫中构造这些参数来获取后续数据。
明白了原理过后,下面是实际代码中重要步骤的大致讲解:
第一步 触发评论显示
即模拟用户行为进行滚动+点击按钮
# 模拟用户行为:滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 点击"查看全部评论"按钮
comment_btn = driver.find_element(By.CLASS_NAME, "side-drawer-btn")
comment_btn.click()
第二步 循环加载所有内容
我们发现评论区的每一条评论下面有多条回复,需要我们不断点击“查看更多回复”来获取所有评论信息。
# 不断点击"查看更多",直到没有新内容
while True:
more_buttons = driver.find_elements(By.CLASS_NAME, "check-more-reply")
if not more_buttons:
break # 没有更多了
for button in more_buttons:
button.click() # 继续加载
time.sleep(2) # 等待新内容
第三步 解析DOM结构
我们想在爬取结果表示A评论是B评论的回复这种父子关系,单靠识别 “@XXX” 这种模式是欠妥的,需要先探测一下评论区中父评论和回复评论形成的代码结构,结构模式如下:
<!-- 主评论 -->
<div class="comment-info">
<div class="content">这是主评论</div>
<!-- 回复评论容器 -->
<div class="replay-list">
<div class="comment-info">回复1</div>
<div class="comment-info">回复2</div>
</div>
</div>
因此我们可以通过CSS选择器精准识别这种层级关系。
# 主评论:不在replay-list内的comment-info
main_comments = driver.find_elements(
By.CSS_SELECTOR,
".comment-info:not(.replay-list .comment-info)"
)
# 回复评论:在replay-list内的comment-info
for main_comment in main_comments:
replies = main_comment.find_elements(
By.CSS_SELECTOR,
".replay-list .comment-info"
)
以上就是个人在这次实践中认为比较重要的点了,更详细的代码和成果我已经放到了GitHub上KKqdtjo/Toutia-Crawler-Practice,按需自取,祝一切顺利。