0

0

网页内容抓取进阶:解析JavaScript动态加载的数据

DDD

DDD

发布时间:2025-10-05 12:02:31

|

222人浏览过

|

来源于php中文网

原创

网页内容抓取进阶:解析JavaScript动态加载的数据

本教程旨在解决使用BeautifulSoup直接解析HTML元素时,无法获取到通过JavaScript动态加载内容的常见问题。我们将深入探讨当目标文本被嵌入到<script>标签内的JavaScript变量(如window.__INITIAL_STATE__)中时,如何结合使用requests库、正则表达式json模块来准确提取所需数据,并最终利用BeautifulSoup对提取出的HTML片段进行清洗,从而实现更高效、更精准的网页内容抓取。

传统HTML解析的局限性

在进行网页数据抓取时,我们经常会遇到内容无法通过简单的html标签选择器获取的情况。例如,当尝试使用beautifulsoup的find_all('p')方法来提取段落文本时,可能会发现返回的文本为空,或者只获取到不相关的部分,甚至出现nameerror: name 'text' is not defined这样的错误。这通常是因为目标内容并非直接存在于页面的静态html结构中,而是通过javascript在页面加载后动态注入的。

以提供的案例为例,尽管页面HTML中存在<p>标签,但我们真正想要的文章标题和摘要文本并未直接包含在这些可见的<p>标签内。相反,它们被封装在一个名为window.__INITIAL_STATE__的JavaScript变量中,以JSON格式存储在页面的<script>标签内部。

原始尝试代码示例:

import requests 
from bs4 import BeautifulSoup

URL = "https://habr.com/ru/hubs/gamedev/articles/"

page = requests.get(URL).content
soup = BeautifulSoup(page, "html.parser")
post = soup.find("article", class_="tm-articles-list__item")

# 这里的discription可能为空或不包含所需内容
discription = post.find_all('p')
for post_text in discription:
    text = post_text.get_text()

# 如果discription为空,text变量将不会被定义,导致NameError
print(text)

这种方法在面对动态加载内容时会失效,因为BeautifulSoup只能解析requests.get()获取到的原始HTML文本,而不能执行JavaScript来渲染页面。

识别动态加载内容

要判断目标内容是否为动态加载,可以通过以下步骤:

立即学习Java免费学习笔记(深入)”;

  1. 查看页面源代码:浏览器中右键点击页面,选择“查看页面源代码”(或“View Page Source”)。搜索你想要抓取的内容,如果找不到,或者只找到一些占位符,那么很可能就是动态加载的。
  2. 检查开发者工具的网络请求: 打开浏览器的开发者工具(F12),切换到“网络”(Network)选项卡。刷新页面,观察是否有XHR(XMLHttpRequest)或Fetch请求加载了包含目标数据的新数据。
  3. 检查<script>标签: 在页面源代码中搜索<script>标签,尤其是那些看起来包含大量JSON数据或定义了全局JavaScript变量的脚本块。例如,window.__INITIAL_STATE__就是一个常见的模式,许多现代前端框架(如React、Vue等)会用它来初始化页面状态。

在我们的案例中,通过检查页面源代码,可以发现文章的标题和摘要信息被封装在window.__INITIAL_STATE__这个JavaScript对象中。

解决方案:正则表达式与JSON解析

当内容被嵌入到JavaScript变量中时,我们需要采用一种混合策略:

  1. 使用requests获取页面的原始HTML文本。
  2. 使用正则表达式从HTML文本中提取包含目标数据的JavaScript变量内容。
  3. 将提取出的JavaScript变量内容解析为JSON对象。
  4. 在JSON对象中导航,找到并提取所需的数据。
  5. 如果提取出的数据仍包含HTML标签,可再次使用BeautifulSoup进行清洗。

步骤一:获取页面原始文本

首先,我们需要获取完整的页面文本,而不仅仅是BeautifulSoup解析后的DOM结构。requests.get(URL).text可以获取到包含JavaScript代码的原始字符串。

步骤二:使用正则表达式提取JavaScript变量

为了从原始文本中精确地提取window.__INITIAL_STATE__变量的值,我们可以使用正则表达式。观察目标变量的结构,它通常以window.__INITIAL_STATE__=开头,并以特定的字符序列(如}}后跟分号)结束。

Programming Helper
Programming Helper

AI代码自动生成器,在AI的帮助下更快地编程

下载
import re
# ... (其他导入)
page = requests.get(URL).text
# 使用正则表达式匹配并提取window.__INITIAL_STATE__变量的内容
# 注意:(.*)}}; 捕获了从等号后到第一个}};之间的所有内容
data_match = re.search(r"window\.__INITIAL_STATE__=(.*}});", page)
if data_match:
    data_str = data_match.group(1)
else:
    print("未找到 window.__INITIAL_STATE__ 数据。")
    exit()

这里的正则表达式r"window\.__INITIAL_STATE__=(.*}});"的含义是:

  • window\.__INITIAL_STATE__=:匹配字面字符串window.__INITIAL_STATE__=。_是特殊字符,需要转义。
  • (.*):这是一个捕获组,.*匹配任意字符(除了换行符)零次或多次。它将捕获=之后的所有内容。
  • }});:匹配字面字符串}});,作为变量内容的结束标志。

