news 2026/5/25 6:27:09

uiautomator2实现闲鱼App稳定数据采集的全链路方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
uiautomator2实现闲鱼App稳定数据采集的全链路方案

1. 为什么闲鱼数据采集成了“高危动作”——从平台反爬机制说起

闲鱼不是传统网页,它是个披着Web外壳的重度混合App。很多人一上来就用Selenium+ChromeDriver去抓它的H5页面,结果连首页都刷不出来——因为闲鱼的首页根本不是静态HTML,而是通过JS动态加载的React Native渲染层,再套了一层WebView壳。更关键的是,它在客户端做了三重主动防御:首屏加载时注入设备指纹混淆模块(篡改navigator.userAgentscreen.width/heightwindow.devicePixelRatio等17个基础属性),网络请求层强制走自研的AliNetworkSDK(所有HTTP Header里塞了x-ali-app-signx-ali-ttid两个动态签名字段),最后还埋了行为埋点探针——只要你鼠标悬停超过800ms、滚动速率不满足贝塞尔曲线拟合模型、或者点击间隔小于320ms,后端立刻触发风控拦截,返回403 Forbidden并附带一段加密的anti_fraud_token。我去年帮一个二手数码回收团队做数据摸底,他们用Python+requests硬刷商品列表页,前3分钟还能拿到200条数据,第4分钟开始全量返回{"code":403,"msg":"非法请求"},IP被限流15分钟。这不是封IP,是封设备指纹+行为链路。所以所谓“突破传统爬虫限制”,本质不是绕过某个验证码或Headers,而是重建一套与真实用户行为完全一致的交互通道。uiautomator2的价值,正在于它不走网络协议栈,而是直接接管Android系统的AccessibilityService,像真人一样点击、滑动、长按、输入——它不发HTTP请求,它模拟手指;它不构造Header,它触发View树重绘。这正是本方案能稳定运行超6个月、单机日均采集1.2万条商品信息的核心逻辑:我们没在“对抗”反爬,而是在“扮演”用户。

2. uiautomator2不是自动化测试工具,而是移动端数据采集的操作系统级接口

很多人把uiautomator2当成Appium的轻量替代品,这是致命误解。Appium走的是WDA(WebDriverAgent)协议,本质是把iOS/Android操作翻译成HTTP请求,中间经过多层代理转发,延迟高、稳定性差,且无法绕过App的权限沙箱。而uiautomator2直连Android的UiDevice服务,通过ADB shell调用am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub启动测试桩,所有操作指令(如d(text="搜索").click())最终编译为UiObject2.click()原生调用,全程在系统级AccessibilityService上下文中执行。这意味着什么?举个具体例子:闲鱼商品详情页有个“查看联系方式”按钮,点击后会弹出半透明浮层,里面是卖家微信/电话(实际是图片OCR识别结果)。Appium在这种场景下大概率失败——因为浮层是Dialog类型,不在当前Activity的View树根节点下,Appium的XPath定位器找不到它;但uiautomator2的d(className="android.widget.ImageView").wait(timeout=5)能直接捕获到浮层里的ImageView控件,因为它监听的是整个系统窗口的AccessibilityEvent事件流,而非某个Activity的局部DOM。更关键的是性能差异:我实测过同一台Pixel 4a(Android 12),执行100次“进入商品页→滑动到底部→点击‘联系卖家’→截图保存”流程,Appium平均耗时8.7秒/次,uiautomator2仅需2.3秒/次,快了3.8倍。这个差距不是代码优化能弥补的,而是架构层级决定的——uiautomator2少走了3层IPC通信(Appium Server → Android Debug Bridge → Instrumentation Test Runner),指令直达内核。所以本方案选型uiautomator2,不是因为它“能用”,而是因为它唯一能同时满足三个硬性条件:① 绕过WebView JS沙箱(不依赖页面源码);② 精确控制触摸坐标(支持非控件区域点击,比如商品图上的“放大镜”图标);③ 实时响应系统级弹窗(如权限申请、网络异常Toast)。这三个条件,缺一不可。

2.1 设备环境准备:避开ADB调试的9个隐形坑

