Appium + Python 移动端数据采集实战:从环境搭建到多设备扩展
1. 为什么我最终选择了 Appium 而不是其他自动化方案做移动端数据采集这行绕不开一个根本问题数据在别人的 App 里你怎么把它拿出来。早期我试过抓包分析接口用 Charles 或者 mitmproxy 挂代理然后写脚本重放请求。这条路在几年前还行得通现在越来越难走——签名校验、证书绑定、请求参数加密、设备指纹一层层加上去逆向成本高得离谱。后来我也试过无障碍服务写个 Android 辅助功能去读屏幕节点但无障碍服务本身权限敏感长时间运行容易被系统回收而且不同厂商 ROM 对无障碍的支持差异很大维护起来心力交瘁。最后我把主力方案切到了Appium Python 真实 Android 设备这条路上。它的核心逻辑很朴素既然 App 把数据渲染在屏幕上那我就模拟人的操作去点、去滑、去读把界面上的文本抓下来。本质上是用自动化的方式替代人工操作不碰接口加密不碰证书绑定走的是 UI 层的路子。这套方案适合什么人我总结下来是三类第一类是做竞品分析的产品或运营需要定期采集某个垂直领域 App 的公开列表数据第二类是做学术研究的学生或研究员需要批量获取 App 内公开展示的信息做统计分析第三类是做自动化测试的工程师本来就在用 Appium顺手把数据采集的活也干了。如果你追求的是海量、高频、毫秒级的数据抓取那这套方案不适合你它的效率受限于 UI 渲染速度和操作间隔属于慢工出细活的类型。但它的优势也很明显稳定性高、对抗性弱、可维护性好。只要 App 的界面结构不大改脚本就能一直跑。而且因为走的是真实用户操作路径服务端的风控系统很难把你和真人区分开——前提是你别把操作频率调得太离谱。在展开具体实现之前先把整条技术链路说清楚。Appium 是一个基于 WebDriver 协议的自动化测试框架它在 Android 端通过 UiAutomator2 驱动与设备通信。Python 作为客户端语言通过 Appium-Python-Client 库向 Appium Server 发送指令Server 再翻译成 UiAutomator2 能理解的命令最终在设备上执行点击、滑动、输入、获取节点信息等操作。整个链路是Python 脚本 → Appium Server → UiAutomator2 → Android 设备 → App 界面。数据采集的关键就在于最后一步——从界面节点树里把我们需要的信息提取出来。理解了这条链路后面所有的配置、调试、优化其实都是在保证这条链路的稳定和高效。2. 环境搭建中最容易翻车的几个环节环境搭建这一步看起来就是装几个软件的事但实际操作下来十个人里有八个会在这里卡住。我把踩过的坑按顺序梳理一遍你照着走能省不少时间。2.1 版本匹配是最大的隐形杀手Appium 生态里版本兼容性问题极其突出。我遇到过最典型的一次Appium Server 升到了 2.x但 Appium-Python-Client 还是 1.x 的旧版本结果脚本发出去的指令格式对不上报了一堆莫名其妙的错误。后来我固定了一套经过验证的组合组件推荐版本说明Appium Server2.5.x用 npm 安装别用桌面版Appium-Python-Client3.x与 Server 2.x 配套UiAutomator2 Driver最新通过 appium driver install 安装Python3.103.8 也能用但 3.10 更稳Android Platform-Tools最新adb 版本太旧会连不上新设备安装 Appium Server 我建议直接用 npm 命令行装不要用那个图形化的 Appium Desktop。桌面版更新慢而且日志输出不如命令行清晰。命令很简单npm install -g appium appium driver install uiautomator2装完之后用appium driver list --installed确认 UiAutomator2 驱动已经就位。这一步不做的话后面启动会话时会报 No driver found 之类的错误。2.2 设备连接不只是插上 USB 线Android 设备连接这块有几个细节必须处理。第一开发者选项和 USB 调试要打开这个大家都知道。但很多人不知道的是部分厂商 ROM 还需要额外打开USB 安装和USB 调试安全设置两个开关否则 adb 能识别设备但 Appium 启动不了应用。第二用adb devices确认设备列表时如果显示的是unauthorized说明设备上还没有授权这台电脑的调试请求。这时候看手机屏幕应该有一个授权弹窗勾选始终允许再确认。如果弹窗没出现可以试试adb kill-server然后adb start-server重新触发。第三如果你要用无线连接比如设备放在远处可以用adb tcpip 5555切换到 TCP 模式然后adb connect 设备IP:5555。但要注意无线连接对网络稳定性要求高采集过程中如果网络抖动导致连接断开脚本就会中断。我的经验是长时间采集任务尽量用 USB 连接稳定性远好于无线。2.3 一个容易被忽略的细节输入法冲突这个问题很隐蔽。Appium 在向输入框发送文本时底层是通过 adb 指令实现的。但如果设备上装了某些第三方输入法尤其是那种带云联想、弹窗广告的输入法会干扰文本输入过程导致输入不完整或者焦点丢失。我的做法是在采集设备上把默认输入法切回系统自带的或者安装 Appium 官方推荐的 Unicode IME。Appium 在需要时会自动切换到 Unicode IME 来输入非 ASCII 字符但如果系统里没有这个 IME中文输入就会失败。你可以在启动参数里加上unicodeKeyboard: true和resetKeyboard: true让 Appium 自动处理输入法切换。但实测下来这个参数在某些 ROM 上不生效最稳妥的办法还是手动确保设备上有一个干净的输入法环境。3. 从零写一个能跑的数据采集脚本环境就绪之后进入正题。我不会给你一个万能模板因为不同 App 的界面结构千差万别通用模板反而容易误导。我会用一个模拟的场景来演示完整的思路和代码结构你理解了逻辑之后套用到自己的目标 App 上就行。假设我们要采集的目标是一个模拟的电商类 App首页有一个商品列表每个商品卡片上显示商品名称、价格和销量。我们需要做的是打开 App滚动列表逐个提取商品信息翻页继续直到采集到足够的数据。3.1 启动会话参数配置里的门道启动一个 Appium 会话核心是配置desired_capabilities。这里面每个参数都有讲究我逐个解释from appium import webdriver from appium.options.android import UiAutomator2Options options UiAutomator2Options() options.platform_name Android options.platform_version 13 options.device_name your_device_id options.app_package com.example.shopapp options.app_activity .MainActivity options.automation_name UiAutomator2 options.no_reset True options.new_command_timeout 300 options.set_capability(unicodeKeyboard, True) options.set_capability(resetKeyboard, True) driver webdriver.Remote(http://127.0.0.1:4723, optionsoptions)platform_version这个参数很多人随便填或者不填。实际上它会影响 UiAutomator2 驱动的行为填错了可能导致某些 API 不可用。最准确的做法是用adb shell getprop ro.build.version.release查一下设备的真实系统版本。no_reset设为 True 的意思是每次启动会话时不重置应用状态。这个参数很关键——如果你设为 FalseAppium 每次都会清除应用数据重新开始那你的登录状态、浏览记录全没了采集效率极低。但要注意no_reset为 True 时App 不会回到初始页面你需要自己处理当前在哪个页面的判断逻辑。new_command_timeout默认是 60 秒意思是如果 60 秒内没有新指令Appium 会自动结束会话。数据采集过程中有时候滚动加载需要等待较长时间60 秒可能不够用我一般设为 300 秒。3.2 元素定位id、xpath 和 accessibility_id 的取舍元素定位是 Appium 脚本里最核心也最脆弱的部分。Appium 支持多种定位策略常用的有这几种resource-id对应 Android 的resource-id属性最稳定优先使用accessibility_id对应content-desc属性适合没有 id 的元素xpath万能但脆弱界面结构一变就失效class name按控件类型定位通常配合其他条件使用我的原则是能用 id 就不用 xpath能用相对路径就不用绝对路径。举个例子假设商品名称的 resource-id 是com.example.shopapp:id/product_name那定位就很简单product_names driver.find_elements(By.ID, com.example.shopapp:id/product_name) for name_el in product_names: print(name_el.text)但现实往往没这么美好。很多 App 的列表项没有独立的 id或者 id 是动态生成的。这时候就得用 xpath。写 xpath 的时候尽量避免用绝对路径以/开头的那种因为绝对路径对层级变化零容忍。用相对路径配合属性条件会好很多# 不推荐绝对路径界面一改就废 # /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/... # 推荐相对路径 属性条件 elements driver.find_elements(By.XPATH, //android.widget.TextView[contains(text, ¥)])还有一个技巧如果列表项的结构一致可以先定位到列表容器再在容器内查找子元素。这样即使页面其他部分发生变化只要列表本身结构不变脚本就不会挂。3.3 滚动加载与去重采集逻辑的核心列表类页面的采集核心逻辑就是滚动—提取—判断是否到底—继续滚动。听起来简单但实际写起来有几个坑。第一个坑是滚动方式的选择。Appium 提供了几种滚动方法swipe、scroll、UiScrollable。swipe是最通用的通过坐标模拟手指滑动def swipe_up(driver, duration500): window_size driver.get_window_size() start_x window_size[width] * 0.5 start_y window_size[height] * 0.8 end_y window_size[height] * 0.3 driver.swipe(start_x, start_y, start_x, end_y, duration)duration参数控制滑动速度单位是毫秒。设得太小比如 100滑动会变成甩的动作列表会惯性滚动很远容易跳过内容设得太大比如 2000滑动会变得很慢效率低。我一般用 500 到 800 之间具体根据 App 的列表滚动特性调整。第二个坑是判断是否已经到底。常见的方法有几种比较滚动前后的页面源码是否一致、检查是否有没有更多了的提示元素、判断当前可见的元素是否和上一次完全重合。我用得最多的是第一种简单可靠def is_bottom(driver, prev_source): current_source driver.page_source return current_source prev_source但这个方法有个问题如果页面有动态内容比如倒计时、动画即使没到底页面源码也会变化。所以更稳妥的做法是结合多种判断比如同时检查页面源码和最后一个元素的文本。第三个坑是数据去重。滚动加载的过程中同一条数据可能在多个屏幕位置出现如果不去重采集结果里会有大量重复。最简单的去重方式是用一个 set 记录已经采集过的唯一标识比如商品 id 或名称价格的组合seen set() items [] # ... 在循环中 ... for name_el in product_names: name name_el.text if name not in seen: seen.add(name) items.append(name)如果数据量很大set 占内存太多可以考虑用布隆过滤器或者把已采集的 id 写入本地文件每次读取判断。不过对于一般规模的采集任务set 足够了。3.4 异常处理让脚本能自己扛过去数据采集脚本跑起来之后最怕的就是中途崩溃。网络波动、元素加载慢、弹窗干扰、App 闪退任何一个环节出问题都会导致脚本中断。所以异常处理不是可选项是必选项。我的做法是在关键操作外面包一层重试逻辑from selenium.common.exceptions import NoSuchElementException, TimeoutException def safe_find(driver, by, value, retries3, timeout5): for i in range(retries): try: element WebDriverWait(driver, timeout).until( EC.presence_of_element_located((by, value)) ) return element except (NoSuchElementException, TimeoutException): if i retries - 1: raise time.sleep(1)除了元素查找滚动操作、点击操作、页面切换都需要类似的保护。另外我强烈建议在脚本里加一个全局的异常捕获当发生未预期的错误时先把当前页面截图保存下来再把页面源码 dump 到文件里。这样事后排查问题的时候你能知道脚本是在哪个页面、什么状态下挂掉的。try: # 主采集逻辑 pass except Exception as e: driver.save_screenshot(error_screenshot.png) with open(error_page_source.xml, w, encodingutf-8) as f: f.write(driver.page_source) print(f采集异常: {e}) raise这个习惯帮我省了无数次调试时间。很多时候脚本在半夜跑挂了第二天早上看到截图和页面源码一眼就能定位问题。4. 让脚本跑得更稳更快的实战调优脚本能跑通只是第一步真正投入生产使用还得在稳定性和效率上下功夫。这一部分我分享几个经过实战验证的调优技巧。4.1 隐式等待与显式等待的配合策略Appium 的等待机制分两种隐式等待implicitly_wait和显式等待WebDriverWait。隐式等待是全局的设置一次之后每次查找元素都会自动等待指定时间显式等待是针对特定条件的可以等待元素可见、可点击、文本包含某个内容等。很多人只设一个隐式等待就完事了比如driver.implicitly_wait(10)。这样做的问题是当元素确实不存在时每次查找都要白等 10 秒采集效率极低。我的策略是隐式等待设一个较小的值比如 3 秒作为兜底关键操作使用显式等待配合具体条件。driver.implicitly_wait(3) # 关键操作使用显式等待 element WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, com.example.shopapp:id/next_button)) ) element.click()这样既保证了元素加载慢时有足够的等待时间又避免了无谓的等待浪费。4.2 页面源码解析比逐个查找元素更高效当需要从列表页提取大量结构化数据时逐个find_element再取.text的方式效率不高因为每次查找都是一次跨进程通信。更高效的做法是直接获取整个页面的 XML 源码然后用 Python 的 XML 解析库在本地提取数据。import xml.etree.ElementTree as ET def parse_page_source(source): root ET.fromstring(source) items [] for node in root.iter(android.widget.TextView): text node.get(text, ) if text.startswith(¥): items.append(text) return items这样做的好处是一次通信拿到全部数据后续解析在本地完成速度快很多。而且你可以用 XPath 或 ElementTree 的各种查询方法灵活提取需要的信息。缺点是对动态加载的内容不友好——如果列表是懒加载的页面源码里可能只有当前可见的部分。所以这个方法适合那些一次性渲染全部内容的页面。4.3 控制操作频率别把风控惹毛了虽然是模拟真人操作但如果你的脚本以固定间隔、毫秒级精度疯狂滑动和点击服务端的风控系统还是能识别出来的。我的经验是在操作之间加入随机化的等待时间import random def random_sleep(min_sec1.0, max_sec3.0): time.sleep(random.uniform(min_sec, max_sec))不要用固定的time.sleep(2)因为固定间隔本身就是一种机器特征。随机化的等待更接近真人行为。另外滑动的距离和速度也可以加入随机扰动不要每次都滑一模一样的距离。还有一点控制单次采集的时长和总量。不要一个脚本连续跑十几个小时不停歇那样很容易触发风控。我的做法是分段采集每跑 30 到 40 分钟就休息几分钟模拟真人使用 App 的节奏。4.4 数据存储边采边存别等最后我见过很多脚本是把所有数据先存在内存的 list 里等全部采集完了再一次性写入文件。这样做风险很大——如果脚本在最后阶段崩溃了前面采的数据全丢了。正确的做法是边采边存每采集一页就追加写入一次import csv def save_items(items, filenamedata.csv): with open(filename, a, newline, encodingutf-8) as f: writer csv.writer(f) for item in items: writer.writerow(item)如果是结构化数据用 CSV 或 JSON Lines 格式都很方便。JSON Lines 的好处是每行一个独立的 JSON 对象追加写入简单读取时也容易按行解析。数据量特别大的话可以考虑写入 SQLite 数据库支持去重和查询。5. 那些只有踩过才知道的坑这一部分我单独拿出来讲因为有些问题在官方文档里根本找不到只有实际跑过才会遇到。5.1 弹窗和广告的随机干扰App 里的弹窗是最烦人的干扰源。开屏广告、版本更新提示、活动弹窗、权限请求这些弹窗出现的时间不固定内容也不固定脚本很容易被卡住。我的处理策略是在每次页面操作之前先检查是否存在已知的弹窗元素如果有就关掉。def close_popups(driver): popup_ids [ com.example.shopapp:id/close_ad, com.example.shopapp:id/dialog_cancel, com.example.shopapp:id/iv_close, ] for pid in popup_ids: try: el driver.find_element(By.ID, pid) if el.is_displayed(): el.click() time.sleep(0.5) except NoSuchElementException: pass这个函数在每次滚动或点击之前调用一次能解决大部分弹窗问题。但有些弹窗的关闭按钮没有固定 id那就得用坐标点击或者图像识别来兜底。坐标点击的缺点是不同分辨率设备上坐标不一样需要做适配。5.2 列表项复用导致的元素错位Android 的 RecyclerView 有个特性叫视图复用——为了节省内存滑出屏幕的列表项会被回收新进入屏幕的项会复用这些被回收的视图。这导致一个问题当你获取了一组元素引用之后如果页面发生了滚动这些引用可能已经指向了错误的元素。我踩过这个坑先find_elements拿到 10 个商品名称元素然后逐个读取.text结果发现读出来的数据错位了第三个元素读到的其实是第五个商品的名字。原因就是在读取过程中页面发生了微小的滚动触发了视图复用。解决办法是先获取页面源码或一次性提取所有文本再进行滚动操作。不要在持有元素引用的同时进行滚动。如果必须逐个操作元素那就在每次操作前重新查找。5.3 不同设备分辨率下的坐标适配用坐标点击或滑动时硬编码的坐标在不同分辨率的设备上会失效。比如你在 1080x2340 的设备上测试通过换到 720x1280 的设备上就点不中了。解决办法是使用相对坐标——基于屏幕宽高的百分比来计算def get_relative_coord(driver, x_ratio, y_ratio): size driver.get_window_size() return int(size[width] * x_ratio), int(size[height] * y_ratio)这样无论在什么分辨率的设备上坐标都能自适应。当然最好的方案还是尽量用元素定位而不是坐标定位坐标定位只作为最后的兜底手段。5.4 App 版本更新导致脚本失效这是最无奈但也最常见的问题。App 一更新resource-id 改了、页面结构变了、按钮位置挪了脚本就跑不通了。应对策略有两个一是尽量使用稳定的定位属性比如 content-desc 通常比 resource-id 更稳定因为开发者在改 id 的时候往往会保留 content-desc二是把定位策略做成可配置的把所有的 id、xpath 抽到一个配置文件里App 更新后只需要改配置不用动代码。# locators.py PRODUCT_NAME com.example.shopapp:id/product_name PRODUCT_PRICE com.example.shopapp:id/product_price NEXT_PAGE_BTN com.example.shopapp:id/next_page这个习惯在长期维护中能省大量时间。6. 关于合规与边界的一些个人体会做数据采集这件事技术之外还有一些必须考虑的东西。我自己的原则是只采集公开可见的信息控制采集频率不对目标服务造成压力采集的数据仅用于个人学习分析不对外传播。具体来说我在写脚本之前会先确认几件事目标数据是否是 App 公开展示给所有用户的采集频率是否在合理范围内采集的数据用途是否合法合规如果任何一个问题的答案是否定的我就会重新评估这个采集任务是否应该做。另外从技术角度讲控制采集频率不仅是为了合规也是为了脚本本身的稳定性。高频请求容易触发服务端的限流或封禁导致 IP 被拉黑、账号被冻结反而得不偿失。我一般会把单次采集任务控制在合理规模内采集完成后间隔一段时间再启动下一轮。还有一点值得注意采集到的数据要做好本地管理不要随意上传到公开平台或分享给不相关的人。数据安全这件事怎么强调都不为过。7. 从单机脚本到批量采集的扩展思路当你跑通了单设备的采集脚本之后很自然会想到扩展到多设备并行采集。这个思路是对的但实现起来有几个关键点需要处理。首先是设备管理。多台设备同时连接时每台设备需要独立的 Appium 会话通过device_name或udid参数区分。你可以用adb devices获取所有已连接设备的列表然后为每台设备启动一个独立的 Python 进程或线程。import subprocess def get_connected_devices(): result subprocess.run([adb, devices], capture_outputTrue, textTrue) lines result.stdout.strip().split(\n)[1:] devices [line.split(\t)[0] for line in lines if \tdevice in line] return devices然后是任务分配。最简单的策略是每台设备负责一部分采集任务比如按页码或按关键词拆分。复杂一点的可以用一个中心化的任务队列每台设备从队列里领取任务完成后回报状态。这样即使某台设备中途挂了任务也不会丢失。最后是资源控制。多设备并行时CPU 和内存的消耗会成倍增加。Appium Server 本身比较吃资源如果同时跑五六台设备建议把 Appium Server 分散到不同的机器上每台机器负责两三台设备。另外USB hub 的供电也要注意设备多了容易供电不足导致连接不稳定用带独立电源的 hub 会好很多。这套方案我在实际项目中跑过四台设备并行采集效率大概是单设备的 3.5 倍左右有调度开销和资源竞争不可能达到线性 4 倍。对于中等规模的数据采集需求这个方案已经足够用了。如果你需要更大规模的采集那就得考虑设备农场或者云真机方案了。不过那涉及到成本和管理复杂度的大幅上升需要根据实际需求权衡。我的建议是先用单设备把逻辑跑通、把稳定性做好再考虑扩展的事。很多需求其实单设备跑一晚上就能搞定没必要一上来就搞集群。

相关新闻

C语言语法分析器实战:递归下降与AST构建

C语言语法分析器实战:递归下降与AST构建

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 2:06:24 阅读更多 →
福昕阅读器10.1绿色版部署实战:目录拆解、配置外置与命令行集成

福昕阅读器10.1绿色版部署实战:目录拆解、配置外置与命令行集成

简介:福昕PDF阅读器10.1.0.37527的绿色便携版压缩包,面向需要在多台电脑间切换办公、希望免安装直接使用的个人用户和办公人群,也适合经常阅读、批注PDF的学生、文员与工程技术人员。压缩包共275个文件,大小约88.57MB,…

2026/10/13 2:05:23 阅读更多 →
九月开源项目盘点:20个值得关注的小而精工具与AI应用

九月开源项目盘点:20个值得关注的小而精工具与AI应用

1. 九月开源项目盘点的整体筛选逻辑九月份是开源社区一年中比较特殊的时间节点。北半球的开发者结束了暑期休假,项目维护者开始集中处理积压的 issue 和 PR,很多团队也会选择在这个时间窗口发布重要版本。我翻了一圈九月份冒出来的项目,发现一…

2026/10/13 2:05:23 阅读更多 →

最新新闻

FPGA入门必做:可逆计数器设计详解与上板调试

FPGA入门必做:可逆计数器设计详解与上板调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 6:41:49 阅读更多 →
外贸企业如何借助社交媒体拓展海外市场

外贸企业如何借助社交媒体拓展海外市场

一、行业现状:海外社交媒体成为外贸获客新渠道近年来,随着数字技术的发展和全球消费习惯的变化,海外社交媒体已不再仅仅是个人社交娱乐的平台,而是逐步成为外贸企业触达海外客户、展示品牌形象、获取商业询盘的重要渠道。Facebook…

2026/10/13 6:41:49 阅读更多 →
Modbus TCP设备通信代码

Modbus TCP设备通信代码

一、核心前置知识点:Modbus TCP协议帧格式(必考背诵)1.1 请求报文(Rx 读取请求帧)帧示例:00 01 00 00 00 06 01 03 00 00 00 04字节段含义说明00 01事务ID(Transaction ID)请求与响应必须一致,用…

2026/10/13 6:41:49 阅读更多 →
Apache Calcite 1.4.0-incubating 发布解析:RelBuilder 关系代数构建器 API 的诞生

Apache Calcite 1.4.0-incubating 发布解析:RelBuilder 关系代数构建器 API 的诞生

后端数据库 【免费下载链接】calcite Apache Calcite 项目地址: https://gitcode.com/gh_mirrors/ca/calcite 点击查看 免费下载 2015 年 9 月 2 日,Apache Calcite(Incubating)发布 1.4.0-incubating 版本,这一版本在…

2026/10/13 6:41:49 阅读更多 →
Peekaboo 实时多目标认证协调器:双后台控制器与前台语义重叠的行为契约全解析

Peekaboo 实时多目标认证协调器:双后台控制器与前台语义重叠的行为契约全解析

GUI 自动化MCP 服务AI 应用 【免费下载链接】Peekaboo Peekaboo is a macOS CLI & optional MCP server that enables AI agents to capture screenshots of applications, or the entire system, with optional visual question answering through local or remote AI mod…

2026/10/13 6:41:49 阅读更多 →
链栈实验避坑指南:头歌数据结构实训全解析

链栈实验避坑指南:头歌数据结构实训全解析

简介:头歌数据结构链栈基本操作与应用学习资料包,面向正在学习数据结构与C实现的学生,以链栈这一动态栈结构为主线展开讲解。内容详细梳理链表节点定义、链栈结构体设计,以及初始化、销毁、清空、判空、求长、取栈顶、入栈、出栈、…

2026/10/13 6:40:49 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →