最省事的是用pd.read_html()直接解析静态表格,但需先过反爬关:加User-Agent、控制请求频率、确认HTML中存在;JS渲染则须换Playwright/Selenium获取渲染后HTML。

用 pandas.read_html() 抓表格最省事,但得先过反爬关
多数静态网页表格能直接用 pandas.read_html() 解析,它底层调用 lxml 或 bs4,返回 list of DataFrame。但遇到 JS 渲染、登录墙、频率限制就立刻失效。
实操建议:
- 先用浏览器开发者工具(Network → HTML 请求)确认表格是否在首屏 HTML 里;如果不是,
read_html()无解,必须换requests-html或selenium - 加请求头模拟真实浏览器:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'},否则很多网站直接返回 403 - 加
time.sleep(1)控制请求间隔,避免被封 IP —— 这点常被跳过,结果跑两分钟就被目标站限流
写入数据库前,必须做字段类型对齐和空值处理
网页表格的“空”可能是 ''、'-'、'N/A' 或真正 NaN,而数据库(比如 PostgreSQL 的 INTEGER 或 MySQL 的 DATE)对 NULL 和类型极其敏感,直接 to_sql() 很容易报 Invalid type for column xxx 或 Incorrect date value。
实操建议:
- 用
df.replace({'-': pd.NA, 'N/A': pd.NA})统一脏空值 - 对日期列显式转换:
pd.to_datetime(df['date'], errors='coerce'),失败变NaT,不会崩 - 数值列用
pd.to_numeric(df['price'], errors='coerce'),避免字符串混入导致整列变object - 写入前检查:
df.dtypes和df.isna().sum(),比出错后再 debug 快十倍
to_sql(if_exists='append') 能写但不能替代 upsert
如果网页数据有更新(比如股价、订单状态),只用 if_exists='append' 会导致重复插入。而 if_exists='replace' 又会清空历史 —— 这不是“实时写入”,是“实时覆盖”。
实操建议:
- PostgreSQL 用户优先用
ON CONFLICT DO UPDATE:配合sqlalchemy.dialects.postgresql.insert()构造 upsert 语句 - MySQL 用户用
INSERT ... ON DUPLICATE KEY UPDATE,前提是表有唯一键(如UNIQUE (symbol, trade_date)) - SQLite 用户没原生 upsert,只能先
SELECT判断是否存在,再INSERT OR REPLACE—— 注意性能,别在循环里查 - 无论哪种,主键或唯一约束必须提前建好,否则 upsert 逻辑无效
定时任务别只靠 time.sleep(),用 APScheduler 更稳
用 while True: crawl(); time.sleep(300) 看似简单,但进程挂了就停,日志难追踪,也没失败重试机制。
实操建议:
- 用
APScheduler的BackgroundScheduler+IntervalTrigger(minutes=5),支持异常捕获、任务暂停、运行状态查询 - 把数据库连接封装进函数内(而不是全局变量),避免长连接超时断开后还拿着失效的
engine - 每次抓取后写一条日志到文件或数据库,含时间、行数、异常信息(如果有),否则哪次漏了根本不知道
网页结构变更是静默的——今天好好的,明天 read_html()[0] 可能指向广告栏而不是数据表。这类问题不会报错,只会写入空数据或错列,得靠日志和定期校验兜底。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