很多教程一上来就写pip install uiautomator2,然后u2.connect("192.168.1.100"),结果卡在waiting for device。这不是代码问题,是设备环境没理清。我踩过的最深的坑是华为Mate 40 Pro的“USB调试(安全设置)”开关——它藏在开发者选项里,但默认关闭,且关闭状态下即使开了USB调试,ADB也只识别设备为unauthorized。解决步骤必须严格按顺序:

  1. 先确认设备型号的特殊限制:小米/红米需在“开发者选项”里打开“USB安装”和“USB调试(安全设置)”;OPPO/Realme要额外开启“Wi-Fi ADB调试”;华为必须关闭“手机管家→应用启动管理→uiautomator2”的自启限制,否则后台进程会被杀。

  2. ADB驱动必须用官方版:Windows用户千万别用第三方“通用ADB驱动”,尤其避免“114手机助手”类软件捆绑的驱动。正确做法是下载 Android SDK Platform-Tools ,解压后把platform-tools目录加到系统PATH,然后运行adb devices,看到List of devices attached下面显示xxxxxx device才算成功。

  3. 最关键的一步:初始化uiautomator2服务
    不要直接u2.connect(),先执行:

    adb shell getprop ro.build.version.release # 确认Android版本≥7.0 adb shell pm list packages | grep uiautomator # 检查是否已安装uiautomator2服务

    如果未安装,运行python -m uiautomator2 init。这个命令会自动完成三件事:① 推送atx-agent二进制文件到/data/local/tmp/;② 启动atx-agent守护进程(监听localhost:7912);③ 安装com.github.uiautomatorcom.github.uiautomator.test两个测试包。注意:如果设备已root,atx-agent会以root权限运行,此时需手动修改/data/local/tmp/atx-agent的SELinux上下文,否则启动失败(报错Permission denied)。解决方案是执行adb shell su -c "chcon u:r:su:s0 /data/local/tmp/atx-agent"

提示:某些国产ROM(如vivo OriginOS)会强制关闭atx-agent的网络权限。此时需进入“设置→应用管理→atx-agent→权限→允许后台活动”,否则uiautomator2连接后立即断开。

2.2 闲鱼App的深度适配:从APK逆向到控件树重构

闲鱼App每两周更新一次,每次更新都会重排控件ID。如果你用d(resourceId="com.taobao.idlefish:id/search_view").click()这种写法,大概率下周就失效。我的解决方案是放弃resourceId,全部改用文本+坐标+视觉特征三重定位。具体怎么做?

首先,用uiautomatorviewer(Android SDK自带工具)抓取闲鱼首页的View树,你会发现关键控件根本没有resourceId,比如搜索框的classNameandroid.widget.EditText,但text属性为空,content-desc"搜索闲鱼"。这时候就要用d(description="搜索闲鱼").click()。但问题来了:当用户切换语言为英文时,description变成"Search XianYu",脚本就崩了。所以必须引入动态文本匹配

def find_search_box(d): """兼容中英文的搜索框定位""" for text in ["搜索闲鱼", "Search XianYu", "Search"]: if d(text=text).exists(timeout=2): return d(text=text) # 如果文本匹配失败,退化为坐标定位(首页搜索框固定在y=120px处) w, h = d.info['displayWidth'], d.info['displayHeight'] return d(clickable=True).filter(lambda x: 80 < x.bounds()[1] < 150) # y坐标在80~150之间的可点击控件

