AI编程(四十四):网页表单提交 — 让程序替你去窗口排队

作者:

在

上一篇的爬虫再能干,也只是个”只读”的观众:网页给什么,它看什么。但生活里的查询网站都是”问答式”的——你得先在表单里填月份、输关键词、点”查询”,它才把结果吐给你。今天教程序开口说话:填表、提交、拿结果。新概念只有一个:requests.post。学完这篇,你的程序就从”看热闹的”升级成”能替你去窗口排队办事的”。

一、真实场景:每月一次的”填表查询”折磨

你在单位兼着一个活儿:每月月初,去人社局的”补贴发放查询”页面,把当月的公示结果逐条查出来、整理成表。查询页面长这样:一个下拉框选月份(2026-09、2026-10……),一个输入框填姓名或单位关键词,一个”查询”按钮。点一下,页面跳到结果页,列出匹配的记录。

问题是你手里有20个单位名要逐个查。于是每月这天你的操作是:选月份→粘贴”沈阳第一机床厂”→点查询→看结果→抄下来→回来→粘贴”沈阳水泵厂”→点查询→抄下来……20个单位查20次,抄错一个字就得重来。查完抬头一小时过去了,而这活儿每月一次,年年如此。

上周的爬虫对这个页面完全没辙:它只能”看”现成的网页,而这个页面你不点查询按钮,它就永远是空空的表单。差别在哪?上周的程序只会”问路”(打开网址),今天的程序要学会”递表单”(提交条件)。

今天全部的内容就是这一件事:让程序替你填表、点查询、把结果收回来。新概念只有一句话:

requests.post(网址, data=表单内容) = 让程序代替你,把填好的表单递进窗口。

就这一句。剩下的全是老手艺:循环(第6篇)批量查20个单位、BeautifulSoup(第41篇)从结果页抠数据、csv(第25篇)存结果、try/except(第10篇)防崩。

二、新概念只有一个:GET 是明信片,POST 是信封

先花两分钟把”提交表单”的原理看懂,写代码时就不慌了。

你早就用过 requests.get(网址)(第37篇)——那是把网址发出去,把页面拿回来。它像寄明信片:你要什么全写在明信片(网址)上,邮递员(服务器)看得见,路人也看得见。

而填表单提交是 requests.post(网址, data=表单内容)——把表单内容塞进信封里寄出去。服务器拆开信封、看完你的条件、把结果寄回来。这就是今天的新朋友:

import requests

# 假设查询页面的网址是 https://example.gov.cn/query
resp = requests.post(
    "https://example.gov.cn/query",
    data={"month": "2026-10", "keyword": "沈阳第一机床厂"}
)
print(resp.status_code)   # 200 = 服务器收到并处理了
print(resp.text[:500])    # 返回的结果页面(HTML)

三个细节,一个都不能少:

  1. data= 是个字典,一个”表单字段名:你填的值”。注意是字段名(网页里 的那个 month),不是你肉眼看到的”月份”两个字。字段名对不上,服务器当没收到——不报错、不吭声、静默返回空结果,这是今天头号大坑,疫苗在第五节。
  2. POST 和 GET 是两种”递表单”的方式。查询类表单用 GET 还是 POST,是网站定的,你不用纠结——F12 一看便知(马上教你)。今天学 POST,因为它更能代表”提交表单”这件事。
  3. resp 还是那个 resp。不管 GET 还是 POST,拿回来的东西一模一样:resp.text 是 HTML、resp.status_code 是状态码、resp.json()(第38篇)是 JSON。你会的那些解析手艺,全部照用。

类比记忆:requests.get 是在图书馆检索机上输书名——输完回车就出结果;requests.post 是到服务台填借书单——你把要什么、条件是什么写在单子上递进去,工作人员处理完把书(结果)给你。上一篇的爬虫是站在书架前抄书名,今天是学会填借书单。

怎么知道一个网页的表单”字段名”是什么、用 GET 还是 POST?F12 侦察三步,一分钟学会:

  1. 打开查询页面,按 F12,切到 Network(网络) 面板;
  2. 在页面上随便填一次查询、点”查询”按钮;
  3. Network 里会出现一条新记录,点它:General 里的 Request Method 写着 POST 还是 GET;往下翻到 Form Data(表单数据),里面就是所有字段名和你填的值——照抄进 data={} 就行。

