如何高效抓取动态加载的网页内容:以BeautifulSoup与XHR请求为例

碧海醫心
发布: 2025-11-23 12:51:21
原创
724人浏览过

如何高效抓取动态加载的网页内容:以BeautifulSoup与XHR请求为例

本教程旨在解决使用beautifulsoup抓取网页时,因内容动态加载而导致目标标签为空的问题。文章将深入探讨传统静态抓取工具的局限性,指导读者利用浏览器开发者工具识别并直接请求隐藏在xhr(xmlhttprequest)中的真实数据源,并通过python的`requests`库处理json响应,从而实现对动态内容的精准抓取。

理解动态内容抓取的挑战

在使用Python进行网页抓取时,BeautifulSoup库因其强大的HTML/XML解析能力而广受欢迎。然而,当目标网页的内容是通过JavaScript动态加载时,仅依赖requests.get()获取的初始HTML文档往往无法包含所有期望的数据。此时,BeautifulSoup解析到的特定标签(例如一个带有id的zuojiankuohaophpcnul>标签)可能会显示为空,即使在浏览器中查看时该标签下有丰富的内容。这是因为requests.get()只获取了服务器返回的原始静态HTML,而JavaScript在页面加载后才执行并填充了这些内容。

例如,尝试从一个动态加载内容的页面抓取id="demoFour"的<ul>标签,可能会得到一个空的<ul>标签:

import requests
from bs4 import BeautifulSoup

url = "https://www.parliament.lk/en/members-of-parliament/directory-of-members/?cletter=A"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

# 尝试查找id为demoFour的ul标签
ul_tag = soup.find('ul', id='demoFour')
print(ul_tag)
# 预期输出可能是:<ul id="demoFour"></ul>
# 实际上,在浏览器中这个ul标签下有li列表项
登录后复制

识别动态数据源:XHR请求

要解决这个问题,关键在于识别并直接访问那些负责动态加载数据的幕后请求。这些请求通常是XHR(XMLHttpRequest)或Fetch API请求,它们在后台与服务器通信,获取JSON或XML格式的数据,然后由JavaScript渲染到页面上。

使用浏览器开发者工具

  1. 打开目标网页: 在Chrome、Firefox等浏览器中打开目标网页。
  2. 打开开发者工具: 按F12键或右键点击页面选择“检查”。
  3. 切换到“网络” (Network) 选项卡: 这个选项卡会显示浏览器加载页面时发出的所有请求。
  4. 过滤XHR/Fetch请求: 在“网络”选项卡中,通常有一个过滤器选项,选择“XHR”或“Fetch/XHR”,这样可以只显示JavaScript发起的异步请求。
  5. 刷新页面或触发内容加载: 如果内容是页面加载时自动填充的,刷新页面即可看到相关请求。如果内容是通过点击按钮、滚动等方式触发的,则需要执行相应的操作。
  6. 检查请求: 仔细查看这些XHR请求的URL、请求方法(GET/POST)、请求头(Headers)、请求载荷(Payload)以及响应(Response)。通常,数据会以JSON格式在响应中返回。

通过检查,我们会发现原页面中议员列表的数据并非直接嵌入HTML,而是通过一个POST请求发送到https://www.parliament.lk/members-of-parliament/directory-of-members/index2.php?option=com_members&task=all&tmpl=component&letter={letter}&wordfilter=&search_district=这样的API端点,并以JSON格式返回。

直接请求API并处理数据

一旦找到了实际的数据API端点,我们就可以直接使用requests库向其发送请求,获取原始数据。

示例代码

以下代码演示了如何通过直接调用XHR API来获取斯里兰卡议会成员的列表数据:

import requests
import string
import json # 导入json模块以更好地处理JSON数据

# 存储所有议员数据的列表
all_members_data = []