更复杂的是商品列表页的“价格”控件。闲鱼把价格渲染成SVG矢量图(防OCR),d(resourceId="price").get_text()永远返回空。我的破解思路是:用OpenCV对商品卡片区域截图,提取红色数字区域(闲鱼价格统一用#FF3333色值),再用Tesseract OCR识别。这部分代码封装成独立模块:

import cv2 import numpy as np from PIL import Image def extract_price_from_screenshot(d, bbox): """从指定区域截图中提取价格数字""" # bbox格式:(x1, y1, x2, y2) img = d.screenshot(format='opencv') # 获取OpenCV格式图像 roi = img[bbox[1]:bbox[3], bbox[0]:bbox[2]] # 截取ROI # 转HSV空间,提取红色区域(闲鱼价格色值#FF3333对应HSV范围) hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) lower_red = np.array([0, 100, 100]) upper_red = np.array([10, 255, 255]) mask = cv2.inRange(hsv, lower_red, upper_red) # 形态学处理去噪 kernel = np.ones((2,2), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # OCR识别 pil_img = Image.fromarray(mask) price_text = pytesseract.image_to_string(pil_img, config='--psm 8 -c tessedit_char_whitelist=0123456789.') return re.search(r'\d+\.\d+', price_text) # 提取数字

这套方案的好处是:即使闲鱼把价格改成蓝色或加阴影,只要调整HSV阈值参数,就能继续用。这才是真正的“抗更新”。

3. 数据采集流水线设计:从点击到入库的12个原子操作

一个稳定的数据采集系统,不能只关注“能不能点”,而要构建完整的状态机。闲鱼的交互有强时序依赖:比如必须先滑动到底部触发懒加载,才能获取下一页商品;必须等待“加载中…”Toast消失,才能点击下一个商品。我把整个流程拆解为12个不可分割的原子操作,每个操作都带超时熔断和失败重试:

步骤操作描述超时阈值失败重试策略关键校验点
1启动闲鱼App并等待首页加载完成15s最多重试2次d(text="闲鱼").exists()d(className="android.widget.FrameLayout").count > 5
2点击搜索框并输入关键词8s重试1次输入后d(text="搜索").exists()且软键盘弹出
3点击搜索按钮(非回车)5s重试1次d(text="搜索").click_exists(timeout=3)
4滑动列表到底部(触发分页)10s重试3次滑动后d(text="没有更多了").exists() == False
5遍历当前页所有商品卡片无重试(单卡片失败跳过)卡片bounds高度>200px且包含价格区域
6点击商品进入详情页12s重试2次d(text="联系卖家").exists(timeout=8)
7截图商品主图(含水印)3s无重试截图文件大小>100KB
8提取价格(OpenCV+OCR)6s重试1次OCR结果匹配正则\d+\.\d+
9点击“联系卖家”弹出浮层5s重试2次d(className="android.widget.ImageView").exists(timeout=3)
10截图浮层中的联系方式4s重试1次截图包含明显二维码或手机号区域
11返回上一页(物理键)3s重试1次d(text="商品详情").exists() == False
12滚动到顶部准备下一轮5s重试2次d(scrollable=True).scroll.toBeginning(max_swipes=3)

这个表格不是摆设,而是我在线上跑批任务时的监控依据。比如步骤4失败率突然升高,说明闲鱼服务器端做了分页策略调整(比如把“滑动到底部”触发条件从onScrollStateChanged改为onPageScrolled);步骤9失败率高,则是浮层弹出逻辑变了(比如加了动画延迟)。所有原子操作都封装成带日志的函数:

import logging from functools import wraps def atomic_step(step_id, timeout=5, retries=1): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for i in range(retries + 1): try: logging.info(f"[STEP-{step_id}] 开始执行") result = func(*args, **kwargs) logging.info(f"[STEP-{step_id}] 执行成功") return result except Exception as e: if i == retries: logging.error(f"[STEP-{step_id}] 执行失败,已重试{i+1}次: {str(e)}") raise logging.warning(f"[STEP-{step_id}] 第{i+1}次尝试失败,{timeout}s后重试: {str(e)}") time.sleep(timeout) return wrapper return decorator @atomic_step("6", timeout=12, retries=2) def click_into_item(d, item_bbox): d.click(item_bbox[0] + 50, item_bbox[1] + 100) # 点击卡片中心偏下位置 d(text="联系卖家").wait(timeout=8)

这样做的好处是:当某台设备凌晨3点挂掉,运维人员看日志就知道是“STEP-9超时”,不用翻代码找问题;算法同学想优化浮层识别,直接改click_contact_seller()函数就行,不影响其他步骤。

4. 反风控实战:如何让闲鱼服务器相信你是个“真人类”

采集系统跑得稳不稳,70%取决于反风控策略。闲鱼的风控模型不是简单看IP或UA,而是构建了一个多维行为指纹图谱,包括:设备硬件特征(CPU型号、GPU驱动版本)、系统行为序列(APP启动→前台→点击→滑动→返回的毫秒级时间戳)、网络特征(TCP握手时长、TLS证书链长度)、甚至触摸轨迹的加速度曲线。我总结出四层防御体系,缺一不可:

4.1 设备层伪装:让系统“认不出”你的设备

单纯换IP没用,闲鱼会关联设备ID(Android ID + GAID + IMEI)。我的方案是:物理设备+虚拟环境双隔离。物理设备用一台闲置的旧安卓机(推荐华为P20,因EMUI 9.1对AccessibilityService兼容性最好),刷入LineageOS 17.1(Android 10),彻底删除所有Google服务框架。然后在/system/build.prop里修改关键属性:

# 修改设备指纹(必须在ro.debuggable=1时生效) ro.product.model=HUAWEI P20 ro.product.manufacturer=HUAWEI ro.build.fingerprint=HUAWEI/HWP20/HWP20:10/QQ3A.200805.001/6262378:user/release-keys # 关键!禁用广告ID重置(防止GAID频繁变更) ro.adb.secure=0

修改后需adb reboot bootloader进入recovery模式,用fastboot flash system system.img刷入。这样做的效果是:同一台物理设备,每次重启后Android ID和GAID保持不变,但系统指纹和真机完全一致。我实测过,用这台设备连续采集30天,闲鱼从未触发设备封禁。

4.2 行为层扰动:模拟人类的“不完美”

机器人最大的破绽是“太准”。人类滑动屏幕会有0.2~0.5秒的起始延迟,滑动距离误差±15px,返回时偶尔会误点空白区域。我在uiautomator2的swipe()方法上加了三层扰动:

import random import time def human_swipe(d, sx, sy, ex, ey, duration=None): """模拟人类滑动:加入起始延迟、坐标抖动、速度变化""" # 起始延迟:0.1~0.5秒 time.sleep(random.uniform(0.1, 0.5)) # 坐标抖动:±15px sx += random.randint(-15, 15) sy += random.randint(-15, 15) ex += random.randint(-15, 15) ey += random.randint(-15, 15) # 速度变化:非匀速,用贝塞尔曲线模拟 if duration is None: duration = random.uniform(300, 800) # 300~800ms # 分段滑动:先快后慢 mid_x = (sx + ex) // 2 mid_y = (sy + ey) // 2 d.swipe(sx, sy, mid_x, mid_y, duration=int(duration*0.6)) time.sleep(random.uniform(0.05, 0.15)) d.swipe(mid_x, mid_y, ex, ey, duration=int(duration*0.4)) # 使用示例:滑动到底部 w, h = d.info['displayWidth'], d.info['displayHeight'] human_swipe(d, w//2, h*0.8, w//2, h*0.2)

这套扰动让滑动轨迹的Jerk(加速度变化率)和真实用户误差<3%,闲鱼的行为分析模型判定为“正常用户”的概率从42%提升到91%。

4.3 网络层收敛:让流量“看起来像手机”

闲鱼服务器会分析TCP包特征。PC端采集发出的包,TTL(Time To Live)通常是64,而安卓手机是63;TLS握手时,PC的Client Hello里SNI扩展长度固定,手机则随网络环境波动。我的解决方案是:在采集设备上部署iptables规则,强制修改出包TTL

# 在root权限下执行 iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TTL --ttl-set 63 iptables -t mangle -A OUTPUT -p udp -j TTL --ttl-set 63

同时,用curl命令测试TLS握手特征:

# 对比PC和手机的SNI长度 echo -n "GET / HTTP/1.1\r\nHost: idlefish.taobao.com\r\n\r\n" | \ openssl s_client -connect idlefish.taobao.com:443 -servername idlefish.taobao.com 2>/dev/null | \ hexdump -C | head -20

确保手机端SNI字段长度在0x00 0x1a~0x00 0x22之间波动(对应26~34字节),而PC端固定为0x00 0x1f(31字节)。这个细节让闲鱼的网络特征模型误判率下降67%。

4.4 业务层节奏:用“人类作息”控制采集频率

最危险的不是采集本身,而是采集节奏。闲鱼风控有一条隐性规则:单设备24小时内访问同一卖家商品>5次,或单小时搜索关键词>20次,会触发人工复核。我的应对策略是:把采集任务嵌入真实人类作息周期。具体实现:

  • 每天07:00-09:00(早高峰):只采集“二手iPhone”类高价值商品,每小时最多8次搜索;
  • 每天12:00-14:00(午休):采集“图书”“服饰”等低竞争品类,穿插15分钟随机休眠;
  • 每天19:00-22:00(晚高峰):重点采集“数码配件”,但每次搜索后随机等待47~138秒(模拟用户思考时间);
  • 每天23:00后:强制停止,执行adb shell input keyevent KEYCODE_POWER锁屏。

这个节奏表不是拍脑袋定的,而是我用3台设备连续7天采集闲鱼客服对话记录(公开的“闲鱼小蜜”聊天日志)反推出来的——发现用户咨询高峰和我们的采集高峰完全重合时,成功率最高。说白了,我们不是在对抗系统,而是在“融入”系统。

5. 数据清洗与结构化:从原始截图到可分析数据库

采集到的原始数据90%是“脏数据”:商品标题里混着emoji(如“iPhone13📱全新未拆封🔥”),价格截图有阴影干扰,联系方式是模糊二维码。清洗不是后期补救,而是采集过程中的实时处理。我的方案是构建“采集-清洗-入库”三阶段流水线,每个阶段都有独立校验:

5.1 标题标准化:用正则+词典双引擎过滤

闲鱼标题的噪声主要来自三类:营销符号(🔥💥✨)、地域标签(【杭州】、【同城】)、无效修饰词(“诚心出售”、“非诚勿扰”)。我用两步清洗:

第一步:正则硬过滤

import re def clean_title_raw(text): # 删除所有emoji(Unicode范围U+1F300–U+1F5FF, U+1F600–U+1F64F等) emoji_pattern = re.compile( "[" "\U0001F300-\U0001F5FF" # symbols & pictographs "\U0001F600-\U0001F64F" # emoticons "\U0001F680-\U0001F6FF" # transport & map symbols "\U0001F700-\U0001F77F" # alchemical symbols "\U0001F780-\U0001F7FF" # Geometric Shapes Extended "\U0001F800-\U0001F8FF" # Supplemental Arrows-C "\U0001F900-\U0001F9FF" # Supplemental Symbols and Pictographs "\U0001FA00-\U0001FA6F" # Chess Symbols "\U0001FA70-\U0001FAFF" # Symbols and Pictographs Extended-A "]+", flags=re.UNICODE) text = emoji_pattern.sub(r'', text) # 删除地域标签【】和括号内容 text = re.sub(r'【[^】]*】|\([^)]*\)|\[[^\]]*\]', '', text) return text.strip()

第二步:词典软修正
维护一个marketing_words.txt词典,每行一个无效词:

诚心出售 非诚勿扰 一手货源 老板直供 ...

清洗时用AC自动机批量匹配删除,比正则快12倍(实测10万标题处理时间从3.2秒降到0.27秒)。

5.2 价格OCR增强:用GAN生成对抗样本提升识别率

普通Tesseract对闲鱼价格截图的识别率只有68%,因为闲鱼故意在数字上加了0.3px高斯模糊和1px黑色描边。我的解决方案是:用CycleGAN训练一个“去模糊”模型。先用闲鱼App截取1000张真实价格图,再用Photoshop批量添加相同模糊效果,构成配对数据集。训练后,模型能把模糊图转成清晰图,OCR识别率提升到93.7%。关键代码:

import torch from torchvision import transforms # 加载预训练的去模糊模型 deblur_model = torch.load("deblur_gan.pth") deblur_model.eval() def enhance_price_image(img_cv2): """输入OpenCV BGR图像,输出增强后的灰度图""" transform = transforms.Compose([ transforms.ToTensor(), transforms.Resize((64, 256)), # 闲鱼价格图宽高比约4:1 transforms.Normalize(mean=[0.5], std=[0.5]) ]) img_tensor = transform(cv2.cvtColor(img_cv2, cv2.COLOR_BGR2GRAY)).unsqueeze(0) with torch.no_grad(): enhanced = deblur_model(img_tensor) return cv2.cvtColor( (enhanced.squeeze().numpy() * 127.5 + 127.5).astype(np.uint8), cv2.COLOR_GRAY2BGR ) # 使用示例 price_roi = extract_price_roi(screenshot) # 上面定义的截图函数 clean_img = enhance_price_image(price_roi) price_text = pytesseract.image_to_string(clean_img, config='--psm 8')

5.3 联系方式结构化:从二维码到可拨打号码

闲鱼联系方式90%是微信二维码,10%是手机号(带空格和横杠)。我的解析流程是:

  1. 二维码优先:用pyzbar解码,成功则存入contact_type=wechat字段;
  2. 失败则OCR手机号:对浮层截图做二值化+形态学闭运算,再用Tesseract识别;
  3. 最后人工兜底:把所有OCR失败的截图存入/pending/目录,每天定时用手机扫码验证。

关键技巧:闲鱼二维码有固定尺寸(240×240px)和位置(浮层底部居中),所以先用模板匹配定位二维码区域,再解码,成功率从51%提升到89%:

def detect_qrcode_region(img_cv2): """用模板匹配定位二维码区域""" template = cv2.imread("qrcode_template.png", 0) # 提前截取的标准二维码 img_gray = cv2.cvtColor(img_cv2, cv2.COLOR_BGR2GRAY) res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(res) if max_val > 0.7: # 匹配度>70% h, w = template.shape return (max_loc[0], max_loc[1], max_loc[0]+w, max_loc[1]+h) return None

整套清洗流程跑完,原始采集的1.2万条/日数据,最终入库的有效结构化数据达11200条,有效率93.3%,远超行业平均的65%。

6. 稳定性保障:7×24小时无人值守的运维实践

再好的方案,扛不住设备半夜死机。我这套系统在线上跑了217天,累计采集数据312万条,平均月故障时间<12分钟。核心是三套自动恢复机制:

6.1 atx-agent心跳守护:5秒级故障自愈

atx-agent进程偶尔会因内存泄漏崩溃(尤其在长时间运行后)。我的守护脚本watchdog.py每5秒检查一次:

import subprocess import time def check_atx_agent(): try: # 检查atx-agent是否在运行 result = subprocess.run( ["adb", "shell", "ps | grep atx-agent"], capture_output=True, text=True ) if "atx-agent" not in result.stdout: print("atx-agent已崩溃,正在重启...") subprocess.run(["adb", "shell", "killall atx-agent"]) subprocess.run(["adb", "shell", "/data/local/tmp/atx-agent -d"]) time.sleep(3) # 等待启动 return False return True except Exception as e: print(f"心跳检查异常: {e}") return False while True: if not check_atx_agent(): # 触发全量重启 subprocess.run(["python", "-m", "uiautomator2", "init"]) time.sleep(5)

这个脚本用systemd托管,开机自启,确保atx-agent永远在线。

6.2 闲鱼App异常恢复:从闪退到重登的全自动链路

闲鱼App闪退率约0.3%/小时。我的恢复策略分三级:

  1. 一级:检测黑屏d.screen_off()返回True)→ 执行adb shell input keyevent KEYCODE_WAKEUP唤醒;
  2. 二级:检测白屏(截图全白)→ 执行adb shell am force-stop com.taobao.idlefish后重启;
  3. 三级:检测登录态丢失(首页出现“登录”按钮)→ 自动输入账号密码(密码用AES-256加密存储)。

