Python爬虫实战:逆向Ajax接口抓取动态加载音频资源

发布时间:2026/10/25 11:26:44

Python爬虫实战:逆向Ajax接口抓取动态加载音频资源 1. 项目缘起当静态爬虫遇上动态加载的音频最近在做一个音频素材收集的小项目目标是一个ASMR资源网站。这类网站通常有大量高质量的音频内容对于内容创作者或者只是想放松一下的用户来说是个宝库。我一开始的想法很简单用requests库抓取页面然后用BeautifulSoup解析出音频链接最后用urllib或者requests的get方法下载下来。这应该是每个学Python爬虫的人都会的第一课。但现实很快给了我一记闷棍。当我用requests.get()拿到网页的HTML源码兴冲冲地用BeautifulSoup去找audio标签或者.mp3、.m4a的链接时发现源码里干干净净除了一个基础的页面骨架和一些JavaScript代码根本看不到任何音频文件的直接链接。页面上的播放列表、音频标题都显示得好好的但源码里就是没有。这就是典型的Ajax动态加载。简单来说网站为了提升用户体验和性能不会在第一次请求页面时就把所有数据比如几十上百条音频信息都塞进HTML里。那样页面会非常臃肿加载缓慢。取而代之的是浏览器先加载一个“空壳”页面然后页面里的JavaScript代码再偷偷地向服务器发起额外的请求通常是XMLHttpRequest或Fetch获取真正的数据往往是JSON格式最后再用JavaScript把这些数据动态地渲染到页面上。我们看到的播放列表就是第二次甚至第N次请求的结果。传统的requestsBeautifulSoup组合只能拿到第一次请求的“空壳”自然抓不到核心数据。所以这个项目的核心挑战就变成了如何找到并模拟浏览器发起那些“偷偷的”数据请求从而拿到包含音频真实地址的JSON数据。这不仅仅是ASMR网站的问题几乎所有现代化的、交互丰富的网站如电商、社交媒体、音乐平台都会采用这种技术。掌握了这套方法你就打开了爬取动态网站的大门。2. 核心武器库工具选型与思路解析面对动态加载我们有几个主流的解决思路每种都有其适用场景和优缺点。我根据这次ASMR网站的特点选择了最直接高效的一种。2.1 方案对比逆向API vs. 模拟浏览器1. 直接逆向Ajax接口本次采用这是最高效、对服务器最友好的方法。核心是使用浏览器的开发者工具F12切换到Network网络面板筛选XHR/Fetch类型的请求。当你点击“加载更多”或页面滚动时观察面板里新出现的请求找到那个返回音频列表数据通常是JSON的请求。然后在Python中用requests库完全模拟这个请求包括它的URL、请求头Headers、请求参数Query String或Payload甚至Cookies。这个方法的优点是速度快、资源消耗低代码简洁。缺点是如果网站接口有复杂的加密、签名或反爬机制如token、sign逆向难度会大大增加。2. 使用Selenium等浏览器自动化工具这种方法简单粗暴就是用一个程序控制的真实浏览器如Chrome去访问网页等待JavaScript执行完毕页面完全渲染后再直接从浏览器的DOM树里提取数据。你可以用driver.find_element_by_xxx来定位元素。它的优点是几乎能应对所有动态加载无需关心背后的网络请求。缺点是速度慢、资源消耗大要启动浏览器不适合大规模爬取且容易被网站检测到自动化行为。3. 使用Pyppeteer或Playwright可以看作是Selenium的现代升级版直接控制无头浏览器Headless Chrome/FirefoxAPI更强大执行效率相对更高。但本质上仍属于模拟浏览器范畴资源开销依然存在。4. 解析JavaScript源码如果数据是通过页面内嵌的JavaScript变量或函数直接输出的可以尝试从第一次请求的HTML源码中用正则表达式提取出这些JS代码片段然后解析出数据。这种方法比较取巧不稳定一旦网站JS代码更新就容易失效。对于这个ASMR网站经过分析其音频列表是通过一个结构清晰的XHR请求获取的JSON数据没有特别复杂的反爬因此逆向API接口是最佳选择。我们的工具链也就确定了请求库requests(模拟HTTP请求)数据解析内置的json模块 (解析接口返回的JSON)数据提取与清洗无 (因为接口直接返回结构化数据)文件下载requests的流式下载功能2.2 关键步骤拆解整个爬虫流程可以分解为以下几步我会在后续章节详细展开侦察阶段使用浏览器开发者工具找到加载音频列表数据的那个“关键请求”并记录下其所有细节URL、方法、头信息、参数。模拟请求在Python中用requests库构造一个一模一样的请求发送出去。解析数据接收服务器返回的JSON响应将其转换为Python的字典或列表从中提取出我们需要的音频信息标题、音频文件URL等。下载存储遍历提取到的音频URL列表使用requests进行流式下载并合理命名、保存到本地。应对策略处理可能的分页、反爬机制如请求头校验、频率限制使爬虫更健壮。3. 实战侦察揪出那个“偷偷”的请求理论说再多不如动手操作一遍。这里我以Chrome浏览器为例演示如何找到ASMR网站加载音频数据的Ajax请求。第一步打开目标网站并清空记录用Chrome打开目标ASMR网站的某个音频列表页面例如“最新上传”页面。按下F12或右键“检查”打开开发者工具切换到Network网络面板。为了看得清楚先点击面板左上角的红色圆形按钮记录按钮旁边的清除按钮一个禁止图标清空已有的网络请求记录。第二步触发数据加载并筛选现在让页面触发一次数据加载。常见的方式有滚动页面到底部触发无限滚动加载。点击“下一页”按钮。点击“加载更多”按钮。 观察Network面板你会看到瞬间多出了一堆新的请求。在面板顶部有一排筛选器点击“XHR”或“Fetch”。这两个类型筛选出来的通常就是由JavaScript发起的、用于获取数据的Ajax请求。其他像document,stylesheet,image等可以先忽略。第三步定位关键请求并分析在筛选出的XHR/Fetch请求列表中一个个点击查看。重点关注Preview预览标签页这里会直观地展示服务器返回的数据。如果你点击一个请求在Preview里看到了结构化的JSON数据里面包含了title,url,id等字段那大概率就是它了Headers标头标签页这是最重要的部分我们需要从这里复制信息来构造我们的Python请求。Request URL请求地址这是我们要请求的完整地址。复制下来。Request Method请求方法通常是GET或POST。Query String Parameters查询参数如果方法是GET参数会附在URL问号?后面。如果是POST则可能在下面的Form Data或Payload里。这些参数如page2,limit20,categorymusic决定了你获取的是哪一批数据必须原样带上。Request Headers请求头这里的信息用于告诉服务器“谁在请求”。必须重点关注User-Agent,Referer,Cookie等字段。很多网站会校验User-Agent如果缺失或不对可能会拒绝请求或返回错误数据。Referer告诉服务器这个请求是从哪个页面发起的有时也有校验作用。Cookie则包含了你的会话信息对于需要登录的页面至关重要。注意有些网站的参数或数据可能被加密或者在Payload里以Form Data或Request Payload通常是JSON格式的形式存在。你需要根据实际情况在Python代码中以字典或JSON字符串的形式提交这些数据。第四步验证请求你可以在开发者工具的“Console控制台”面板里尝试用JavaScript的fetch函数重放这个请求看看是否能拿到相同的数据。或者更简单把Request URL复制到浏览器地址栏直接访问如果是GET请求看返回的是不是JSON数据。这一步能帮你确认这个接口是否可以直接访问。通过以上步骤我找到了目标ASMR网站的关键接口URL:https://api.asmr.example.com/v1/audios(示例地址)方法:GET参数:page1page_size50orderlatest重要请求头:User-Agent: Mozilla/5.0...,Referer: https://www.asmr.example.com/4. 代码实现从模拟请求到本地存储侦察完毕拿到了“作战地图”现在开始用Python代码实现爬虫。4.1 环境准备与请求模拟首先确保安装了requests库。如果没有在命令行执行pip install requests。import requests import json import time import os from urllib.parse import urljoin # 1. 定义目标API地址和请求头从开发者工具复制 api_url https://api.asmr.example.com/v1/audios headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.asmr.example.com/, # 如果接口需要可能还需要添加 Authorization, Cookie 等 # Cookie: your_cookie_here } # 2. 定义查询参数 params { page: 1, # 页码 page_size: 50, # 每页数量 order: latest # 排序方式 } # 3. 发送GET请求 try: response requests.get(api_url, headersheaders, paramsparams, timeout10) # 检查请求是否成功 response.raise_for_status() # 如果状态码不是200会抛出HTTPError异常 print(f请求成功状态码{response.status_code}) except requests.exceptions.RequestException as e: print(f请求失败{e}) exit() # 4. 解析返回的JSON数据 data response.json() # 打印一下数据结构方便后续提取 print(json.dumps(data, indent2, ensure_asciiFalse))运行这段代码如果成功你会在终端看到打印出来的、格式化的JSON数据。接下来就是从中提取我们需要的信息。4.2 解析数据与提取音频链接观察打印出的JSON结构。假设数据结构如下{ code: 0, msg: success, data: { list: [ { id: 101, title: 雨声白噪音, audio_url: https://cdn.asmr.example.com/audio/101.mp3, duration: 1800, artist: 自然之声 }, // ... 更多音频对象 ], total: 150, has_more: true } }我们需要从data[list]这个列表里遍历每一个音频对象提取title和audio_url。# 接上面的代码 if data.get(code) 0: # 假设code为0表示成功 audio_list data[data][list] for audio in audio_list: audio_title audio[title] audio_url audio[audio_url] print(f标题{audio_title}) print(f链接{audio_url}) # 这里可以调用下载函数 # download_audio(audio_url, audio_title) else: print(f接口返回错误{data.get(msg)})4.3 实现稳健的音频下载功能下载文件时一定要使用流式模式这样可以避免一次性将整个文件加载到内存对于大文件尤其重要。同时要设置合理的超时和重试机制。def download_audio(url, filename, save_dirdownloads): 下载音频文件 :param url: 音频文件直链 :param filename: 保存的文件名不含后缀 :param save_dir: 保存目录 # 创建保存目录 if not os.path.exists(save_dir): os.makedirs(save_dir) # 从URL中提取文件后缀或根据内容类型判断 # 简单处理假设URL最后包含后缀 file_ext os.path.splitext(url)[1] # 例如 .mp3 if not file_ext: file_ext .mp3 # 默认后缀 save_path os.path.join(save_dir, f{filename}{file_ext}) # 避免文件名中的非法字符Windows import re save_path re.sub(r[:/\\|?*], _, save_path) # 流式下载 try: # 设置streamTrue并可以添加一些请求头比如模拟浏览器接受音频 headers {User-Agent: Mozilla/5.0} resp requests.get(url, headersheaders, streamTrue, timeout30) if resp.status_code 200: total_size int(resp.headers.get(content-length, 0)) print(f开始下载{filename}大小约{total_size / 1024 / 1024:.2f} MB) downloaded_size 0 with open(save_path, wb) as f: for chunk in resp.iter_content(chunk_size1024*1024): # 每次1MB if chunk: f.write(chunk) downloaded_size len(chunk) # 可以在这里添加进度显示 # if total_size: # percent downloaded_size / total_size * 100 # print(f\r进度{percent:.1f}%, end) print(f 下载完成 - {save_path}) else: print(f下载失败状态码{resp.status_code}, URL: {url}) except Exception as e: print(f下载 {filename} 时出错{e}) # 如果出错删除可能不完整的文件 if os.path.exists(save_path): os.remove(save_path) # 在主循环中调用 for idx, audio in enumerate(audio_list): audio_title audio[title] audio_url audio[audio_url] # 使用索引和标题组合作为文件名避免重名 safe_filename f{idx1:03d}_{audio_title} download_audio(audio_url, safe_filename) # 礼貌性延迟避免请求过快 time.sleep(1)4.4 处理分页与循环抓取通常音频列表不止一页。我们需要根据接口返回的信息如has_more字段或根据total和page_size计算总页数来循环请求所有页面。def crawl_all_audios(base_url, headers, start_page1, max_pagesNone): 爬取所有分页的音频 page start_page all_audios [] params_template { page: page, page_size: 50, order: latest } while True: print(f正在抓取第 {page} 页...) params_template[page] page try: resp requests.get(base_url, headersheaders, paramsparams_template, timeout10) resp.raise_for_status() data resp.json() except Exception as e: print(f第 {page} 页请求失败{e}) break # 判断接口返回状态 if data.get(code) ! 0: print(f第 {page} 页接口返回错误{data.get(msg)}) break current_list data[data][list] if not current_list: # 如果当前页没有数据说明到底了 print(没有更多数据了。) break all_audios.extend(current_list) print(f 本页获取到 {len(current_list)} 条音频。) # 判断是否还有下一页 has_more data[data].get(has_more, False) # 或者通过计算 total data[data].get(total, 0) # if page * params_template[page_size] total: break if not has_more: print(已到达最后一页。) break page 1 # 如果设置了最大页数限制 if max_pages and page max_pages: print(f已达到最大抓取页数 {max_pages}。) break # 重要每次请求间隔一段时间避免给服务器造成压力 time.sleep(2) return all_audios # 使用函数 all_audios crawl_all_audios(api_url, headers, start_page1) print(f总共获取到 {len(all_audios)} 条音频信息。) # 然后遍历 all_audios 进行下载5. 进阶策略与常见问题排雷一个能稳定运行的爬虫必须考虑反爬机制和异常处理。以下是几个关键点。5.1 请求头伪装与会话保持User-Agent是最基本的但有些网站会检查更多的头信息。一个比较完整的、模拟浏览器的请求头可以这样设置headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: application/json, text/javascript, */*; q0.01, # 声明接受JSON Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Referer: https://www.asmr.example.com/, # 来源页非常重要 X-Requested-With: XMLHttpRequest, # 表明是Ajax请求 Connection: keep-alive, # Cookie: ... # 如果需要登录从这里添加 }对于需要保持登录状态的网站使用requests.Session()对象是更好的选择它会自动管理Cookies。session requests.Session() session.headers.update(headers) # 为会话设置默认头 # 如果需要先登录 login_data {username: xxx, password: xxx} login_resp session.post(login_api_url, datalogin_data) # 登录后session会自动保存登录后的cookies # 后续的请求都用这个session resp session.get(api_url, paramsparams)5.2 应对频率限制与IP封锁这是爬虫工程师的日常斗争。延迟Sleep在每次请求之间加入随机延迟time.sleep(random.uniform(1, 3))模拟人类操作。代理IP池如果单个IP请求过于频繁被封锁就需要使用代理IP。可以从付费或免费的代理IP服务商获取IP列表然后在请求时通过proxies参数使用。proxies { http: http://your_proxy_ip:port, https: http://your_proxy_ip:port, } response requests.get(url, headersheaders, proxiesproxies)处理验证码如果遇到验证码通常意味着你的爬虫行为已经被识别。这时需要降低请求频率或者考虑引入验证码识别服务如OCR、打码平台但这会大大增加复杂度和成本。5.3 异常处理与日志记录健壮的爬虫必须能妥善处理各种异常并记录下发生了什么方便排查。网络异常requests.exceptions.ConnectionError,Timeout等。HTTP错误如404未找到、403禁止访问、429请求过多、500服务器内部错误等。数据解析错误JSON解码错误、键不存在KeyError等。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[logging.FileHandler(crawler.log), logging.StreamHandler()]) def safe_request(url, session, paramsNone, max_retries3): for i in range(max_retries): try: resp session.get(url, paramsparams, timeout15) resp.raise_for_status() return resp except requests.exceptions.Timeout: logging.warning(f请求超时第{i1}次重试: {url}) time.sleep(2 ** i) # 指数退避 except requests.exceptions.HTTPError as e: logging.error(fHTTP错误 {resp.status_code}: {url}) if resp.status_code 429: # 请求过多 wait_time int(resp.headers.get(Retry-After, 60)) logging.info(f收到429等待{wait_time}秒) time.sleep(wait_time) continue else: break # 其他HTTP错误可能无法通过重试解决 except requests.exceptions.RequestException as e: logging.error(f请求异常: {e}) time.sleep(5) logging.error(f请求失败已达最大重试次数: {url}) return None5.4 数据去重与增量爬取如果爬虫需要定期运行下载新音频而不是每次都全部重新下载就需要做增量处理。记录已下载ID每次成功下载后将音频的唯一ID如audio[id]记录到一个文件或数据库里。爬取时过滤下次爬取时先加载已下载的ID列表只处理不在列表中的新音频。使用数据库对于大规模爬取使用SQLite或MySQL等数据库来管理爬取状态、音频元数据等会更加高效和可靠。6. 项目复盘与经验之谈做完这个项目最大的感触是爬虫的核心从“解析HTML”变成了“分析网络请求”。面对动态加载的网站开发者工具里的Network面板就是你的“眼睛”。能否快速、准确地找到那个承载数据的XHR请求决定了项目的成败。几个关键心得先人工后自动化写代码前一定要在浏览器里手动把整个数据加载流程走通看清楚每一个请求和响应。不要想当然。参数不是一成不变的分页参数page、时间戳_t、加密签名sign等都可能随着请求变化。要理解每个参数的含义是固定值、递增值还是需要从上一个响应中提取。尊重robots.txt与版权在爬取任何网站前检查其robots.txt文件通常在网站根目录如https://www.example.com/robots.txt尊重网站设置的限制。更重要的是明确你爬取数据的目的。ASMR音频通常是创作者的心血用于个人学习、研究或欣赏是合理的但未经授权用于商业分发或公开传播可能涉及版权问题。务必遵守相关法律法规和网站的使用条款。控制频率做个“好人”在代码中主动添加延迟避免对目标服务器造成过大压力。你的爬虫行为不应该影响网站的正常服务。错误处理是必修课网络环境复杂服务器也可能不稳定。完善的异常处理、重试机制和日志记录能让你的爬虫在无人值守时也能稳定运行并在出错时告诉你原因。这个项目虽然以ASMR网站为例但其中逆向Ajax接口、模拟请求、处理分页和反爬的思路完全适用于其他任何动态加载内容的网站比如新闻资讯、电商商品列表、社交媒体动态等。掌握了这套方法你就拥有了爬取现代Web应用的通用能力。
延伸阅读

