从“抓不到“到“抓得全“:requests-html网页解析实战,3招拿下JS动态数据
从抓不到到抓得全requests-html网页解析实战3招拿下JS动态数据【免费下载链接】requests-htmlPythonic HTML Parsing for Humans™项目地址: https://gitcode.com/gh_mirrors/re/requests-html凌晨一点同事把一段爬虫代码拍在我桌上帮我看下这个页面明明有数据我抓出来却是空的。他抓的是一个内部培训站的课程列表浏览器里整整齐齐排着三十行课程requests拿回来的 HTML 里却只有一张空壳。这种浏览器有、源码没有的诡异现象几乎每个做数据抓取的人都撞过——问题不在网络请求而在 JavaScript 渲染。这篇文章就用 requests-html 这个网页解析库从一次真实救火出发把静态抓取、JS 渲染、异步批量这三关逐一打通。先搞清抓不到的三种病因排查问题之前先给数据抓取做个快速体检。网页上的数据大体分三种对应三种完全不同的处理策略数据形态典型特征传统方案是否有效纯静态 HTML数据写在返回的源码里✅ 有效懒加载 / 异步接口滚动后 data 才从接口拉回❌ 拿不到JS 动态渲染DOM 由脚本生成❌ 拿不到同事抓的就是第三种。他的原方案是经典的三件套urlopen拿源码、正则抠字符串、BeautifulSoup 收尾。这套组合对付静态页面绰绰有余但遇到前端框架React/Vue 之类生成的页面就彻底哑火——服务端返回的是空骨架真正的数据要靠浏览器里的 JavaScript 去填充。要治这种病思路只有一条把发请求和跑浏览器合并成一件事。requests-html 干的就是这个活它外面套着 requests 的皮里面装着解析引擎还内置了一个无头 Chromium能在抓取时像真人浏览器一样把脚本执行完。第一关HTMLSession 取代裸 requests三行代码建会话先看最基础的动作。requests-html 对 requests 的老用户几乎零迁移成本把requests.get()换成session.get()即可返回的对象在请求能力之外多了一个会解析的.html属性from requests_html import HTMLSession session HTMLSession() response session.get(https://python.org/) # 这行是关键响应对象自带解析能力 page response.html # 一行拿到全部链接自动去重、剔除锚点 print(len(page.links)) print(len(page.absolute_links)) # 绝对地址版本HTMLSession并不是简单包一层它会自动模拟浏览器 User-Agent、自动跟随重定向、复用连接池、持久化 Cookie。这些特性在后面的反爬环节都会派上用场。第二关CSS 选择器与 XPath两把手术刀交替用会话建好了接下来是定位数据。requests-html 的find()方法在项目源码 requests_html.py 的BaseParser类中定义支持完整的 CSS 选择器语法用法和 jQuery 几乎一致# 取第一个 #about 元素 about page.find(#about, firstTrue) # 按 class 筛选所有链接 nav_links page.find(nav a) # 按属性筛选找所有外链 external page.find(a[href^http]) # 按包含文本筛选找含 python 的元素 matches page.find(containingpython)定位到元素后.text拿文本、.attrs拿属性字典、.html拿内层 HTML.links和.absolute_links拿元素内的链接集合。项目测试文件 tests/test_requests_html.py 里有一整套断言可以当用法速查表来读。习惯 XPath 的也不用纠结xpath()方法平行存在项目文档 docs/source/index.rst 里两者都有示例# XPath 同样支持拿整个文档根节点 root page.xpath(/html, firstTrue) print(root.attrs[class]) # 输出 no-js # 直接取所有 a 标签的 href 属性值列表 hrefs page.xpath(//a/href)第三关render() 唤醒沉睡的数据——真正的重头戏现在回到同事的问题。静态抓取解决了但目标站点是 JS 渲染的怎么办答案是render()方法——它在源码 requests_html.py 的HTML类中实现会启动无头 Chromium把页面完整执行一遍再拿回渲染后的 DOM# 渲染整页等 JS 跑完 response.html.render( retries3, # 加载失败自动重试 wait1, # 打开页面后先等 1 秒 scrolldown2, # 向下滚动 2 次触发懒加载 sleep1 # 每次滚动后歇 1 秒 ) # 渲染之后之前空荡荡的选择器终于有结果了 courses response.html.find(.course-item) print(f渲染后抓到 {len(courses)} 个课程)❗注意render()首次运行会往主目录~/.pyppeteer/下载一份 Chromium耗时几分钟之后就不再下载。在服务器上跑之前先确认磁盘和网络环境。render()的几个参数对应着真实用户的行为scrolldown模拟滚动很多页面滚动到哪才加载到哪、sleep给异步请求留出返回时间、retries兜底页面偶发超时。如果页面滚动后仍缺数据把scrolldown和sleep一起调大通常能解决九成问题。⚡进阶玩法render()还能直接执行自定义 JavaScript 并把返回值带回来等于在浏览器里装了个数据探头script () { const items document.querySelectorAll(.course-item); return items.map(el ({ title: el.querySelector(.title).textContent, url: el.querySelector(a).href, })); } result response.html.render(scriptscript) print(result) # 直接拿到结构化列表相对链接不用愁一个方法自动转绝对地址抓链接时最烦的一件事页面里全是/course/42这种相对路径存下来没法直接用。requests-html 内置的_make_absolute()同样定义在BaseParser中专门治这个测试文件里test_links就是靠它把 6 个相对链接全部转成了绝对地址from requests_html import HTML # 不经过网络直接解析 HTML 字符串也行 doc a href/course/42Python 入门/a html HTML(htmldoc, urlhttps://example.com/) # 相对路径自动拼接成完整 URL for link in html.links: print(link) # /course/42 print(html._make_absolute(link)) # https://example.com/course/42_make_absolute()的实现逻辑很实在没有域名就拼上基础地址有域名缺协议就补http(s)已经完整的原样返回。比自己手写urljoin省心得多。批量抓取提速AsyncHTMLSession 一次开多个页面同事的需求很快变成了帮我把十个栏目都抓下来。这时候逐页串行抓就太慢了——每页都要等渲染动辄几秒。requests-html 自带异步方案AsyncHTMLSession对应源码里的arender()方法import asyncio from requests_html import AsyncHTMLSession asession AsyncHTMLSession() async def fetch_courses(url): response await asession.get(url) # 异步渲染效果等同 render() await response.html.arender(scrolldown1, sleep1) return { url: response.html.url, count: len(response.html.find(.course-item)), } async def main(): urls [ https://example.com/python, https://example.com/data, https://example.com/web, ] # 并发跑起来各自返回结果 results await asyncio.gather(*[fetch_courses(u) for u in urls]) for r in results: print(r) asyncio.run(main())提示异步版本能并发但也要节制目标站点扛不住瞬时压力会直接把你 IP 拉黑。批量大时建议分批每批 5~10 个页面并加随机延时。把前面全串起来一个能直接跑的通关脚本下面这个脚本把三关全打通——静态抓取、JS 渲染、批量并发加上去重和结果输出就是同事最终交出去的版本from requests_html import HTMLSession, AsyncHTMLSession import asyncio BASE https://example.com/training def extract_from_page(html): 从渲染完成的页面里抠出课程清单 items [] for el in html.find(.course-item): title_el el.find(.title, firstTrue) link_el el.find(a, firstTrue) if title_el is not None: items.append({ title: title_el.text.strip(), url: html._make_absolute(link_el.attrs[href]), }) return items def fetch_static(url): 第一关不渲染直接抓静态内容 session HTMLSession() response session.get(url) session.close() return response.html def fetch_dynamic(url): 第二关渲染 JS 后再抓 session HTMLSession() response session.get(url) response.html.render(scrolldown2, sleep1) session.close() return response.html async def fetch_many(urls): 第三关异步并发批量抓 asession AsyncHTMLSession() async def one(url): response await asession.get(url) await response.html.arender(scrolldown1, sleep1) return extract_from_page(response.html) return await asyncio.gather(*[one(u) for u in urls]) if __name__ __main__: # 单页 渲染 html fetch_dynamic(f{BASE}/index.html) print(本页课程数:, len(extract_from_page(html))) # 批量异步 pages [f{BASE}/list/{i}.html for i in range(1, 4)] all_items asyncio.run(fetch_many(pages)) merged {item[url]: item for batch in all_items for item in batch} print(批量去重后共:, len(merged), 门课程) for item in list(merged.values())[:5]: print(-, item[title])三个实战中高频踩到的坑坑一抓回来还是空。先确认是不是render()没等够。用response.html.search(xxx...{}yyy)或直接len(html.text)对比渲染前后的内容量能快速判断没抓到还是没渲染完。项目 README 里那个 pythonclock 例子就是标准诊断流程。坑二被反爬拦截。先把render()停掉观察——有时数据其实在静态页里渲染反而触发风控。请求头方面HTMLSession默认已经带了浏览器风格的 User-Agent如果还不行在会话上手动补Accept和Accept-Language并优先通过 Cookie 保持会话状态。坑三下载音频、文件这类资源。抓链接只是第一步真正下载时注意用流式写入别一把梭进内存同时控制并发别让目标服务器过载。让这套抓取方案自己转起来同事的脚本最后跑在了服务器上配了一个简单的定时任务每天凌晨抓一次更新课程表数据落成 JSON 供内部系统消费。想更进一步你可以把抓下来的数据接上 SQLite 做增量归档、接到企业微信/钉钉机器人做变更推送或者把课程页面整页导出成 Markdown 存档。requests-html 的价值在于它把请求—解析—渲染揉成了一个顺手的小工具静态页面用find()动态页面加一个render()量大就换AsyncHTMLSession。同样的三板斧换成抓图片、抓视频、抓文档思路完全一致只是选择器里的标签名不同。下次再遇到浏览器有、源码没有的页面别急着上 Selenium 全家桶——先试试把浏览器塞进 requests 里三行代码问题可能就没了。【免费下载链接】requests-htmlPythonic HTML Parsing for Humans™项目地址: https://gitcode.com/gh_mirrors/re/requests-html创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Linux运维面试题

