资讯动态

浏览器抓包到Python POST请求:手把手模拟翻译接口实战

发布时间:2026/10/13 23:33:48 来源:尧图企业网站定制
你有没有遇到过这种情况想拿某个网页的翻译结果做自动化处理想着“不就是发个请求吗”结果打开浏览器调试面板一看全是看不懂的请求名点开又不知道看哪一行最后只能回到手工复制粘贴的老路上去。这个场景太常见了。我最早学爬虫的时候就在“发请求”这件事上栽过不少跟头——尤其是POST请求。很多新手习惯用GET顺手一写就完事但到了真实网站尤其是带交互的页面搜索、翻译、登录、评论提交后端几乎都是通过POST接收数据的。GET是“取”POST是“交”你买东西得把订单“交”给店家光是喊一嗓子“我要买”是没用的。这篇文章就用一个最典型的案例——模拟向某在线翻译服务提交翻译请求——来把“浏览器控制台抓包 POST请求构造 请求头伪装”整条链路拆开讲清楚。适合刚学完requests基础、想进阶一点的人也适合那些已经写过不少代码、但始终觉得抓包调试不系统的朋友。1. 为什么拿“翻译服务”当练习案例1.1 拆解这个案例到底难在哪翻译服务这类页面是最适合练POST请求的“教科书级”场景。原因有三点第一交互模式天然是POST。你输入一段英文点“翻译”前端要把这段文本交给服务器处理服务器返回结果。这就是典型的“提交数据-返回数据”对应POST请求。第二数据格式够典型。翻译请求的提交参数里既有文本内容本身又有源语言、目标语言、翻译类型这类附加字段非常像你在真实业务里会碰到的表单数据。第三响应结构丰富。翻译接口返回的一大坨JSON里通常包含了翻译结果、词典解释、音标、例句、更多候选翻译等等。解析起来比“返个200就完事”的接口有意思得多能顺带练JSON解析。但难点也在这翻译服务的请求参数往往不只是你填的那个文本。它还带着一堆“额外字段”——签名、时间戳、盐值、客户端类型、版本号之类的。这些字段是怎么来的、作用是什么是新手最容易一头雾水的地方。我刚学的时候也犯过傻直接在代码里发一个只有文本框数据的POST结果服务器返我一串错误码。后来才明白服务器端早就不是“你给我一句话我给你翻一下”那么简单了——它需要验证“这个请求是不是从正规客户端发出来的”。1.2 这个案例能迁移到什么场景不要以为学完了就只会“翻译一下”。把“抓包拿到接口→分析参数→构造请求→带Cookie/头信息→解析JSON”这套流程跑通之后你就能迁移到很多场景表单提交类问卷、留言板、注册登录数据查询类查天气、查快递、查股票、查论文内容生成类AI对话、语音合成、图片描述本质上只要网页上有一个输入框、一个按钮、一次结果刷新背后基本都是一个接口调用。你只要学会“在控制台里找到那个请求、照葫芦画瓢”任何带交互的页面你都能自动化。这也是我写这篇文章想强调的核心教程里教的“某翻译服务”只是载体真正要带走的是“抓包-还原-请求-解析”的思维。2. 浏览器控制台抓包实操手把手拆给你看2.1 一套能复用到任意网页的抓包流程打开浏览器按F12切到Network网络面板这一套大家可能都知道。但真正的关键在细节勾选“Preserve log”保留日志这样页面跳转或刷新时请求不会被清空筛选条件选“Fetch/XHR”把所有图片、CSS、字体请求都过滤掉只留接口请求在“名称”列表中一眼扫过去优先看那些名字里带“translate”“search”“submit”“api”的请求拿翻译服务举例在页面上输入一个词点“翻译”按钮回到控制台能看到列表里多了一个新请求。选中它右边会分几个TabHeaders请求头、Payload请求体、Preview响应预览、Response原始响应。最常用的组合是Headers里看Request Headers重点是User-Agent、Referer、Origin、CookieHeaders里看Request Payload或者Form Data重点是表单字段名和值Preview里看响应结构确认返回的JSON长什么样这是我总结的一套“看接口三板斧”。只要是前端用AJAX/Fetch发起的请求十有八九都能用这个流程找到它。2.2 抓包时的两个关键动作第一步找到请求之后别急着一行行看。直接右键这个请求选“Copy → Copy as cURLbash”。你会得到一整段curl命令。这段命令是宝藏——它把你刚才在浏览器里发的那个请求的完整信息都“翻译”成了文本URL、请求方法、每个请求头、每个请求体字段。把它保存到一个文本文件里就等于把真实请求现场完整记录下来了。第二步把curl命令和代码结构对应着看。你能看到类似这样的结构curl https://fanyi.example.com/api/translate_v2 \ -H user-agent: Mozilla/5.0 ... \ -H referer: https://fanyi.example.com/ \ -H cookie: xxx \ --data-raw {content:hello,from:en,to:zh}对应到Python里就是import requests url https://fanyi.example.com/api/translate_v2 headers { user-agent: Mozilla/5.0 ..., referer: https://fanyi.example.com/, cookie: xxx } data { content: hello, from: en, to: zh } resp requests.post(url, headersheaders, datadata) print(resp.text)每一条curl里的-H参数对应requests里的headers字典的键值对--data-raw对应的就是请求体。这样一对照整个请求的“骨架”就出来了。2.3 从curl到Python的三种常见映射关系curl命令和Python代码的映射看起来简单但有不少细节容易出错。curl的-H对应headers字典。但要注意有些请求头是有“动态性”的比如Cookie可能过一段时间就失效了。写代码时不要完全照搬要理解哪些头是必须的哪些是“带了更容易通过”的。curl的--data-raw表示“原文发送”对应requests里的data参数。data接受字符串或字典如果你把字典传进去requests会自动编码为表单格式如果你想发JSON格式需要传一个JSON字符串并用headers里的“content-type: application/json”声明。curl的--data-urlencode表示“URL编码后发送”通常用于表单提交对应requests里的data字典默认就会做URL编码。理解了这三组关系你再看任何copy as cURL都会有一种“我能翻译它”的感觉而不是对着一段黑压压的命令发呆。3. Python代码怎么写从“照抄”到“理解”3.1 一份能用的POST请求骨架抓包完成之后写Python代码就有据可依了。先给一个标准骨架import requests import time import hashlib def translate_text(text, from_langen, to_langzh): url https://fanyi.example.com/api/translate_v2 headers { user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36, referer: https://fanyi.example.com/, origin: https://fanyi.example.com, content-type: application/json, cookie: 你的cookie } payload { content: text, from: from_lang, to: to_lang, timestamp: int(time.time() * 1000) } resp requests.post(url, headersheaders, jsonpayload, timeout10) return resp.json() if __name__ __main__: result translate_text(hello) print(result)这段代码涵盖了POST请求的几大要点URL、请求头、请求体、超时设置。其中requests.post里的json参数会自动做两件事把字典序列化成JSON字符串自动加上“content-type: application/json”请求头。所以如果在headers里没写content-type问题也不大。但我故意在headers里保留了content-type是为了让你养成习惯——当你在调试时发现“我明明发的是JSON服务器却报格式错误”排查思路就是看看声明的content-type和数据格式是否一致。3.2 data、json、params到底有什么区别这是POST请求里最容易混乱的三个参数我用通俗的话解释一下。params拼在URL问号后面的参数一般用来做查询条件比如?page1size20。data请求体里的数据默认编码成表单格式application/x-www-form-urlencoded。适合那种“在页面填了表单提交”的场景。json请求体里的数据编码成JSON格式application/json。适合接口API、前后端分离的页面。如果你混着用比如该用data的地方用了json服务器就会一脸懵明明我说的是“把鸡汤面加辣”你却在“把面条蘸辣酱”虽然不是不能吃但不是我要的那份。实际调试中的一条实用建议看到页面上是一个输入框、一个按钮、刷新一段文字结果优先用json试试看到的是页面跳转、整表提交优先用data试试。当然最可靠的办法还是看抓包里请求体的“Content-Type”是什么一锤定音。3.3 请求头伪装不能只带User-Agent“伪装”是很多爬虫教程爱提的词但伪装的学问在于“像不像一个正常用户”。只带一个User-Agent就是典型的“半吊子伪装”。因为服务器不只看你的User-Agent它还会看Referer你这个请求是从哪个页面来的。翻译服务自然是从它的首页发起的如果你不带Referer服务器会想“这个请求没有来源可能是程序直接调接口”。Origin类似Referer但更偏重安全校验部分接口会严格校验。Cookie代表“登录态”和“访问历史”。你每次访问网页都会种下Cookie服务器用Cookie来识别你是不是一个“回头客”。我在实操里发现很多反爬拦截都是先从“缺头信息”开始的。带齐Referer和Origin之后接口被拒的概率会明显下降。但我必须强调Cookie是双刃剑。Cookie会过期会绑定IP会受登录状态影响。真实项目里需要单独处理“Cookie失效后自动更新”的逻辑不是塞一个固定字符串就一劳永逸。4. 深入一步加密参数是怎么回事4.1 加密参数的常见套路真实的翻译服务请求体里往往不止有“content、from、to”这种明文字段还会有一些看起来不知道干吗用的参数比如salt、sign、time。拿经典的时间戳签名组合来说import time import hashlib timestamp str(int(time.time() * 1000)) salt timestamp 某个随机数 sign hashlib.md5((固定字符串 salt 额外盐值).encode()).hexdigest()这里的逻辑是客户端把“当前时间戳 一些固定字符串”拼接成一个字符串做一次MD5或SHA256哈希生成一个签名sign。服务器收到请求后会用自己的算法重新计算一遍签名比对一致才认为请求是合法的客户端发来的。时间是拿来干嘛的拿来限制请求“新鲜度”的。如果你的签名是30分钟前生成的服务器一比对时间戳发现太老就直接拒绝。这就防止了别人把请求录制下来反复重放攻击。这段知识很重要。很多人觉得“加密参数就是抄下来写死就完事”但时间戳变了签名就失效。这是POST请求从基础走向进阶的第一道门槛。4.2 怎么判断参数是“写死”的还是“动态生成”的看网页源码和抓包时有个判断技巧把同一个请求发两次对比请求体里的字段值。每次请求都一模一样的字段 → 大概率是写死的数据每次请求都会变的字段 → 大概率是动态生成的最常见的是时间戳和签名只在特定操作后出现的字段 → 可能是从上一个接口返回里取出来的拿某在线翻译服务实测多翻几次你会看到类似“time”字段一直在变而“token”字段可能一直不变。这就是典型的组合参数结构——一个动态校验、一个静态标识。遇到这种情况正确思路是把“每次会变的字段”当作函数参数在代码里动态计算而不是硬编码。这需要你锁定它的生成逻辑最常用的手段是看页面的JS源码找到生成签名的那段代码然后用Python重新实现一遍。4.3 一个完整的“带签名”POST请求示例import requests import time import hashlib class Translator: def __init__(self, token你的固定token): self.token token self.session requests.Session() self.session.headers.update({ user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, referer: https://fanyi.example.com/ }) def _sign(self, text, timestamp, salt): raw 某个固定盐值 text timestamp salt self.token return hashlib.md5(raw.encode()).hexdigest() def translate(self, text, from_langen, to_langzh): timestamp str(int(time.time() * 1000)) salt str(int(time.time() * 1000) 100) payload { content: text, from: from_lang, to: to_lang, time: timestamp, salt: salt, sign: self._sign(text, timestamp, salt) } resp self.session.post(https://fanyi.example.com/api/translate_v2, jsonpayload) return resp.json()这段代码是我在很多项目里的通用模板。用类封装是刻意的翻译请求涉及“签名算法 请求头 会话保持”三块逻辑拆成类以后代码更清晰也方便在别的地方复用。尤其是session的使用——一个Session对象会把Cookie和请求头自动带到同一次的后续请求里比每次requests.post独立发请求要稳得多。5. 常见问题与排查技巧实录5.1 一个新手最容易踩的坑返回HTML而不是JSON症状代码执行了但打印出来的不是清爽的JSON而是一大段HTML。排查思路首先看返回状态的Content-Type。如果是“text/html”说明你请求到的不是API接口而是普通网页。这种情况常见于URL拼错了写到了页面地址而非接口地址请求头缺失被反爬系统拦了服务器返回了一个“验证页面”需要先访问首页拿到Cookie再访问接口解决办法也很直接回到控制台确认你复制请求时的那个URL是否和代码里的完全一致带上完整的headers再试用session先GET一下首页把Cookie存下来再POST。5.2 请求成功了但数据不对一个必须学会的调试技巧有时候状态码是200但返回的数据里没有翻译结果只有一个错误描述。这时候别急着改代码先在浏览器里手动操作一次把控制台里“那个请求”的响应和Python里拿到的响应做对比。如果浏览器里响应正常、Python响应异常说明请求构造出了问题。这时候最有效的招是把Python的请求体和浏览器抓包里的Request Payload逐字段比对一个字母一个字母地对包括引号、括号、结尾的逗号。我遇到过很多次以为完全一样后来发现少了一个字段或者字段名大小写差了一个字母。5.3 被反爬机制识别的几道防线反爬不是一道墙是好几道。常见的拦截点按顺序排拦截维度典型特征应对方式请求头缺少User-Agent/Referer/Cookie带齐关键头用Session维持状态频率单IP短时间大量请求加延时随机间隔控制QPS签名请求体含动态sign字段分析并动态生成签名行为请求轨迹不像真人请求顺序、鼠标事件等模拟很多人只关注第一道以为带上User-Agent就安全了。实际上真实开发里签名才是大头。特别是翻译服务这类接口签名几乎必带。5.4 日常调试时值得养成的几个习惯先curl后代码。每次都从控制台复制curl保存起来再翻译成代码能减少“凭记忆写”的错误。写代码时把返回结果格式化输出。用json.dumps(resp.json(), ensure_asciiFalse, indent2)代替直接print一眼就能看到数据层级结构。加延时。翻译服务这类接口对频率异常敏感。写死每次请求间隔1-3秒随机延时才算是一个“有礼貌”的客户端。6. 实用小技巧几个我一直用的“加速包”抓包这件事多练几次手就熟了。但有几个习惯是我长期实践后才养成的写在这里给你做个参考。一是利用控制台的“Search”功能。如果你不知道一个请求叫什么名字但知道返回结果的某个关键词比如翻译结果“你好”可以在Network面板按CtrlF输入“你好”浏览器会直接帮你定位到包含这个关键词的请求。这个功能在接口较多的时候极其好用。二是学会看Initiator发起者列。每次请求都会显示它是由哪段JS触发的。顺着Initiator往上看能找到生成请求参数的JS文件对分析加密参数很有帮助。三是别忽略Preview和Response的差异。Preview是浏览器帮你格式化好的数据树Response是原始字符串。有时候Preview里看不到某些隐藏字段但Response里能看到。解析数据时以Response里的原始JSON为准。四是用Python写一个通用的“抓包转代码”的小工具思路。把每次复制的curl命令保存成一个文本写一段代码解析这个文本里的-H和--data-raw自动生成字典结构。这个工具会随着你抓包越来越多而越用越顺手。我在实际项目中很多POST请求都是这样一步一步“抄”出来的。抄得多了你会发现很多网站的参数规则大同小异总结出几套通用的签名算法模板新项目上手速度会快很多。最后再分享一个小技巧写爬虫代码的时候别把逻辑全部堆在一个文件里。把“请求封装”“签名生成”“数据解析”“主流程”分开写后期维护会舒服得多。我早期吃过这个亏——几千行代码揉成一团改一个参数要翻半天后来拆开之后就清爽了。这个翻译案例虽小但如果你认真按模块去写它会成为你爬虫代码设计能力的一块很好的磨刀石。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