# 遍历字母A-Z,因为议员列表是按字母分类加载的
for letter in list(string.ascii_uppercase):
    # 构建API请求URL。注意这里使用了POST请求
    # 这个URL是通过浏览器开发者工具的XHR请求中发现的
    api_url = f'https://www.parliament.lk/members-of-parliament/directory-of-members/index2.php?option=com_members&task=all&tmpl=component&letter={letter}&wordfilter=&search_district='

    try:
        # 发送POST请求到API端点
        # 实际的网页可能会在请求头中包含其他参数,例如User-Agent,
        # 如果遇到反爬,可能需要模拟更多请求头
        response = requests.post(api_url)
        response.raise_for_status() # 检查请求是否成功(状态码200)

        # 解析JSON响应
        members_json = response.json()

        # 遍历返回的每个议员数据项
        for member_info in members_json:
            # 提取所需信息并构建字典
            member_details = {
                'url': f"https://www.parliament.lk/en/members-of-parliament/directory-of-members/viewMember/{member_info['mem_intranet_id']}",
                'id': member_info['mem_intranet_id'],
                'name': member_info['member_sname_eng']
            }
            all_members_data.append(member_details)

    except requests.exceptions.RequestException as e:
        print(f"请求字母 {letter} 时发生错误: {e}")
    except json.JSONDecodeError as e:
        print(f"解析字母 {letter} 的JSON响应时发生错误: {e}")

# 打印获取到的部分数据作为示例
print(json.dumps(all_members_data[:5], indent=2, ensure_ascii=False)) # 打印前5条数据,格式化输出

# 如果需要,可以将所有数据保存到文件
# with open('parliament_members.json', 'w', encoding='utf-8') as f:
#     json.dump(all_members_data, f, indent=2, ensure_ascii=False)
登录后复制

代码解析

  1. import requests, string, json: 导入必要的库。string用于生成字母列表,json用于处理API返回的JSON数据。
  2. all_members_data = []: 初始化一个空列表,用于存储所有抓取到的议员信息。
  3. for letter in list(string.ascii_uppercase):: 循环遍历所有大写字母(A到Z)。这是因为目标网站的议员列表是按姓氏首字母分类加载的,通过改变API请求中的letter参数来获取不同字母开头的议员。
  4. api_url = f'...': 构建API请求的URL。这个URL是根据在浏览器开发者工具中分析XHR请求得到的。
  5. requests.post(api_url): 发送HTTP POST请求到API端点。注意,这里是post而不是get,因为开发者工具显示该请求是POST类型。
  6. response.raise_for_status(): 这是一个良好的实践,用于检查请求是否成功。如果HTTP状态码表示错误(如4xx或5xx),它会抛出一个requests.exceptions.HTTPError。
  7. members_json = response.json(): 将API响应体解析为Python字典或列表(如果响应是JSON格式)。
  8. 数据提取与存储: 遍历members_json中的每个成员对象,提取mem_intranet_id和member_sname_eng等关键信息,并构造一个包含议员详情URL、ID和姓名的字典,然后将其添加到all_members_data列表中。
  9. 错误处理: 使用try-except块捕获可能的requests请求错误和json解析错误,增强代码的健壮性。

注意事项与总结

  1. 动态内容是常态: 现代网页开发中,动态加载内容已成为主流。当BeautifulSoup抓取不到数据时,首先应怀疑是否为动态加载。
  2. 开发者工具是利器: 熟练使用浏览器开发者工具是抓取动态网页的关键技能。特别是“网络”选项卡,能揭示所有HTTP请求的细节。
  3. API稳定性: 直接调用网站的内部API可能会受网站更新影响而失效。网站所有者随时可能更改API端点、参数或响应格式。
  4. 请求头模拟: 有些网站会检查请求头,例如User-Agent。如果直接请求API被拒绝,尝试在requests.post()或requests.get()中添加headers参数来模拟浏览器行为。
  5. 频率限制与道德: 抓取网站数据时,请务必遵守网站的robots.txt协议,并控制请求频率,避免对目标服务器造成过大负担。过高的请求频率可能导致IP被封禁。
  6. 数据格式: 大多数动态内容API返回JSON格式数据,但也有可能是XML或其他格式。根据实际响应调整解析方法。

通过上述方法,我们不仅解决了最初<ul>标签为空的问题,更掌握了抓取动态网页的核心技术。这种直接与后端API交互的方式,通常比模拟浏览器行为(如使用Selenium)更高效、资源消耗更低,是处理这类抓取任务的首选方案。

以上就是如何高效抓取动态加载的网页内容:以BeautifulSoup与XHR请求为例的详细内容,更多请关注php中文网其它相关文章!

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

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

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

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