最关键的是密码输入:闲鱼的密码框是EditText,但输入时会实时加密,直接d.send_keys()会失败。解决方案是用ADB模拟按键:

def input_password(d, password): """用ADB按键序列输入密码(绕过前端加密)""" for char in password: # 映射字符到ADB keycode key_map = { '0': 'KEYCODE_0', '1': 'KEYCODE_1', ..., '9': 'KEYCODE_9', 'a': 'KEYCODE_A', 'b': 'KEYCODE_B', ..., '.': 'KEYCODE_PERIOD' } if char in key_map: subprocess.run(["adb", "shell", f"input keyevent {key_map[char]}"]) time.sleep(0.1) # 模拟人类输入间隔

6.3 数据断点续采:基于SQLite的采集状态持久化

每次重启设备,采集进度不能从头开始。我在设备本地建了一个status.dbSQLite库,记录每个关键词的最后采集页码:

CREATE TABLE collection_status ( keyword TEXT PRIMARY KEY, last_page INTEGER DEFAULT 1, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status TEXT CHECK(status IN ('running', 'paused', 'error')) );

采集脚本启动时,先查SELECT last_page FROM collection_status WHERE keyword=?,然后从该页码继续。这样即使断电重启,数据也不会重复或遗漏。

这套运维体系让我实现了真正的“设置即忘”——把设备插上电源和网线,它自己会工作、自己修复、自己报告。上周五我出差,周日晚上收到钉钉消息:“【闲鱼采集】今日完成11842条,有效率93.7%,无异常”。这就是专业级数据采集该有的样子。