更多相关文章

2026/10/25 2:02:05

Beyond Compare 多平台文件对比完整教程:功能、命令行与问题排查

在日常开发和运维工作中,文件对比是一个高频操作。无论是核对配置文件差异、检查代码分支改动,还是确定两个目录是否完全一致,靠“肉眼”逐一审查不仅耗时,还容易漏掉细节。Beyond Compare 长期被开发者、测试人员和运维工程师作为…

2026/10/25 2:02:13

基于Hypermesh14.0的汽车内外饰件快速建模全流程指南

刚接触汽车内外饰件有限元建模的同学,通常会遇到两个问题:一是几何数据质量参差不齐,清理半天还是补不全面;二是网格表面上看画完了,一检查全是超差单元,返工改稿把项目周期拖得很长。围绕“基于Hypermesh1…

2026/10/26 3:15:58

16串磷酸铁锂BMS源码实战:AFE采样、保护均衡与SoC估算

简介:这是一份面向嵌入式开发与新能源汽车电子学习者的国内电池管理系统源码,围绕主机XC2287M、从机MC9S08DZ60以及500kbps的CAN总线通信展开,可用于理解电池参数采集、均衡控制、故障检测、荷电状态估算、健康状态评估和异常保护等核心逻辑。…

2026/10/26 3:15:58