这个侦察动作是今天最值钱的技能。以后遇到任何”填了才能看”的页面,都是这三步。

三、动手:先往”回声墙”扔个纸团

照旧先在安全的地方练手。但 POST 有个麻烦:它没法对着本地 HTML 文件练(file:// 只能”看”,没有”窗口”可递表单)。所以今天换一种练法——两步走:先用一个公开的测试网站确认你的 POST 姿势正确,再自己捏一个”查询网站”实战。

第一步:httpbin.org —— 会回声的墙

https://httpbin.org/post 是个专门给程序员练手的网站:你 POST 什么给它,它就把你发的东西原样回显在返回的 JSON 里。相当于对着山谷扔纸团,山谷把纸团原样扔回来——先确认你的纸团(表单)包对了,再去砸真窗户。

import requests

resp = requests.post(
    "https://httpbin.org/post",
    data={"month": "2026-10", "keyword": "沈阳第一机床厂"}
)

result = resp.json()
print("服务器收到的表单:", result["form"])

运行后你会看到:

服务器收到的表单: {'month': '2026-10', 'keyword': '沈阳第一机床厂'}

result["form"] 里原样回显了你递的表单——字段名、值,一字不差。看到了这两个字段,说明你的 POST 姿势是标准的。如果这里少了一个字段,就是字段名写错了,还没碰真网站就已经暴露了。

30 秒自查:data= 里的键是字段名(month),值是你填的内容(2026-10);写反了回声墙会立刻告诉你。

第二步:自己捏一个”补贴查询网站”

老规矩,用自己捏的网站练手(跑通了再换真网站,一次只改一个变量——第38篇的规矩)。这次捏的不是静态网页,而是一个会收表单、会回结果的迷你网站,20 行 Python 就能当”站长”。

建一个文件 fake_site.py(这是假网站本体,照抄即可,能看懂更好):

# fake_site.py —— 自己捏的"补贴查询网站",运行后就是一个真网站
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import parse_qs

# 假网站的"数据库":月份 -> 多条(姓名, 单位, 金额)
DATA = {
    "2026-09": [
        ("张三", "沈阳第一机床厂", 800),
        ("李四", "沈阳水泵厂", 1200),
        ("王五", "沈阳第一机床厂", 600),
    ],
    "2026-10": [
        ("赵六", "沈阳水泵厂", 900),
        ("张三", "沈阳第一机床厂", 850),
        ("孙七", "沈阳变压器厂", 700),
    ],
}

FORM_PAGE = """
补贴发放查询

补贴发放查询

月份: 2026-09 2026-10

单位关键词:

""" class Handler(BaseHTTPRequestHandler): def do_GET(self): # 打开网址:给用户看查询表单 self.send_response(200) self.send_header("Content-type", "text/html; charset=utf-8") self.end_headers() self.wfile.write(FORM_PAGE.encode()) def do_POST(self): # 收到表单:查"数据库",回一张结果页 length = int(self.headers.get("Content-Length", 0)) params = parse_qs(self.rfile.read(length).decode()) month = params.get("month", [""])[0] keyword = params.get("keyword", [""])[0] rows = [r for r in DATA.get(month, []) if keyword in r[1]] trs = "".join(f"{n}{u}{m}" for n, u, m in rows) page = f""" 查询结果

查询结果:{month} / {keyword}

{trs}
姓名单位金额
""" self.send_response(200) self.send_header("Content-type", "text/html; charset=utf-8") self.end_headers() self.wfile.write(page.encode()) def log_message(self, *args): # 别刷屏 pass print("假网站开张了:http://127.0.0.1:8000") HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()

打开一个终端运行它:python fake_site.py,然后浏览器打开 http://127.0.0.1:8000——你会看到一个真的查询页面,能选月份、输关键词、点查询、看结果。恭喜,你现在是站长了。

注意表单里的两行,它们就是 F12 侦察要找的东西: 告诉你字段名是 month, 告诉你字段名是 keyword,method="post" 告诉你用 POST。你捏的网站,字段名当然是你自己起的——但侦察姿势和真网站一模一样。

第三步:写程序去”递表单”

新开一个终端(假网站那个终端要留着跑),建 form_spider.py:

# form_spider.py —— 像人一样填表查询,但一次查完所有单位
import requests
from bs4 import BeautifulSoup

# ---- 配置区 ----
URL = "http://127.0.0.1:8000/query"   # 真网站上换成 F12 里看到的提交地址
MONTH = "2026-10"
KEYWORDS = ["沈阳第一机床厂", "沈阳水泵厂", "沈阳变压器厂"]

# ---- 主流程:循环填表、提交、收结果 ----
all_rows = []
for kw in KEYWORDS:
    print(f"正在查询:{MONTH} / {kw}")
    resp = requests.post(URL, data={"month": MONTH, "keyword": kw})
    if resp.status_code != 200:
        print(f"  查询失败,状态码:{resp.status_code}")
        continue

    soup = BeautifulSoup(resp.text, "html.parser")
    table = soup.find("table", class_="result")
    rows = table.find_all("tr")[1:] if table else []
    for tr in rows:
        cells = [td.get_text(strip=True) for td in tr.find_all("td")]
        all_rows.append([kw] + cells)
    print(f"  抓到 {len(rows)} 条")

# ---- 保存 ----
import csv
with open("查询结果.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerow(["查询关键词", "姓名", "单位", "金额"])
    writer.writerows(all_rows)

print(f"完成!共 {len(all_rows)} 条,已存入 查询结果.csv")

运行 python form_spider.py,你会看到:

正在查询:2026-10 / 沈阳第一机床厂
  抓到 1 条
正在查询:2026-10 / 沈阳水泵厂
  抓到 1 条
正在查询:2026-10 / 沈阳变压器厂
  抓到 1 条
完成!共 3 条,已存入 查询结果.csv

你手动要干 20 分钟的活儿(查 20 次就是 20 遍同样的动作),程序 3 秒干完,而且一个字都不会抄错。CSV 打开一看,每行还自动带上了”是哪个关键词查出来的”,方便核对。

逐块读懂(30 秒自查):data={"month": ..., "keyword": kw} 就是那张借书单,每个关键词填一次、递一次;soup.find("table", class_="result") 是老朋友 BeautifulSoup 从结果页抠表格(第42篇);continue 是”这次查询失败别停,接着查下一个”(第43篇的容错姿势)。

跟AI说需求的参考说法:

“这是一个查询表单页面的 HTML(贴 FORM_PAGE),我用 requests 提交。帮我写一个脚本:对每个月份×每个关键词提交查询,把结果页里的表格存成 CSV,每次查询失败不中断。”

四、改进:把”查询脚本”变成”每月照跑的工具”

三步升级,都跟AI协作完成:

  1. 关键词从文件读:把 20 个单位名存成 单位名单.txt 一行一个,程序读文件逐个查——下个月名单变了,改 txt 就行,不碰代码。跟AI说:”关键词从 txt 读,一行一个,跳过空行。”
  2. 结果自动带上时间戳:输出文件名带月份,如 查询结果_2026-10.csv,每月跑完留档,不会覆盖上个月的。跟AI说:”输出文件名拼上月份,比如 查询结果_2026-10.csv。”
  3. 跑完自动汇报:末尾打印”查询 20 个关键词、成功 19、失败 1、共 87 条记录”,失败的关键词列出来。汇报是脚本和工具的分界线——工具自己会交代干得怎么样,脚本只顾闷头跑。跟AI说:”最后打印汇总:每个关键词查到几条、有没有失败的。”

五、三个必踩的坑(先打疫苗)

坑1:字段名对不上,服务器”静默”当没收到。data={"月份": "2026-10"} 这种用肉眼看到的中文当字段名的写法,不会报任何错——服务器默默忽略它,返回一个空结果页,你对着空 CSV 猜一天。疫苗:字段名以 F12 的 Form Data 为准(或自己网页源码里 name="xxx" 的 xxx),大小写一个字母都不能差。排查第一招:先把 resp.text 打印出来看服务器到底回了啥——空结果页和报错页长得不一样。

坑2:漏掉隐藏字段,查询怎么都”不对劲”。很多真表单里藏着你看不见的字段: 之类(防止重复提交的令牌、页面标识)。你在网页上看不到它,但提交时它跟着一起递进去了——你的程序漏了它,服务器要么拒绝、要么给默认结果。疫苗:F12 的 Form Data 里有几个字段,data={} 里就写几个,一个不落照抄(包括那些看不懂的乱码值)。这正是 httpbin 回声墙的价值——发之前先对一遍。

坑3:对着真网站乱”递表单”,你不是在测试,你是在添乱。GET 请求只是”看”,POST 提交是有副作用的:报名、投票、注册、下单,递一次就真生效一次。拿真表单练手写循环?一口气提交 20 次报名,主办方的电话就打过来了。疫苗:练习一律在假网站和 httpbin 上做;对真网站只做”查询类”表单(查公示、查成绩、查余额),且单次查询之间隔 1~2 秒(time.sleep(1),第47篇的爬虫礼仪会细讲)。一句话:查询可以自动化,办事要留给人。

六、跟AI协作的正确姿势(递表单特供版)

  1. 把 Form Data 整个贴给AI:”这是我要提交的表单,F12 抓到的 Form Data 如下(贴原文),帮我写 requests.post 的代码。”字段名一个都不会错——AI 替你抄表单,比你自己抄得还准。
  2. 先写”回声版”再写”实战版”:”先用 httpbin.org/post 写个 10 行的脚本,确认表单发对了;再改成打真实查询页。”和第43篇”先勘测后施工”同一个道理。
  3. 让AI帮你读结果页:”这是我提交查询后返回的 HTML(贴 resp.text),里面结果表格的结构是什么?怎么用 BeautifulSoup 把每行的姓名、单位、金额抠出来?”——贴真实返回内容,别让AI猜页面结构。
  4. 问清楚 GET 和 POST 的判断:”这个表单用的是 POST,如果我遇到用 GET 的查询表单,代码要怎么改?”——答案是:GET 把参数拼在网址后面(requests.get(url, params={...})),params= 和 data= 用法几乎一样。一问就通,以后两种表单都不怕。

七、今日小练习

练习1:跑通”回声墙”和假网站。先用 httpbin.org/post 确认打印出你发的两个字段;再运行 fake_site.py,浏览器手动查一次”2026-09 / 沈阳第一机床厂”确认网站好使;最后跑 form_spider.py,确认 CSV 里有 3 行且金额没错。

提示:如果 httpbin 那步少了一个字段,九成是 data={} 的键写错了;如果假网站打不开,检查 fake_site.py 的终端是不是还在运行(它得一直跑着,像真的服务器一样)。

练习2:故意填错,看它”安静地死”。把 form_spider.py 里的 "month" 临时改成 "月份",运行。注意观察:程序不报错、不崩溃,只是每条查询都抓到 0 条——这就是坑1说的”静默失败”。然后照疫苗办:打印 resp.text 看服务器回了什么,再把字段名改回来。

提示:能让你看见的失败是好失败,安静的失败才可怕。养成”结果为 0 时先打印原始返回”的本能,这比多学十个语法都值钱。测完记得改回来。

练习3(动脑+选做):做成”每月照跑”的工具。按第四节的三个改进跟AI协作实现:关键词从 txt 读、文件名带月份、末尾汇总汇报。验收标准:把 单位名单.txt 里故意写一个不存在的单位”沈阳不存在厂”,运行后汇报里应该明确写着”该关键词查到 0 条”或列入失败清单,而不是假装一切正常。选做挑战:给假网站加一个”金额下限”表单字段(min_money),程序提交时只查 800 元以上的记录——加一个字段,改两处代码(假网站收参数、爬虫发参数),体会”表单加字段”对两边各意味着什么。

下篇预告

下一篇(第45篇):登录和 Cookie — 让程序带着”工牌”进门。今天你递的表单还都是”匿名查询”,但很多网站得先登录才给查——账号密码递进去,服务器发你一张”工牌”(Cookie),之后每次请求都得带着它。下一篇学 requests.Session:登录一次、带着工牌反复进出,把需要登录的查询也自动化。今天先把”填表、提交、收结果”练成本能——下一篇只是多递一张表(登录表单),外加把工牌揣好。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注