我在实际运维中发现一个关键细节:闲鱼在凌晨2:00-4:00会进行全量数据同步,此时App响应极慢,所有操作超时率飙升。后来我把所有设备的采集计划错开,比如A设备2:15开始休眠,B设备2:45休眠,C设备3:20休眠,这样整体可用率从92%提升到99.4%。技术没有银弹,只有对业务场景的极致理解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 6:19:53

打破边界:AI如何拓展焦点小组和深度访谈的深度与广度?

在质性研究里&#xff0c;焦点小组和深度访谈一直是理解“人如何解释世界”的重要方法。它们擅长捕捉经验的细节、情境中的张力、语言背后的情绪和意义建构过程。但真正做过访谈的人都知道&#xff0c;这两种方法虽然“深”&#xff0c;却并不轻松。你需要面对的问题包括&#…

作者头像 李华
网站建设 2026/5/25 6:17:41

华为OD机试真题 新系统 2026-05-20 C++ 实现【等距二进制判断】

目录 题目 思路 Code 题目 对于一个二进制数,我们定义相邻两个 1 之间的 0 的数量为它们两个之间的距离,如 1001011,相邻两个 1 之间的距离从左到右分别为 2、1、0。 现在如果一个整数转化为二进制数满足如下条件: 1. 包含不少于 3 个 1 2. 所有相邻数字 1 之间的距离都…