注册表模式在中大型代码辅助系统中的应用与实践

1. Claude Code工具系统的架构挑战与设计目标在构建一个包含40工具的中大型代码辅助系统时,传统的硬编码集成方式会面临几个典型问题。首先是工具间的依赖关系管理——当工具B需要调用工具A的功能时,直接引用会导致编译期强耦合。其次是动态扩展困难&…

2026/10/26 3:15:58

Ollama+WebUI+AnythingLLM本地知识库部署与RAG调优

1. 先想清楚:为什么要自己搭一套知识库把 Ollama、WebUI、AnythingLLM 这三个东西拼在一起做知识库,这两年问的人越来越多,但很多人一上来就照着教程敲命令,装完了发现模型跑起来了、界面也能打开,可一问问题就开始胡说…

2026/10/26 3:15:58

C#串口读取电子秤重量:帧解析、粘包处理与判稳滤波

简介:这是一份基于 C# 的电子秤重量读取示例工程,面向需要对接串口称重设备的 .NET 开发者与物联网入门学习者,重点解决串口通信、委托事件接收和重量数据解析等问题。压缩包共 21 个文件,以 6 个 cs 源码为核心,配有 …

2026/10/26 3:15:58

MySQL性能优化实战:从慢查询分析到架构调优

1. 为什么MySQL性能优化如此重要?在当今数据驱动的时代,MySQL作为最流行的开源关系型数据库之一,承载着无数企业的核心业务数据。我曾参与过一个电商平台的数据库优化项目,在未进行任何优化前,高峰期订单查询响应时间长…

2026/10/26 3:10:58

Block Copy内存布局优化:从原理到性能调优实践

1. Block Copy 内存布局解析:从原理到实践在系统级编程和性能优化领域,内存操作的高效性直接决定了程序性能的上限。Block Copy(块拷贝)作为内存操作的基础单元,其背后的内存布局设计直接影响着拷贝操作的效率。不同于…

2026/10/26 0:20:20

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/26 1:53:47

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/26 0:20:17

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/26 0:00:48

Nested Learning:解决AI灾难性遗忘的分层记忆架构

1. 为什么AI需要"记忆"能力?在传统深度学习模型中,我们常遇到一个根本性问题:模型在处理新任务时,往往会完全遗忘之前学到的知识。这种现象被业界称为"灾难性遗忘"(Catastrophic Forgetting)。想象一下&#…

2026/10/26 0:00:48

企业级大模型应用:OCR预处理、Dify编排与DeepSeek落地实战

1. 这不是“用大模型”,而是重构工作流的起点“大模型的应用和工具”——这八个字现在被贴在无数技术分享、招聘JD、内部立项书的标题栏里,但绝大多数人点进去后看到的,是零散的API调用示例、几个开源UI截图、或者一句“接入LLM就能降本增效”…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