发布时间:2026/7/18 8:08:01
C++爬虫实战:libcurl与RapidJSON构建谷歌翻译API客户端 1. 项目概述为什么用C写一个谷歌翻译爬虫最近在整理硬盘里的老项目翻出来一个几年前写的C谷歌翻译爬虫源码。现在回头看这个项目虽然技术栈不算新潮但里面涉及到的网络编程、数据解析、反爬对抗策略以及用C这种“重型武器”来做爬虫的独特考量依然有很多值得深挖的细节。今天就来一次彻底的源码解析不只是贴代码更想聊聊当年做这个项目时的设计思路、踩过的坑以及C在这个场景下的优劣。你可能会问现在Python的requests加BeautifulSoup或者各种异步框架写爬虫不是又快又方便吗为什么非得用C这其实回到了一个本质问题工具的选择永远服务于场景和需求。这个爬虫项目诞生于一个需要高并发、低延迟、稳定长期运行并且对资源消耗极其敏感的后台服务中。Python的易用性毋庸置疑但在当时的环境下我们需要精细控制每一个TCP连接、管理内存以避免GC带来的不确定性停顿并且要能轻松编译成单个可执行文件部署在各种边缘服务器上。C虽然上手门槛高但能提供极致的性能和可控性。当然这不是说C是爬虫的万能解药而是针对特定“硬核”需求的一种工程权衡。这个爬虫的核心目标很明确模拟浏览器行为向谷歌翻译的接口发送翻译请求并稳定、高效地抓取返回的翻译结果。它不是一个简单的HTTP GET而需要处理表单数据、会话Session、可能的动态令牌以及应对谷歌随时可能变更的反爬机制。接下来我们就一层层剥开它的实现。2. 核心架构与设计思路拆解2.1 技术栈选型为什么是libcurl和RapidJSON打开源码首先引入眼帘的通常是头文件包含。这个项目主要依赖两个核心库libcurl用于处理所有网络通信RapidJSON用于解析JSON格式的返回数据。为什么选择libcurl在C的世界里进行HTTP通信有几个选择自己用socket从头撸工程量大容易出bug、使用Boost.Beast强大但复杂依赖Boost、或者使用libcurl。libcurl是一个久经沙场、跨平台、支持多种协议HTTP/HTTPS/FTP等的C语言库。它的优势非常突出成熟稳定几乎你能想到的所有HTTP特性它都支持包括HTTPS需要搭配OpenSSL或mbedTLS、Cookie、代理、连接复用等。易于集成C接口与C兼容性好通常系统自带或容易安装。回调机制灵活通过设置写回调write callback、读回调read callback等可以非常精细地控制数据的收发过程这对于处理流式数据或大响应体非常有用。在源码中你会看到一个CurlHandle类它是对libcurl C接口的简单RAIIResource Acquisition Is Initialization封装确保CURL*句柄在使用结束后能被正确清理这是C管理C资源的最佳实践。为什么选择RapidJSON谷歌翻译的API返回的数据是JSON格式。我们需要一个高效、轻量级的JSON解析器。RapidJSON是腾讯开源的一个C JSON解析/生成库以其高性能号称最快的JSON解析器之一和仅头文件header-only的特性而闻名。仅头文件意味着你只需要包含它的头文件无需编译和链接额外的库极大简化了项目构建过程。它的API虽然略显繁琐基于DOM树的手动遍历但在性能至关重要的爬虫场景下这点代价是值得的。源码中会有一个JsonParser类专门封装对RapidJSON的使用提供如getString、getArray等更易用的辅助方法。2.2 核心类设计职责分离与高内聚良好的类设计是项目可维护性的基石。这个爬虫的类结构大致如下TranslatorCrawler(核心控制器类)这是爬虫的“大脑”。它持有配置参数如目标语言对、重试次数、超时时间、网络句柄(CurlHandle)、解析器(JsonParser)的实例。它暴露了主要的接口translate(const std::string text)外部调用这个接口即可获得翻译结果。它负责协调整个翻译流程准备请求、发送请求、处理响应、解析结果、错误处理。RequestBuilder(请求构建器)它的职责是构造发送给谷歌翻译API的HTTP请求。这远不止是拼接一个URL那么简单。需要构造正确的POST数据体通常是application/x-www-form-urlencoded格式其中可能包含待翻译文本、源语言、目标语言代码以及一些看似随机但必需的参数如client,dt,tk等。这些参数往往需要通过分析网页JavaScript或之前的响应动态计算。RequestBuilder类封装了这些复杂的参数生成逻辑使得TranslatorCrawler的核心流程保持清晰。ResponseParser(响应解析器)负责处理谷歌翻译返回的、通常是嵌套结构复杂的JSON数据。它需要从JSON中精准地提取出翻译文本、可能的音标、备选翻译等信息。由于API格式可能变化将解析逻辑集中在这个类里一旦需要适配新格式修改点就非常集中。RateLimiter(速率限制器)这是爬虫的“良心”也是稳定运行的保障。直接用一个while循环狂发请求很快就会被封IP。RateLimiter实现了简单的令牌桶算法控制请求发出的频率例如“每秒不超过2次请求”。它会记录最后一次请求的时间并在下次请求前根据需要休眠一段时间。注意直接、无节制地爬取任何网站包括谷歌翻译都是不道德且可能违反其服务条款的。RateLimiter不仅是技术需要更是网络公民责任的体现。在实际项目中我们还会设置更保守的间隔并严格遵守robots.txt如果有的话。3. 关键实现细节与源码剖析3.1 网络请求的封装与错误处理让我们深入CurlHandle类的实现。一个健壮的网络封装必须处理好以下几点初始化与全局设置class CurlHandle { public: CurlHandle() { curl_ curl_easy_init(); if (!curl_) { throw std::runtime_error(Failed to initialize CURL); } // 一些全局性的稳妥设置 curl_easy_setopt(curl_, CURLOPT_USERAGENT, Mozilla/5.0 (compatible; MyTranslatorBot/1.0)); curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1L); // 跟随重定向 curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 1L); // 验证SSL证书生产环境应为1L curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 2L); // 设置写回调函数和自定义数据指针 curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, CurlHandle::writeCallback); curl_easy_setopt(curl_, CURLOPT_WRITEDATA, this); } private: static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { CurlHandle* self static_castCurlHandle*(userdata); size_t totalSize size * nmemb; self-responseBuffer_.append(ptr, totalSize); return totalSize; // 必须返回实际处理的数据大小 } CURL* curl_; std::string responseBuffer_; };这里有几个关键点User-Agent设置为一个看起来像浏览器的字符串这是绕过基础反爬的第一步。纯CURL的默认UA很容易被识别。SSL验证在开发调试时有时会设为0L来跳过证书验证但在生产环境中绝对不要这样做这会引入中间人攻击风险。这里设置为验证。写回调这是libcurl的经典模式。当收到数据时libcurl会调用我们注册的回调函数。我们将数据追加到成员变量responseBuffer_中。回调函数必须是静态的所以我们通过CURLOPT_WRITEDATA将this指针传进去。执行请求与错误处理bool performPost(const std::string url, const std::string postData, long httpCode) { responseBuffer_.clear(); // 清空上次的缓冲区 curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, postData.c_str()); // 可以设置超时 curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 10L); CURLcode res curl_easy_perform(curl_); if (res ! CURLE_OK) { lastError_ curl_easy_strerror(res); return false; } curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, httpCode); return true; }错误处理是爬虫稳定性的生命线。curl_easy_perform的返回值CURLcode需要仔细检查。常见的错误有CURLE_COULDNT_CONNECT连接失败、CURLE_OPERATION_TIMEDOUT超时、CURLE_SSL_CONNECT_ERRORSSL问题。我们需要记录错误信息(lastError_)并在上层根据错误类型决定是重试、降级还是直接失败。3.2 参数构造与“Token”生成之谜这是爬虫与谷歌翻译API“斗智斗勇”的核心。早期的谷歌翻译网页版其请求参数中包含一个叫tk可能代表token的字段这个值是由JavaScript函数根据待翻译文本动态计算出来的目的是防止简单的API调用。在源码的RequestBuilder::buildRequestParams方法中你会看到类似这样的逻辑std::string buildRequestParams(const std::string text, const std::string sl, const std::string tl) { std::string tk calculateToken(text); // 关键且复杂的步骤 std::stringstream postData; postData sl sl // 源语言如 zh-CN tl tl // 目标语言如 en q curl_easy_escape(nullptr, text.c_str(), text.length()) // 对文本进行URL编码 tk tk clientwebapp; // 模拟网页客户端 // ... 可能还有其他参数如 dt返回数据类型、bd字典等 return postData.str(); }calculateToken函数的实现通常是逆向工程谷歌翻译网页端JavaScript的结果。它可能涉及对文本进行某种哈希运算如MD5、SHA1再与一个基于时间或其他种子生成的盐值进行组合计算。这部分代码是爬虫最脆弱的部分因为谷歌一旦更新其前端加密逻辑这里的算法就需要同步更新。在项目中我们通常会将这部分算法单独放在一个模块中并准备好快速更新和测试的机制。实操心得逆向这类动态token生成算法通常需要借助浏览器开发者工具在Network面板中找到翻译请求仔细查看其Payload并尝试在Sources面板中搜索关键参数名如tk定位到生成它的JS函数。然后可以用C重新实现该JS逻辑或者使用如v8引擎嵌入执行JS代码更重但更可靠。这是一个持续对抗的过程。3.3 JSON响应解析与数据提取假设我们成功收到了一个JSON响应它可能长这样简化版[[[Hello World,你好世界,,]],null,zh-CN,...]或者更结构化的版本{ sentences: [{trans: 你好世界, orig: Hello World, ...}], src: en, confidence: 1.0, // ... }ResponseParser::parse方法就需要处理这种不确定性。使用RapidJSON的典型解析流程如下std::string ResponseParser::parse(const std::string jsonResponse) { rapidjson::Document doc; doc.Parse(jsonResponse.c_str()); if (doc.HasParseError()) { throw ParseError(Invalid JSON); } // 方案1尝试从新格式解析 if (doc.IsObject() doc.HasMember(sentences) doc[sentences].IsArray()) { const auto sentences doc[sentences]; if (sentences.Size() 0 sentences[0].IsObject() sentences[0].HasMember(trans)) { return sentences[0][trans].GetString(); } } // 方案2尝试从旧的嵌套数组格式解析 else if (doc.IsArray() doc.Size() 0 doc[0].IsArray()) { const auto firstArray doc[0]; if (firstArray.Size() 0 firstArray[0].IsArray()) { const auto transArray firstArray[0]; if (transArray.Size() 0 transArray[0].IsString()) { return transArray[0].GetString(); } } } throw ParseError(Unsupported JSON format); }这里展示了防御性编程和格式兼容的重要性。爬虫面对的是不受控的外部接口其返回格式可能因地区、时间、客户端类型而变化。我们的解析器需要足够健壮能尝试多种可能的格式并在所有尝试都失败时给出明确的错误。4. 高级话题性能优化与资源管理4.1 连接复用与会话保持频繁地创建和销毁TCP连接是性能杀手。libcurl提供了连接复用的机制通过保持一个“连接池”来实现。在CurlHandle类中我们可以考虑使用curl_easy_setopt(curl_, CURLOPT_TCP_KEEPALIVE, 1L)来启用TCP keepalive更重要的是在TranslatorCrawler层面维护一个CurlHandle对象池而不是每次翻译都新建一个。对于需要Cookie的会话可以使用CURLOPT_COOKIEFILE和CURLOPT_COOKIEJAR选项来让libcurl自动管理Cookie。4.2 异步与并发模型一个单线程同步的爬虫吞吐量有限。为了提升效率我们可以引入并发。C中实现并发有几种方式多线程 同步libcurl每个线程拥有自己独立的CurlHandle这是最简单的方式。但需要注意线程安全以及操作系统线程创建和切换的开销。多线程 异步libcurl (multi interface)libcurl提供了curl_multi接口允许在单个线程中同时管理多个并发的、非阻塞的HTTP请求。这是更高效的方式。你需要使用curl_multi_add_handle添加多个easy handle然后通过curl_multi_perform进行轮询使用curl_multi_fdset配合select或poll等系统调用来等待I/O事件。基于事件循环的异步库如libuv或Boost.Asio将libcurl的回调集成到事件循环中。这能构建出非常高效、可扩展的网络应用但架构复杂度最高。在最初的版本中我选择了第一种多线程同步因为实现简单且对于每秒几个请求的速率限制场景已经足够。每个工作线程从任务队列中取出待翻译文本使用自己的CurlHandle执行请求然后将结果放回结果队列。主线程负责分发任务和收集结果。4.3 内存与异常安全C要求我们手动管理资源。在爬虫中主要资源是网络句柄(CURL*)、动态分配的字符串或缓冲区。我们必须确保在任何情况下包括发生异常时这些资源都能被正确释放。RAII是黄金法则CurlHandle类就是典范。它在构造函数中获取资源curl_easy_init在析构函数中释放资源curl_easy_cleanup。这样只要对象离开作用域资源就会被自动清理。智能指针管理动态数据对于解析JSON后可能产生的复杂数据结构可以使用std::unique_ptr或std::shared_ptr来管理生命周期避免内存泄漏。异常安全保证关键操作如网络请求和JSON解析都可能抛出异常。我们需要确保当异常发生时对象仍处于有效状态基本保证或者操作完全成功或完全回滚强保证。例如在performPost中如果curl_easy_perform失败我们清理了响应缓冲区并返回false而没有破坏curl_句柄本身这提供了基本保证。5. 反爬虫策略与伦理实践写爬虫尤其是针对大型商业公司服务的爬虫必须时刻保持敬畏之心。谷歌等公司拥有强大的反爬虫系统我们的代码需要在不影响对方服务的前提下运行。尊重robots.txt虽然谷歌翻译的API接口可能没有明确的robots.txt禁止但我们应该有节制地访问。RateLimiter就是为此而生。将请求频率控制在人类操作的范围内如每秒1-2次。模拟人类行为除了设置合理的User-Agent还可以在请求之间加入随机延迟模拟人类思考和阅读时间。避免在固定时间点规律性地发送请求。处理验证码如果触发了反爬机制可能会收到验证码挑战。一个成熟的爬虫系统需要有应对策略可能是降级切换到备用翻译源如Bing翻译。告警通知管理员人工介入。自动识别集成图像识别库如Tesseract尝试破解简单验证码但这通常违反服务条款且技术对抗成本高不推荐。使用代理IP池对于大规模爬取单一IP很容易被封锁。需要维护一个代理IP池并在请求失败时自动切换IP。这涉及到代理IP的获取、验证、权重管理等一系列复杂问题。明确法律与道德边界爬取的数据仅用于个人学习、研究或授权的业务集成。绝对不要用于大规模商业复制、攻击服务、侵犯版权或隐私。要仔细阅读目标网站的服务条款。6. 项目构建、测试与部署6.1 跨平台构建与依赖管理这个项目使用CMake作为构建系统这是C跨平台开发的事实标准。CMakeLists.txt文件会定义如何查找libcurl和RapidJSON。cmake_minimum_required(VERSION 3.10) project(GoogleTranslateCrawler) set(CMAKE_CXX_STANDARD 11) # 查找CURL库 find_package(CURL REQUIRED) # RapidJSON是header-only只需包含路径 find_path(RAPIDJSON_INCLUDE_DIRS rapidjson/rapidjson.h) include_directories(${RAPIDJSON_INCLUDE_DIRS}) add_executable(translate_crawler main.cpp src/TranslatorCrawler.cpp ...) target_link_libraries(translate_crawler ${CURL_LIBRARIES}) # 如果CURL需要SSL可能还需要链接OpenSSL # target_link_libraries(translate_crawler ${CURL_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)对于RapidJSON可以直接将源码放入项目的third_party目录或者使用包管理器如vcpkg, conan安装。6.2 单元测试与集成测试爬虫的测试分为几个层次单元测试使用Google Test或Catch2等框架。测试RequestBuilder的参数生成是否正确ResponseParser能否解析各种格式的JSONRateLimiter是否真的限制了频率。集成测试在测试环境中用一个模拟服务器如使用Python的Flask或http.server模块搭建来模拟谷歌翻译的API测试整个TranslatorCrawler的流程。模拟服务器可以返回预设的响应也可以模拟网络延迟、错误状态码等验证爬虫的健壮性。端到端测试谨慎地进行少量真实API调用验证整个链路在真实环境下是否工作。这类测试必须频率极低且最好在非高峰时段进行。6.3 日志与监控一个运行在后台的爬虫必须有完善的日志系统。我们使用一个轻量级的日志库如spdlog来记录不同级别的信息DEBUG: 记录详细的请求URL、POST数据、原始响应。用于开发和调试。INFO: 记录翻译任务的开始、成功完成。WARN: 记录网络超时、重试、API返回非预期格式。ERROR: 记录连接失败、解析失败、达到重试上限等严重错误。日志可以帮助我们快速定位线上问题。此外还可以集成简单的监控统计翻译的成功率、平均耗时、不同错误类型的分布为系统优化提供数据支持。7. 常见问题排查与调试技巧即使设计再完善爬虫在运行中也会遇到各种稀奇古怪的问题。这里记录几个典型的排查场景问题1突然所有请求都返回403 Forbidden或429 Too Many Requests。排查首先检查RateLimiter的设置是否过于激进。查看日志中请求的时间戳计算实际QPS。其次检查User-Agent是否被识别为爬虫。可以尝试更换为更常见的浏览器UA字符串。最后检查是否触发了IP级别的限制此时需要暂停爬虫一段时间如几小时或者使用代理IP。问题2翻译结果突然全部为空或错误。排查这极有可能是谷歌翻译的API接口或参数格式发生了变化。第一步用浏览器开发者工具抓取一次最新的翻译请求对比你的RequestBuilder生成的参数特别是tk等动态字段是否一致。第二步检查ResponseParser是否能解析新的响应格式。可以临时开启DEBUG日志将收到的原始响应体打印出来进行分析。问题3程序运行一段时间后内存缓慢增长。排查这是典型的内存泄漏。使用ValgrindLinux或Visual Studio的诊断工具Windows来运行程序检查是否有未释放的CURL句柄、动态分配的内存。确保所有从RapidJSONGetString()等函数获取的字符串指针其生命周期得到了妥善管理如果文档被销毁这些指针会悬空。问题4在Linux服务器上编译失败提示找不到CURL或SSL库。排查这是依赖问题。在Ubuntu/Debian上需要安装开发包sudo apt-get install libcurl4-openssl-dev。确保OpenSSL库也已安装。CMake的find_package需要这些库的头文件和链接库存在于系统路径中。问题5HTTPS请求失败证书验证错误。排查如果是在自定义环境中如内部服务器、旧系统可能需要指定CA证书捆绑包的路径。使用curl_easy_setopt(curl_, CURLOPT_CAINFO, /path/to/cacert.pem)。可以从CURL官网下载最新的cacert.pem文件。切勿在生产环境长期关闭证书验证。写一个健壮的C爬虫就像在钢丝上跳舞需要在性能、稳定性、可维护性和对目标服务的尊重之间找到平衡。这个谷歌翻译爬虫的项目虽然核心功能不复杂但把它做“硬核”、做可靠涉及的知识点却非常广泛。从底层的网络编程、内存管理到上层的架构设计、反爬策略每一个环节都需要仔细打磨。希望这次深度的源码解析能给你带来一些超越代码本身的工程思考。

相关新闻

2026/7/18 8:08:01

终极自由:用Python脚本完全掌控索尼DPT-RP1电子纸设备

终极自由:用Python脚本完全掌控索尼DPT-RP1电子纸设备 【免费下载链接】dpt-rp1-py Python script to manage a Sony DPT-RP1 without the Digital Paper App 项目地址: https://gitcode.com/gh_mirrors/dp/dpt-rp1-py 你是否厌倦了索尼官方Digital Paper Ap…

2026/7/18 8:08:01

ToastFish:Windows通知栏背单词的终极效率革命

ToastFish:Windows通知栏背单词的终极效率革命 【免费下载链接】ToastFish 一个利用摸鱼时间背单词的软件。 项目地址: https://gitcode.com/GitHub_Trending/to/ToastFish 在数字时代,我们的注意力被无限切割成碎片,而ToastFish正在悄…

2026/7/18 9:23:07

芯片设计中的网表:从基础概念到先进工艺应用

1. 网表在芯片设计流程中的定位 当RTL代码通过综合工具转换为门级网表时,标志着芯片设计从抽象描述进入了物理实现阶段。这个阶段转换就像把建筑师的蓝图转化为施工队的钢筋水泥配料单。网表本质上是一种用文本形式描述的电路连接关系,包含了设计中所有逻…

2026/7/18 9:23:07

液晶电视花屏遥控器调试指南:60%问题无需拆机

这次我们来看一个实用的家电维修技巧——液晶电视花屏问题的遥控器调试方法。很多用户遇到电视花屏第一反应就是屏幕坏了要换屏,但实际上不少花屏问题只是设置或信号源的小故障,用遥控器简单调整就能解决。 液晶电视花屏通常表现为屏幕出现条纹、色块、…

2026/7/18 9:23:07

openEuler Native-Turbo-kernel:终极性能优化内核深度解析

openEuler Native-Turbo-kernel:终极性能优化内核深度解析 【免费下载链接】native-turbo-kernel Native-Turbo-kernel is the performance optimized kernel of native microarchitecture of operating system. 项目地址: https://gitcode.com/openeuler/native-…

2026/7/18 9:23:07

免费解锁网易云音乐:NCMDump解密工具完全指南

免费解锁网易云音乐:NCMDump解密工具完全指南 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM加密文件无法在其他设备播放而烦恼吗?今天我要为你介绍一款神奇的NCMDump解密工具&…

2026/7/18 9:18:07

CardKit实战案例:构建社交媒体图片生成器的完整教程

CardKit实战案例:构建社交媒体图片生成器的完整教程 【免费下载链接】cardkit A simple, powerful and fully configurable image editor for web browsers and servers. Optional UI included. 项目地址: https://gitcode.com/gh_mirrors/ca/cardkit CardKi…

2026/7/17 5:59:06

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/17 1:21:45

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/17 7:39:19

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/18 0:00:24

某智驾大牛创业

作者:钟声编辑:Mark出品:红色星际头图:智能驾驶图片据悉,国内某头部智驾公司端到端模型技术大牛Z投身创业,并且已经拿到融资。Z不仅是该头部公司内部最年轻的对标阿里P10级别技术负责⼈,更是业内…

2026/7/17 14:59:44

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…