Linux运维面试题

文章目录一、云计算与传统计算的区别?以吃包子为例:云计算又分为laaS、PaaS、SaaS总结:云计算的本质—《浪潮之巅第四版下册》二、云平台运维需要做哪些安全配置?1.安全组2.ECS主机3.Nginx反向代理4.数据安全第三方防护日志处理四…

2026/10/11 16:29:04 阅读更多 →
Linux-日志查询

Linux-日志查询

一、Linux cat命令 cat 命令用于连接文件并打印到标准输出设备上。即一般用来查看文件。 1. 根据关键字查询 cat -n console.log |grep "关键字" 1.关键字查询并打印前后n行 检索日志,并显示该条日志的前后N(10)行记录&#x…

2026/10/8 9:53:30 阅读更多 →
文件被 rm 之后还能救回来吗?一文掌握 trash-cli 命令行回收站的完整用法

文件被 rm 之后还能救回来吗?一文掌握 trash-cli 命令行回收站的完整用法

文件被 rm 之后还能救回来吗?一文掌握 trash-cli 命令行回收站的完整用法 【免费下载链接】trash-cli Command line interface to the freedesktop.org trashcan. 项目地址: https://gitcode.com/gh_mirrors/tr/trash-cli 如果你在服务器上做过运维&#xff…

2026/10/10 5:53:48 阅读更多 →

最新新闻

豆瓣影评情感分析:朴素贝叶斯文本分类实战

豆瓣影评情感分析:朴素贝叶斯文本分类实战

简介:面向人工智能与自然语言处理初学者的情感分析实践项目,聚焦豆瓣Top250影评数据,基于朴素贝叶斯分类器完成正负面情感判别。资料涵盖Scrapy爬虫采集的约5万条影评语料,好评差评比例均衡,并按照4:1划分训练集与测试…

2026/10/12 13:43:55 阅读更多 →
操作系统T1考题解析:进程状态迁移与上下文切换双线推演

操作系统T1考题解析:进程状态迁移与上下文切换双线推演

1. 项目概述:这是一份面向操作系统的教学实操型考题解析,不是试卷搬运工“第2单元进程的描述与控制考试T1”——光看标题,你可能以为这只是某高校《操作系统原理》课程的一道普通期中题。但作为带过十几届系统课实验的从业者,我得…

2026/10/12 13:43:55 阅读更多 →
Java中extends与implements的区别:从语法到设计选型

Java中extends与implements的区别:从语法到设计选型

1. 为什么这两个关键字总让人犯迷糊刚入行那会儿,我在一个后台管理系统里写代码,需要定义一个订单服务类。当时脑子里想的是“订单服务是一种服务,那就用 extends 继承一个基础服务类吧”,结果写着写着发现基础服务类里有一堆跟订…

2026/10/12 13:43:55 阅读更多 →
C语言浮点数精度陷阱与避坑指南:从IEEE 754到工程实践

C语言浮点数精度陷阱与避坑指南:从IEEE 754到工程实践

1. 从"整数思维"到"浮点思维"的认知切换很多人学C语言学到浮点数这一块,都会有一种"好像懂了但又好像没懂"的感觉。声明一个float变量、打印用%f、做做加减乘除,看起来和整数没什么区别。但一旦程序跑出0.1 0.2 ! 0.3这种…

2026/10/12 13:43:55 阅读更多 →
运动估计实验从零到一:块匹配原理、实现与调参避坑指南

运动估计实验从零到一:块匹配原理、实现与调参避坑指南

1. 运动估计到底在解决一个什么问题很多人第一次接触运动估计,脑子里冒出来的画面是摄像头追踪一个跑来跑去的人。这个理解不算错,但只看到了冰山一角。运动估计真正要回答的核心问题其实非常朴素:同一场景在相邻两帧画面里,像素点…

2026/10/12 13:43:55 阅读更多 →
【yyds】大模型开发新风向:上下文工程接管AI编程,RAG真的凉了吗?用TaoToken统一Key实测进阶指南

【yyds】大模型开发新风向:上下文工程接管AI编程,RAG真的凉了吗?用TaoToken统一Key实测进阶指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 13:42:54 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →