Android使用WebView搭配Jsoup解析网页数据异常解决方案问询
问题根因
- TikTok为单页应用(SPA),页面核心内容为异步加载渲染,
WebViewClient.onPageFinished仅代表页面主资源加载完成,此时异步请求的用户数据还未渲染到DOM上,导致你拿到的HTML不存在目标节点。 - 你使用的
div[class='jsx-2839133444 main user-page']选择器语法错误,该写法要求节点class属性完全匹配括号内的字符串,若节点存在其他附加class则无法匹配;且jsx开头的class为前端编译生成的动态哈希值,版本迭代后会变化,稳定性极低。 - 直接拼接
innerHTML到JS字符串中时,若HTML内容存在单引号会截断字符串,导致传入Java层的HTML内容不完整。 - 未对WebView做反爬适配,TikTok识别到异常请求会返回残缺页面或拦截内容。
修复方案
1. 调整HTML获取时机与转义逻辑
延迟执行JS获取逻辑,预留足够的页面渲染时间,同时对HTML内容做转义处理避免引号截断问题:
@Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 延迟3秒执行,可根据实际加载情况调整时长 view.postDelayed(() -> { // 用encodeURIComponent转义HTML内容,避免特殊字符截断JS字符串 browser.loadUrl("javascript:window.JSBridge.showHTML(encodeURIComponent(document.documentElement.outerHTML));"); progressDialog.dismiss(); }, 3000); }
在showHTML中对转义后的内容解码:
@android.webkit.JavascriptInterface public void showHTML(String encodedHtml) { try { final String htmlContent = java.net.URLDecoder.decode(encodedHtml, java.nio.charset.StandardCharsets.UTF_8.name()); uiHandler.post(() -> { org.jsoup.nodes.Document doc = org.jsoup.Jsoup.parse(htmlContent); // 调整选择器写法,改用类选择器标准语法 org.jsoup.select.Elements elements = doc.select("div.jsx-2839133444.main.user-page"); // 可先打印全量HTML确认是否包含目标节点,排查是内容未加载还是选择器错误 android.util.Log.d("全量HTML", htmlContent); android.util.Log.e("匹配节点", String.valueOf(elements)); }); } catch (Exception e) { e.printStackTrace(); } }
2. 优化WebView配置,规避反爬拦截
初始化WebView时添加以下配置,伪装为正常移动端浏览器:
android.webkit.WebSettings webSettings = browser.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setDomStorageEnabled(true); // 替换为真实移动端Chrome UA webSettings.setUserAgentString("Mozilla/5.0 (Linux; Android 13; SM-G998B Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36"); webSettings.setAllowContentAccess(true); webSettings.setAllowFileAccess(true);
3. 替换更稳定的选择器
不要依赖动态生成的jsx类名,优先使用TikTok页面上固定的属性选择器,例如很多节点带有固定的data-testid属性,例如用户页的外层节点若存在data-testid="user-profile-container",可直接使用doc.select("div[data-testid='user-profile-container']")匹配,稳定性远高于动态类名。
4. (可选)直接在JS层筛选目标内容
无需传递全量HTML到Java层再解析,可直接在JS中筛选目标节点后传递,提升运行效率:
browser.loadUrl("javascript:window.JSBridge.showHTML(encodeURIComponent(document.querySelector('div.main.user-page')?.outerHTML || ''));");
内容的提问来源于stack exchange,提问作者leonscot
相关产品推荐
相关产品推荐