步骤三:解析JSON数据

提取到的data_str是一个JSON格式的字符串,我们需要使用json模块将其转换为Python字典或列表。

import json
# ... (其他代码)
data = json.loads(data_str)

步骤四:导航至目标数据

一旦数据被解析为Python对象,我们就可以像操作普通字典和列表一样,通过键和索引来访问所需的信息。这通常需要对目标网站的JSON结构有所了解,可以通过浏览器开发者工具(Network -> XHR/Fetch -> Response 或 Console -> window.__INITIAL_STATE__)进行探索。

在提供的案例中,文章数据位于data["articlesList"]["articlesList"]中。我们可以遍历这个字典,并根据时间戳进行排序,以获取最新的文章。

# ... (其他代码)
for article_id, article_data in sorted(
    data["articlesList"]["articlesList"].items(),
    key=lambda item: item[1]["timePublished"], # 根据发布时间排序
    reverse=True, # 倒序,获取最新文章
):
    # 提取文章标题
    title = article_data["titleHtml"]
    # 提取文章摘要,可能包含HTML标签
    lead_text_html = article_data["leadData"]["textHtml"]

    # 我们只取第一篇文章作为示例
    break

步骤五:处理HTML片段

从JSON中提取出的lead_text_html可能仍然包含HTML标签(如<strong>、<a>等)。为了获取纯文本,我们可以再次利用BeautifulSoup。

# ... (其他代码)
# 使用BeautifulSoup清洗摘要中的HTML标签
clean_lead_text = BeautifulSoup(lead_text_html, "html.parser").text

print(title)
print(clean_lead_text)

完整示例代码

结合上述步骤,完整的解决方案代码如下:

import re
import json
import requests
from bs4 import BeautifulSoup

URL = "https://habr.com/ru/hubs/gamedev/articles/"  # 目标网站URL

# 1. 获取页面原始文本
page = requests.get(URL).text

# 2. 使用正则表达式从页面文本中提取JavaScript变量__INITIAL_STATE__的内容
# 匹配从"window.__INITIAL_STATE__="开始,到第一个"}};"结束的JSON字符串
data_match = re.search(r"window\.__INITIAL_STATE__=(.*}});", page)

if data_match:
    data_str = data_match.group(1)
else:
    print("错误:未能在页面中找到 'window.__INITIAL_STATE__' 数据。")
    exit()

# 3. 解析提取到的JSON字符串
data = json.loads(data_str)

# 4. 导航至目标数据并提取信息
# 遍历文章列表,并按发布时间倒序排序,以获取最新文章
for article_id, article_data in sorted(
    data["articlesList"]["articlesList"].items(),
    key=lambda item: item[1]["timePublished"],
    reverse=True,
):
    # 提取文章标题(通常是HTML片段)
    title_html = article_data["titleHtml"]
    # 提取文章摘要(通常是HTML片段)
    lead_data_html = article_data["leadData"]["textHtml"]

    # 5. 使用BeautifulSoup清洗HTML片段,获取纯文本
    clean_title = BeautifulSoup(title_html, "html.parser").text
    clean_lead_text = BeautifulSoup(lead_data_html, "html.parser").text

    print(f"标题: {clean_title}")
    print(f"摘要: {clean_lead_text}\n")

    # 示例中我们只获取第一篇文章,然后退出循环
    break

输出结果示例:

标题: 30 лет DOOM: новый код — новые баги
摘要: Сегодня первой игре из серии DOOM исполняется ровно 30 лет! Мы не могли обойти стороной это событие и в честь этого решили посмотреть, как же выглядит код этой легендарной игры спустя годы.

注意事项

  • 页面结构变化: 网站的HTML结构或JavaScript变量名/JSON结构可能会随时间变化。如果网站更新,您的正则表达式或JSON路径可能需要调整。
  • 正则表达式的鲁棒性: 正则表达式需要足够精确以避免误匹配,但也要足够灵活以应对微小的格式差异。过于严格的正则可能因细微变化而失效。
  • JSON结构探索: 在编写代码之前,花时间在浏览器开发者工具中仔细检查目标JSON数据的结构是至关重要的。
  • 错误处理: 在实际项目中,应加入更完善的错误处理机制,例如使用try-except块来处理requests请求失败、正则表达式匹配失败或JSON解析错误等情况。
  • 性能考量: 对于大规模抓取,频繁地使用正则表达式和JSON解析可能会有性能开销。考虑缓存或优化数据处理逻辑。

总结

当传统的HTML解析工具BeautifulSoup无法直接获取网页内容时,这通常意味着内容是通过JavaScript动态加载的。通过结合requests获取原始页面文本,使用re模块的正则表达式提取嵌入在<script>标签中的JavaScript变量(尤其是JSON格式的数据),再通过json模块进行解析,我们能够有效地获取这些动态内容。最后,BeautifulSoup仍然可以用于清洗从JSON中提取出的HTML片段,确保获得纯净的文本数据。这种多工具组合的方法是处理现代复杂网页抓取任务的关键技能。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

547

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

335

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

258

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

766

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

219

2023.08.11

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue 教程
Vue 教程

共42课时 | 9.5万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号