网页抓取与自动化
这页适合作为“网页数据抓取与浏览器自动化”的起步说明。真正重要的不是会不会抓,而是先判断目标站点是静态页面、动态渲染页面,还是明确提供了 API;选错抓取方式,后面维护成本会很高。
先按页面类型选方案
- 静态 HTML:优先
curl+cheerio/BeautifulSoup - 强依赖前端渲染:优先
Playwright - 需要浏览器交互和脚本执行:
Playwright/Puppeteer - 有官方 API:优先直接用 API,不要硬抓页面
只要能直接用 API,就尽量别走页面抓取。
curl 基础
# GET 请求
curl https://api.example.com/data
# 带 Header
curl -H "Authorization: Bearer token" https://api.example.com
# POST JSON
curl -X POST https://api.example.com/data \
-H "Content-Type: application/json" \
-d '{"key": "value"}'
# 下载文件
curl -O https://example.com/file.zip
curl -o output.zip https://example.com/file.zip
# 跟随重定向
curl -L https://example.com
# 查看响应头
curl -I https://example.com
Node.js 抓取
cheerio(静态页面)
pnpm add cheerio
import * as cheerio from "cheerio";
const html = await fetch("https://example.com").then((r) => r.text());
const $ = cheerio.load(html);
$("h2").each((i, el) => {
console.log($(el).text());
});
const links = $("a")
.map((i, el) => $(el).attr("href"))
.get();
Playwright(动态页面)
pnpm add playwright
npx playwright install chromium
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
// 等待元素
await page.waitForSelector(".content");
// 获取文本
const title = await page.textContent("h1");
// 截图
await page.screenshot({ path: "screenshot.png", fullPage: true });
// 点击与输入
await page.fill("#search", "keyword");
await page.click("button[type=submit]");
await browser.close();
Puppeteer
pnpm add puppeteer
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto("https://example.com", { waitUntil: "networkidle2" });
const data = await page.evaluate(() => {
return Array.from(document.querySelectorAll(".item")).map((el) => ({
title: el.querySelector("h3")?.textContent,
link: el.querySelector("a")?.href,
}));
});
await browser.close();
Python 抓取
requests + BeautifulSoup
import requests
from bs4 import BeautifulSoup
resp = requests.get("https://example.com")
soup = BeautifulSoup(resp.text, "html.parser")
for item in soup.select(".item"):
title = item.select_one("h3").text
link = item.select_one("a")["href"]
print(title, link)
实用技巧
请求头伪装
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
Accept: "text/html,application/xhtml+xml",
"Accept-Language": "zh-CN,zh;q=0.9",
};
请求间隔
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
for (const url of urls) {
await fetch(url);
await sleep(1000 + Math.random() * 2000); // 1-3 秒随机间隔
}
代理
import { HttpsProxyAgent } from "https-proxy-agent";
const agent = new HttpsProxyAgent("http://proxy:8080");
const resp = await fetch(url, { agent });
推荐执行顺序
一个稳妥的抓取流程通常是:
- 先观察目标页面是静态还是动态
- 再检查网络请求里是否已有可用 API
- 确定数据结构后,再决定用轻量抓取还是浏览器自动化
- 最后补节流、重试和异常处理
如果一开始就上完整浏览器自动化,很多简单任务会被做得过重。
常见问题
本地能抓,跑一会就失败
高频原因通常包括:
- 请求频率太高
- 缺少必要 Header 或 Cookie
- 目标站点风控升级
- 依赖页面结构,DOM 一改就失效
Playwright 很重,跑得慢
这很正常。只有在必须执行 JS、等待登录态、模拟点击时,才值得用浏览器自动化;静态页面优先轻量方案。
数据抓到了,但不好维护
建议尽早做这几件事:
- 把选择器集中管理
- 记录页面结构假设
- 为关键字段做兜底和空值判断
- 降低对文案和顺序的强依赖
风险提醒
- 尊重目标站点条款、robots 和访问频率限制
- 不要抓取超出授权范围的内容
- 涉及账号、Cookie、验证码、付费内容时尤其要谨慎
延伸阅读
参考链接
- Playwright — 浏览器自动化
- Puppeteer — Chrome 自动化
- cheerio — 服务端 jQuery
- curl 手册 — 官方文档