ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python爬虫POST请求实战:从基础到高级反爬策略

2026/8/8 5:33:06 拓冰建站 浏览量
Python爬虫POST请求实战:从基础到高级反爬策略 1. 从GET到POST为什么爬虫必须掌握POST请求刚接触Python爬虫的朋友最开始学的几乎都是requests.get()。这很自然毕竟我们打开浏览器输入网址回车本质上就是一个GET请求。它简单、直观用来抓取静态网页信息比如新闻列表、商品详情页确实够用。但如果你以为爬虫就是反复调用get()那可能连很多网站的门都摸不着。我遇到过不少新手兴冲冲地写了个爬虫去抓某个网站的数据结果返回的不是想要的数据而是一句冷冰冰的“暂无数据”或者直接403。排查半天发现浏览器里明明能正常显示数据。问题出在哪十有八九这个网站的数据是通过POST请求动态加载的。GET请求的参数是挂在URL后面的像?keywordpythonpage2一目了然。而POST请求是把数据藏在请求的“身体”body里发送给服务器的你在地址栏里根本看不见。现在绝大多数搜索、登录、表单提交、翻页加载尤其是Ajax动态加载的接口用的都是POST。所以不会POST请求你的爬虫能力就被限制在了互联网的浅水区。那些真正有价值、动态交互的数据比如电商的搜索列表、社交媒体的评论、需要登录后才能查看的个人信息都藏在POST请求的背后。requests库的post()方法就是你潜入深水区的潜水装备。这篇文章我就以一个老爬虫工程师的角度带你彻底搞懂requests.post()不止是会用更要明白背后的门道避开那些我踩过的坑。2. POST请求的核心请求体Body与请求头Headers的奥秘GET和POST在requests库里的调用看起来很像无非是把get()换成post()。但魔鬼藏在细节里这两个方法的灵魂完全不同。GET的核心是URLPOST的核心是请求体Body和与之配套的请求头Headers。理解这对组合是写好POST爬虫的关键。2.1 请求体Body的三种主要格式服务器通过请求头里的Content-Type字段来判断你发送的body是什么格式然后才能正确解析。我们常用的主要有三种1. 表单格式 (application/x-www-form-urlencoded)这是最常见的一种模拟网页表单提交。数据格式就像GET的查询字符串但放在body里。比如usernameadminpassword123456。import requests url https://example.com/login # 数据以字典形式传给 data 参数 data { username: your_username, password: your_password } response requests.post(url, datadata) # requests会自动将字典转换为urlencoded格式并设置Content-Type为 application/x-www-form-urlencoded注意这是最基本的形式但很多现代网站登录会有额外的token或加密直接这样发大概率会失败。我们后面会讲如何处理。2. JSON格式 (application/json)这是目前API接口最流行的数据交换格式。数据是一个JSON字符串。import requests import json url https://api.example.com/search # 数据以字典形式传给 json 参数是最方便的做法 json_data { query: Python爬虫, page: 1, size: 20 } response requests.post(url, jsonjson_data) # 使用json参数requests会自动将字典序列化为JSON字符串并设置Content-Type为 application/json这是我最推荐的方式清晰且不易出错。你也可以手动序列化后传给data参数但必须自己设置请求头headers {Content-Type: application/json} response requests.post(url, datajson.dumps(json_data), headersheaders)3. 多部分表单数据 (multipart/form-data)主要用于上传文件。它会将body分成多个部分每个部分包含一个字段。url https://example.com/upload files {file: open(report.pdf, rb)} # 以二进制读模式打开文件 data {comment: 这是上传的文件} response requests.post(url, filesfiles, datadata) # 使用files参数requests会自动构建multipart/form-data格式的body和对应的Content-Type2.2 请求头Headers的伪装艺术请求头是爬虫的“身份证”和“通行证”。服务器用它来识别客户端的身份、能力和意图。一个赤裸裸的、只带着Python-requests默认头信息的请求就像没穿衣服走在街上异常显眼极易被拦截。必须处理的几个关键请求头User-Agent: 这是最重要的标识。默认的python-requests/2.28.2就是在告诉网站“我是爬虫快来封我。”必须把它换成常见浏览器的值。headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } response requests.post(url, jsonjson_data, headersheaders)Content-Type: 如上所述它告诉服务器body的格式。当你使用data、json或files参数时requests通常会帮你设置好。但如果你手动构建了body字符串就必须自己设置。Referer: 表示这个请求是从哪个页面发起的。对于一些有页面流程限制的接口比如必须先访问A页面才能提交B页面的表单设置正确的Referer能绕过检查。Cookie: 维持会话状态。登录后的身份凭证通常保存在Cookie里。你可以使用requests.Session()对象来自动管理Cookie这比手动提取、设置要方便可靠得多。其他认证头: 如Authorization: Bearer token用于JWT认证X-CSRFToken用于防御跨站请求伪造等。这些需要从网站的前端代码或之前的响应中提取。一个实战中的技巧不要只复制一个固定的User-Agent。可以准备一个列表每次请求随机选取这样能更好地模拟真实用户的行为。同样Referer也可以根据爬取逻辑动态设置。3. 实战演练逆向分析一个动态网页的POST接口理论说再多不如动手干一票。我们以一个虚构的、但非常典型的商品搜索页面为例。假设我们要爬取“某电商网站”搜索“Python书籍”的结果列表。第一步浏览器中观察打开浏览器开发者工具F12切换到Network网络标签。在网站搜索框输入“Python书籍”点击搜索。在网络面板中你会看到一大堆请求。重点关注类型为XHR或Fetch的请求这些通常是Ajax请求。在筛选框输入search、list、query等关键词能帮你快速定位。找到目标请求后点击它查看Headers和Payload在Chrome中或请求在Firefox中。第二步解析请求详情在Headers里你需要记录Request URL: POST请求的目标地址。注意这个地址可能和你在地址栏看到的网页地址完全不同。Request Method: 确认是POST。Request Headers: 复制下Content-Type,User-Agent,Cookie如果有等重要信息。在Payload里Chrome或请求体Firefox你需要查看发送的数据。如果是Form Data你会看到键值对如果是Request Payload通常是JSON格式你需要复制这个JSON结构。第三步用Python复现请求假设我们分析出的信息如下URL:https://api.fake-mall.com/search/productContent-Type:application/jsonPayload:{keyword: Python书籍, pageIndex: 1, pageSize: 20, sortType: default}那么我们的爬虫代码雏形就是import requests import json url https://api.fake-mall.com/search/product headers { User-Agent: Mozilla/5.0..., Content-Type: application/json, # 如果接口需要可能还有 Referer: https://www.fake-mall.com/ } payload { keyword: Python书籍, pageIndex: 1, # 翻页关键参数 pageSize: 20, sortType: default } try: response requests.post(url, jsonpayload, headersheaders) response.raise_for_status() # 检查请求是否成功状态码非200则抛出异常 data response.json() # 将响应的JSON字符串解析为Python字典 # 处理data提取商品列表 for product in data.get(productList, []): print(f商品名: {product.get(name)}, 价格: {product.get(price)}) except requests.exceptions.RequestException as e: print(f请求出错: {e}) except json.JSONDecodeError as e: print(fJSON解析出错: {e}, 响应文本: {response.text[:200]}) # 打印前200字符方便调试第四步处理翻页翻页通常就是修改payload里的pageIndex或page参数用一个循环即可。for page in range(1, 6): # 假设爬取前5页 payload[pageIndex] page response requests.post(url, jsonpayload, headersheaders) # ... 处理每一页的数据注意务必在循环内加入延时如time.sleep(1)避免请求过快被服务器封禁。这是一个基本的道德和自我保护措施。4. 高级技巧与常见反爬策略应对如果所有网站都像上面那么简单爬虫工程师就要失业了。现实是网站会设置各种障碍。下面是我总结的几个典型难题和破解思路。4.1 参数加密与签名很多APP或网页的接口其POST数据不是明文的keywordxxx而是一串毫无规律的加密字符串或者带有一个根据算法生成的sign签名参数。这是为了确保请求的完整性和来源合法性。应对策略逆向分析前端JavaScript代码。你需要找到生成这些加密参数或签名的JS函数。使用浏览器开发者工具的Sources面板搜索关键词如encrypt、sign、md5、hmac等。然后用Python的execjs库来执行这些JS代码或者更彻底地用Python重写其加密逻辑。这是一个技术难点需要耐心和一定的JS功底。4.2 动态Token如CSRF Token很多网站的表单里会隐藏一个名为csrf_token、authenticity_token的字段每次刷新页面都会变。提交POST请求时必须带上这个token否则服务器会拒绝。应对策略使用requests.Session()Session对象会像浏览器一样自动保存一次会话中的Cookie。通常流程是先用Session发一个GET请求到目标表单页面。从返回的HTML页面中用lxml或BeautifulSoup解析出隐藏的token值。将这个token值加入到后续POST请求的data中。用同一个Session对象发送POST请求它会自动携带上一步GET请求获得的Cookie。import requests from bs4 import BeautifulSoup session requests.Session() session.headers.update({User-Agent: ...}) # 1. 获取表单页面和token login_page_url https://example.com/login resp_get session.get(login_page_url) soup BeautifulSoup(resp_get.text, html.parser) csrf_token soup.find(input, {name: csrf_token}).get(value) # 2. 构造登录数据并POST login_data { username: ..., password: ..., csrf_token: csrf_token } resp_post session.post(login_page_url, datalogin_data) # 登录成功后session会自动保存登录后的cookie后续请求都带着登录状态4.3 请求频率限制与IP封禁这是最直接的反爬手段。你的IP如果短时间内发出太多请求会被暂时或永久封禁。应对策略增加延迟在每个请求间使用time.sleep(random.uniform(1, 3))加入随机性更逼真。使用代理IP池这是应对IP封禁的终极方案。你需要有一批代理IP然后在请求时通过proxies参数轮换使用。proxies { http: http://10.10.1.10:3128, https: http://10.10.1.10:1080, } response requests.post(url, jsondata, headersheaders, proxiesproxies)设置超时和重试使用requests的适配器或第三方库如retrying来配置重试逻辑应对网络波动或临时封禁。4.4 检查JavaScript渲染有些网站的数据在初始的HTML源码里根本不存在是通过执行JavaScript代码后再通过Ajax即POST/GET请求动态填充的。你用requests直接拿到的HTML是空的容器。应对策略首选像之前一样在Network面板里找到那个动态加载数据的Ajax请求通常是POST直接模拟这个请求。这是最高效的方式。备选如果Ajax请求参数过于复杂如加密可以考虑使用Selenium或Playwright这类浏览器自动化工具。它们能真正驱动浏览器渲染页面等数据加载完成后再获取HTML。但代价是速度慢、资源消耗大。5. 调试与错误处理从报错信息中寻找线索你的POST请求很可能第一次不会成功。别慌系统的报错信息是最好的老师。5.1 检查响应状态码200 OK成功。可以继续解析response.json()或response.text。400 Bad Request客户端错误。通常是你的请求参数格式不对、缺少必要字段或字段值非法。仔细核对你的payload字典和浏览器里捕获的原始payload逐字段对比。注意数字和字符串类型有时API要求page: 1字符串而不是page: 1整数。401 Unauthorized / 403 Forbidden未授权/禁止访问。说明你需要登录401或者没有权限403。检查你的请求头里是否包含了正确的Cookie或Authorization信息。先用Session完成登录流程。404 Not Found接口地址错了。确认URL是否正确。429 Too Many Requests请求频率太高。必须立刻降低请求速度增加延迟或使用代理IP。500 Internal Server Error服务器内部错误。可能是你发送的数据触发了服务器端的bug也可能是服务器暂时故障。可以稍后重试。5.2 解析响应内容即使状态码是200也不一定代表拿到了数据。很多API会在JSON返回体里用一个code或status字段来表示业务逻辑的成功与否。resp_json response.json() if resp_json.get(code) 0: # 假设0代表成功 data resp_json[data] else: print(f业务逻辑错误: {resp_json.get(msg)})5.3 使用打印和日志进行调试在开发阶段把关键信息打印出来是非常有用的。import logging logging.basicConfig(levellogging.DEBUG) # 这会打印出requests发出的所有HTTP信息非常详细但可能包含敏感信息调试完记得关闭。 # 或者手动打印 print(请求URL:, response.request.url) print(请求头:, dict(response.request.headers)) print(请求体:, response.request.body) # 查看实际发出的数据 print(响应状态码:, response.status_code) print(响应头:, dict(response.headers)) print(响应文本前500字符:, response.text[:500])通过对比浏览器发送的请求和你代码发送的请求看上面打印的request.body和request.headers几乎能定位99%的问题。6. 工程化实践构建一个健壮的POST请求爬虫模块当你要爬取大量数据时就不能再把所有代码写在一个文件里了。需要一些工程化的思路来提高代码的可维护性和健壮性。6.1 使用Session管理会话如前所述requests.Session()是管理Cookie、保持连接池、复用TCP连接的最佳选择。你应该为每个需要保持状态的爬虫任务创建一个Session实例。6.2 配置统一的请求头与超时将固定的请求头配置在Session或一个全局变量中避免每次请求都写一遍。DEFAULT_HEADERS { User-Agent: ..., Accept: application/json, text/javascript, */*; q0.01, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, } session requests.Session() session.headers.update(DEFAULT_HEADERS) session.timeout (5, 15) # (连接超时 读取超时) 单位秒6.3 实现简单的代理IP池和重试机制一个最简单的代理IP池可以是一个列表每次请求前随机选取一个。结合重试可以提高成功率。import random from retrying import retry # 需要安装pip install retrying proxy_list [ http://ip1:port, http://ip2:port, # ... ] retry(stop_max_attempt_number3, wait_fixed2000) # 最多重试3次每次间隔2秒 def make_post_request(url, data): proxy {https: random.choice(proxy_list)} try: resp session.post(url, jsondata, proxiesproxy, timeout10) resp.raise_for_status() return resp except requests.exceptions.ProxyError: # 代理失败从列表中移除该代理 proxy_list.remove(proxy[https]) raise # 重新抛出异常触发重试 except requests.exceptions.Timeout: print(请求超时) raise6.4 数据解析与存储分离将网络请求、HTML/JSON解析、数据存储的逻辑分开。这样如果网站改版你只需要修改解析部分如果换数据库只需要修改存储部分。class ProductSpider: def __init__(self): self.session requests.Session() self.session.headers DEFAULT_HEADERS def fetch_page(self, page): 负责发送请求 url ... payload {page: page} resp self.session.post(url, jsonpayload) return resp.json() def parse_products(self, json_data): 负责解析数据 product_list [] for item in json_data.get(list, []): product { name: item.get(title), price: float(item.get(price, 0)), sku: item.get(skuId) } product_list.append(product) return product_list def save_to_csv(self, product_list, filename): 负责存储数据 import csv with open(filename, a, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[name, price, sku]) writer.writerows(product_list) def run(self, start_page, end_page): 主流程 for page in range(start_page, end_page1): print(f正在爬取第{page}页...) try: data self.fetch_page(page) products self.parse_products(data) self.save_to_csv(products, products.csv) time.sleep(random.uniform(1, 2)) # 礼貌性延迟 except Exception as e: print(f第{page}页爬取出错: {e}) continue6.5 处理异常与日志记录使用Python的logging模块替代print可以方便地控制日志级别将日志输出到文件。import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(spider.log), logging.StreamHandler() # 同时输出到控制台 ] ) logger logging.getLogger(__name__) # 在代码中使用 logger.info(f开始爬取第{page}页) logger.warning(f代理IP {proxy} 失效已移除) logger.error(f请求失败: {e}, exc_infoTrue) # exc_infoTrue会打印异常堆栈走到这一步你已经不再是简单地调用requests.post()了而是在构建一个有一定抗风险能力的数据采集系统。记住爬虫的核心是模拟人的行为但要比人更守规矩控制频率、处理错误。每一次失败的请求其状态码和响应体都是宝贵的调试信息。多观察、多分析、多思考网站背后的逻辑你的POST爬虫之路就会越走越顺。