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

DDD
发布: 2025-10-05 12:02:31
原创
196人浏览过

网页内容抓取进阶:解析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__=开头,并以特定的字符序列(如}}后跟分号)结束。

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__=(.*}});"的含义是:

阶跃星辰开放平台
阶跃星辰开放平台

阶跃星辰旗下开放平台,提供文本大模型、多模态大模型、繁星计划

阶跃星辰开放平台0
查看详情 阶跃星辰开放平台
  • 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片段,确保获得纯净的文本数据。这种多工具组合的方法是处理现代复杂网页抓取任务的关键技能。

以上就是网页内容抓取进阶:解析JavaScript动态加载的数据的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

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