author: 专注Python实战分享爬虫与数据分析干货title: Python爬虫实战⑤抓取动态网页AJAX与API接口解析update: 2026-04-26tags: Python,爬虫,动态网页,AJAX,API,JSON,异步加载作者专注Python实战分享爬虫与数据分析干货更新时间2026年4月适合人群已掌握基础爬虫、想突破动态网页抓取的开发者前言页面数据看不到因为它是动态加载的你一定遇到过这种情况——浏览器能看到数据但requests抓下来是空的网页源码里没有你要的内容但页面上明明显示着点击加载更多后URL不变但内容增加了这不是bug是AJAX动态加载。数据不在HTML里而是通过JavaScript额外请求API获取。学会抓动态网页你的爬虫能力直接翻倍。一、什么是AJAX动态加载1.1 传统网页 vs 动态网页传统网页静态浏览器请求 → 服务器返回完整HTML → 浏览器渲染显示数据就在HTML源码里requests直接能拿到。动态网页AJAX浏览器请求HTML → HTML是空的/半空的 ↓ 浏览器执行JS → JS发起AJAX请求API → 服务器返回JSON数据 → JS填充到页面数据在API接口的JSON响应里requests抓HTML拿不到数据。1.2 怎么判断是不是动态网页importrequestsfrombs4importBeautifulSoup headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36}urlhttps://example.com/dynamic-pageresponserequests.get(url,headersheaders,timeout10)soupBeautifulSoup(response.text,html.parser)# 如果页面上的数据在HTML里找不到就是动态加载contentsoup.find(div,class_data-list)ifcontent:print(静态网页数据在HTML里)else:print(动态网页需要找API接口)二、用浏览器开发者工具找API接口2.1 开发者工具打开方式Chrome/Edge按F12或右键→检查切换到 Network网络标签勾选 “Preserve log”保留日志选择 XHR 或 Fetch 过滤器2.2 找到API请求的步骤打开目标网页F12 → Network → XHR在网页上触发数据加载刷新/翻页/滚动/点击观察Network里新出现的请求点击请求 → Preview/Response 查看返回数据点击 Headers 查看请求URL、方法、参数2.3 用代码模拟API请求找到API接口后直接用requests请求JSON数据importrequestsimportjson headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Referer:https://example.com/,Accept:application/json, text/plain, */*,}# API接口URL从开发者工具中获取api_urlhttps://example.com/api/data/list# 请求参数params{page:1,size:20,keyword:Python,}responserequests.get(api_url,headersheaders,paramsparams,timeout10)dataresponse.json()print(f状态:{data.get(status,unknown)})print(f数据条数: {len(data.get(data, {}).get(list, []))})# 提取具体字段foritemindata.get(data,{}).get(list,[]):print(f 标题:{item.get(title,)})print(f 价格:{item.get(price,)})print(f 链接:{item.get(url,)})print()三、常见AJAX接口格式与解析3.1 GET请求 URL参数importrequests headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Referer:https://example.com/,}# 翻页接口forpageinrange(1,6):api_urlhttps://example.com/api/articlesparams{page:page,per_page:20,category:tech,}responserequests.get(api_url,headersheaders,paramsparams,timeout10)dataresponse.json()articlesdata.get(data,{}).get(articles,[])print(f第{page}页:{len(articles)}篇文章)forarticleinarticles:print(f -{article[title]})3.2 POST请求 JSON参数importrequestsimportjson headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Content-Type:application/json;charsetUTF-8,Referer:https://example.com/,}api_urlhttps://example.com/api/searchpayload{keyword:Python爬虫,page:1,pageSize:20,filters:{category:book,priceRange:[0,100]}}responserequests.post(api_url,headersheaders,jsonpayload,timeout10)dataresponse.json()resultsdata.get(result,{}).get(items,[])print(f搜索到{len(results)}条结果)foriteminresults:print(f{item[name]}- ¥{item[price]})3.3 需要Token认证的APIimportrequests# 第1步登录获取tokenlogin_urlhttps://example.com/api/loginlogin_data{username:user,password:pass}responserequests.post(login_url,jsonlogin_data,timeout10)tokenresponse.json().get(token,)print(f获取Token:{token[:20]}...)# 第2步带token请求APIheaders{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Authorization:fBearer{token},Referer:https://example.com/,}api_urlhttps://example.com/api/user/dataresponserequests.get(api_url,headersheaders,timeout10)dataresponse.json()print(f用户数据:{data})四、动态网页实战案例4.1 抓取新浪微博搜索结果importrequestsimportjsonimporttimeimportrandom headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Referer:https://s.weibo.com/,Accept:application/json, text/plain, */*,X-Requested-With:XMLHttpRequest,}keywordPythonall_posts[]forpageinrange(1,6):api_urlhttps://s.weibo.com/ajax/jsonp/searchparams{keyword:keyword,page:page,}print(f搜索 {keyword} 第{page}页...,end )try:responserequests.get(api_url,headersheaders,paramsparams,timeout10)# 有些API返回JSONP格式需要去掉回调函数包装textresponse.textiftext.startswith(jQuery):texttext[text.index(()1:text.rindex())]datajson.loads(text)cardsdata.get(data,{}).get(cards,[])forcardincards:mblogcard.get(mblog,{})ifmblog:all_posts.append({用户:mblog.get(user,{}).get(screen_name,),内容:mblog.get(text,)[:50],转发:mblog.get(reposts_count,0),评论:mblog.get(comments_count,0),点赞:mblog.get(attitudes_count,0),})print(f获取{len(cards)}条)time.sleep(random.uniform(2,5))exceptExceptionase:print(f失败:{e})print(f\n共获取{len(all_posts)}条微博)4.2 抓取知乎热榜importrequests headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Referer:https://www.zhihu.com/hot,}api_urlhttps://www.zhihu.com/api/v3/feed/topstory/hot-lists/totalparams{limit:50,desktop:true}try:responserequests.get(api_url,headersheaders,paramsparams,timeout10)dataresponse.json()itemsdata.get(data,[])print(f知乎热榜共{len(items)}条)print(*60)fori,iteminenumerate(items,1):targetitem.get(target,{})titletarget.get(title,)hotitem.get(detail_text,)print(f{i:2}.{title})print(f 热度:{hot})exceptExceptionase:print(f抓取失败:{e})print(提示知乎API可能需要登录Cookie才能访问)五、处理JSONP响应有些API返回的不是标准JSON而是JSONP带回调函数名// 标准JSON {name: 张三, age: 25} // JSONP callback({name: 张三, age: 25}) jQuery123456({name: 张三, age: 25})importjsonimportredefparse_jsonp(jsonp_text):解析JSONP响应提取JSON数据# 方法1用正则提取括号内内容matchre.search(r\((.)\);?$,jsonp_text,re.DOTALL)ifmatch:json_strmatch.group(1)returnjson.loads(json_str)# 方法2如果是标准JSON直接解析try:returnjson.loads(jsonp_text)exceptjson.JSONDecodeError:pass# 方法3暴力去掉常见回调前缀forprefixin[callback,jQuery]:ifjsonp_text.startswith(prefix):json_strjsonp_text[jsonp_text.index(()1:jsonp_text.rindex())]returnjson.loads(json_str)raiseValueError(f无法解析JSONP:{jsonp_text[:100]}...)# 使用示例jsonp_responsejQuery123456({status: ok, data: [1, 2, 3]})dataparse_jsonp(jsonp_response)print(data)# {status: ok, data: [1, 2, 3]}六、无限滚动加载很多网站如微博、淘宝、Pinterest用无限滚动代替分页6.1 找到滚动加载的API初始页面https://example.com/feed 滚动加载https://example.com/api/feed?afterabc123count20 再次滚动https://example.com/api/feed?afterdef456count20after参数通常是上一批最后一条数据的ID或时间戳。6.2 模拟无限滚动importrequestsimporttimeimportrandom headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36,Referer:https://example.com/feed,}api_urlhttps://example.com/api/feedall_items[]after_cursorNone# 游标初始为Nonemax_rounds10# 最多加载10轮forround_numinrange(1,max_rounds1):params{count:20}ifafter_cursor:params[after]after_cursorprint(f加载第{round_num}轮...,end )try:responserequests.get(api_url,headersheaders,paramsparams,timeout10)dataresponse.json()itemsdata.get(items,[])ifnotitems:print(没有更多数据了)breakall_items.extend(items)# 获取下一轮的游标after_cursordata.get(next_cursor)ordata.get(pagination,{}).get(after)ifnotafter_cursor:print(已到最后一页)breakprint(f获取{len(items)}条累计{len(all_items)}条)time.sleep(random.uniform(1,3))exceptExceptionase:print(f失败:{e})breakprint(f\n共加载{len(all_items)}条数据)七、知识卡概念说明AJAX异步JavaScript请求页面不刷新即可获取数据API接口后端提供的数据接口通常返回JSONXHRXMLHttpRequestAJAX请求的类型JSONP带回调函数的JSON跨域请求用Network面板浏览器开发者工具的网络请求监控游标(cursor)无限滚动的分页标记指向下一批数据TokenAPI访问凭证放在请求头里X-Requested-WithAJAX请求标识值通常为XMLHttpRequestReferer告诉API你从哪个页面来的Content-Type请求体格式JSON为application/json八、课后作业必做题用浏览器开发者工具找到任意一个动态网站的API接口用requests直接请求API提取JSON数据实现一个简单的动态网页爬虫至少3页选做题写一个通用函数自动解析JSONP响应实现无限滚动页面的数据抓取完成作业的同学把运行截图发到评论区动态网页 爬虫的进阶门槛。跨过去90%的网站你都能爬。本篇要点判断静态/动态网页用开发者工具找API接口GET/POST/Token三种API请求方式JSONP解析无限滚动加载处理下一篇我们学习反爬虫应对策略——IP代理、随机延迟、UA伪装让你不被封。收藏 关注专栏更新不迷路有问题欢迎评论区留言大家一起讨论标签Python | 爬虫 | 动态网页 | AJAX | API | JSON | 异步加载 | 数据抓取