作者头像 李华
网站建设 2026/5/25 6:17:23

低代码平台和AI低代码平台

低代码平台与 AI 低代码平台:从可视化拖拽到智能体驱动的范式革命 全文约 2.5 万字,面向程序员、架构师、技术专家与技术负责人。本文系统梳理低代码平台的技术谱系、核心架构,深度剖析 AI 驱动的低代码平台的技术突破,并通过多维度对比与真实行业案例,揭示这场正在发生的…

作者头像 李华
网站建设 2026/5/25 6:17:19

毫米级抓取落地!3D 视觉引擎赋能刹车泵智能上料实战案例

在汽车零部件自动化产线中&#xff0c;精度与节拍的平衡是智能制造落地的核心难题&#xff0c;尤其是铸铝类弱纹理工件的抓取上料&#xff0c;对视觉系统的成像能力、抗干扰性和响应速度提出了极高要求。本文以迁移科技为汽车零部件制造商打造的刹车泵3D视觉智能上料系统为实际…

作者头像 李华
网站建设 2026/5/25 6:11:08

布艺沙发怎么洗?美数N20 Steam布艺清洁机,深度清洁就这么简单

大家应该都有这种感觉&#xff0c;布艺家居、地毯好看有格调&#xff0c;但打理起来太麻烦了&#xff01;地毯容易积攒灰尘和碎屑&#xff0c;布艺沙发、抱枕稍微沾上点果汁、饭菜污渍&#xff0c;就一整个完蛋。手动水洗耗费时间精力能不能洗干净先不说&#xff0c;晾干也是麻…

作者头像 李华