js生成的html如何爬取
- 前端开发
- 2025-08-17
- 6
核心挑战:为何传统爬虫失效?
现代网页普遍采用异步加载技术(AJAX/Fetch API),其本质特征为:
| 现象类型 | 表现形式 | 技术根源 |
|—————-|——————————|————————|
| 空白初始页面 | 首次请求仅返回空容器/占位符 | DOM由JS动态构建 |
| XHR二次请求 | 网络面板可见额外API调用 | XMLHttpRequest
/fetch
|
| 加密数据流 | 响应体为二进制密文 | WebAssembly/WASM沙箱 |
| 单页应用(SPA) | URL不变但内容切换 | History API路由控制 |
传统爬虫(如requests+BeautifulSoup
)只能获取初始HTML骨架,无法直接捕获后续JS生成的内容,需采用能模拟浏览器行为的方案。
四大主流解决方案深度对比
方案1:Selenium + ChromeDriver(全能型)
适用场景:复杂交互式页面(表单提交/滚动加载/弹窗处理)
工作原理:启动真实Chrome实例,完全复现用户操作环境
️ 实施步骤:
from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager import time # 配置无头模式(可选) options = webdriver.ChromeOptions() options.add_argument('--headless') # 后台运行 options.add_argument('--disable-gpu') # 初始化驱动 driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options) try: driver.get("https://example.com") # 目标网址 time.sleep(3) # 等待JS执行完成(可根据需求调整) # 获取渲染后的页面源码 html_content = driver.page_source # 此处可接续解析逻辑(BS4/lxml等) finally: driver.quit()
优势:支持CSS选择器/XPath定位,可模拟点击/输入等操作
️ 缺点:资源占用高(约500MB内存),速度较慢(冷启动~2秒)
方案2:Pyppeteer/Playwright(轻量化替代)
适用场景:中等复杂度页面,追求更快速度
工作原理:基于Chromium内核的轻量级浏览器引擎
代码示例(Playwright):
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto("https://example.com", wait_until="domcontentloaded") # 关键等待条件 # 等待特定元素出现(更精准的控制) page.wait_for_selector("div.data-container") html_content = page.content() # 获取完整HTML browser.close()
优势:原生支持异步API,内置截图/PDF生成功能
性能提升:相比Selenium快3-5倍,内存占用降低60%
方案3:逆向分析API接口(高效首选)
核心思路:通过开发者工具找到数据源接口,直连后端服务
操作流程:
- 打开浏览器F12 → Network面板
- 刷新页面 → 筛选XHR/fetch请求
- 识别返回JSON/数据的请求URL
- 构造请求参数(注意Cookie/Headers)
典型请求头构造示例:
| 字段 | 值 | 说明 |
|—————|——————————|————————–|
| User-Agent | Mozilla/5.0 (Windows NT...)
| 模拟浏览器访问 |
| Referer | https://example.com/list
| 防盗链机制 |
| Cookie | sessionid=xxx; token=yyy
| 身份验证凭证 |
| Content-Type | application/json
| POST请求常见类型 |
代码实现(requests):
import requests import json headers = { "User-Agent": "Mozilla/5.0...", "Referer": "https://example.com/list", "Cookie": "sessionid=abc123" } params = {"page": 1, "size": 20} response = requests.get("https://api.example.com/data", headers=headers, params=params) data = response.json() # 直接获取结构化数据
优势:毫秒级响应,无需渲染JS,适合大规模采集
️ 限制:部分网站采用JWT令牌/非对称加密,需破解签名算法
方案4:PhantomJS(淘汰预警)
现状:已停止维护,存在安全破绽,推荐迁移至上述方案
关键技术增强点
智能等待策略
方法 | 适用场景 | 代码片段 |
---|---|---|
time.sleep() |
简单粗暴(不推荐) | time.sleep(5) |
显式等待(Explicit) | 特定元素出现 | WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "main"))) |
隐式等待(Implicit) | 全局超时设置 | driver.implicitly_wait(10) |
滚动触发加载 | 无限滚动列表 | driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") |
反爬对抗策略
威胁类型 | 解决方案 | 实施方式 |
---|---|---|
IP封禁 | 代理池轮换 | 集成ProxyMesh/Luminati代理服务 |
人机验证 | 打码平台/AI识别 | Tesseract OCR + 第三方验证码破解 |
请求频率限制 | 随机延迟+分布式节点 | random.uniform(1,3) 间隔请求 |
指纹追踪 | 动态UA/Canvas指纹混淆 | fake_useragent库 + Stealthy模块 |
数据解析优化
- 正则表达式:适用于非标准HTML结构(如
<script>
标签内的JSONP数据) - JsonPath:比XPath更适合处理嵌套JSON数据
- AST解析:对付混淆过的JS代码(需借助esprima等库)
实战案例:电商评论爬取
假设目标站点特点:
- 评论通过滚动加载(无限下拉)
- 数据来源于
/comments?pid=XXX&page=Y
接口 - 需要登录态才能访问
混合方案设计:
- 使用Selenium登录获取Cookie
- 提取Cookie中的
auth_token
- 改用requests直接调用API接口
- 并行多线程采集(控制并发数≤5)
关键代码片段:
# Step1: Selenium登录获取Cookie driver.get("https://login.example.com") driver.find_element(By.NAME, "username").send_keys("user") driver.find_element(By.NAME, "pwd").send_keys("pass") driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click() cookies = driver.get_cookies() # Step2: 构造API请求 session = requests.Session() for cookie in cookies: session.cookies.set(cookie['name'], cookie['value']) # Step3: 批量采集评论 results = [] for page in range(1, 10): url = f"https://api.example.com/comments?pid=123&page={page}" resp = session.get(url, headers={"Accept": "application/json"}) results.extend(resp.json()["data"])
常见问题FAQs
Q1: 为什么有时能抓到数据,有时抓不到?
原因分析:
- 时机问题:JS执行未完成即开始解析(解决方案:增加等待时间/监听事件)
- 环境差异:本地IP被屏蔽(解决方案:启用高质量代理)
- 版本冲突:网站检测到自动化工具特征(解决方案:更新User-Agent/禁用图片加载)
- 缓存机制:第一次访问成功后续失败(解决方案:强制刷新
Ctrl+F5
)
排查步骤:
① 手动复制目标元素的XPath到浏览器控制台测试有效性
② 在代码中插入print(len(driver.find_elements(...)))
验证元素数量
③ 启用日志记录(logging
模块)追踪完整请求流程
Q2: 如何处理动态加载的图片/视频地址?
技术路线:
- 拦截网络请求:使用Mitmproxy中间人代理截获媒体资源URL
- 延迟加载检测:通过
IntersectionObserver
API判断元素是否进入视口 - 懒加载破解:修改
img
标签的data-src
属性为src
后重新加载 - WebSocket监控:对实时推送类应用(如直播弹幕)建立长连接监听
代码示例(修改懒加载):
// 在Page对象创建后注入脚本 await page.evaluate(() => { const lazyImages = document.querySelectorAll('img[data-src]'); lazyImages.forEach(img => { img.src = img.dataset.src; }); });
伦理与法律提示
- robots.txt协议:遵守
/robots.txt
文件中的规则(可通过requests.get("https://example.com/robots.txt")
查看) - 版权风险:商业用途需获得网站授权(《著作权法》第10条)
- 个人隐私:避免采集敏感信息(身份证号/手机号等),违反《个人信息保护法》
- 技术底线:禁止突破网站安全防护(如绕过支付验证流程)
建议将采集频率控制在合理范围(一般不超过1次/秒),必要时联系网站管理员