
做 Java 爬虫或自动化测试,定位元素是基本功。今天聊聊 jvppeteer 里那些好用到飞起的选择器 API。
做前端自动化或者写爬虫的朋友,对 Puppeteer 一定不陌生。但如果你的项目是 Java 技术栈,Node.js 那套用不上怎么办?
jvppeteer 就是答案——它是 Puppeteer 的 Java 移植版,API 风格几乎一模一样,Java 开发者无缝上手。
这篇文章,我们聚焦一个核心能力:如何通过选择器精准获取页面元素信息。
一、最基础的两兄弟:$ 和 $$
如果你用过 jQuery,这两个方法会让你倍感亲切。
获取单个元素:
ElementHandle el = page.$("#myId");
ElementHandle el = page.$(".my-class");
ElementHandle el = page.$("div.container > p:first-child");
获取多个元素:
List<ElementHandle> items = page.$$("li.item");
for (ElementHandle item : items) {
// 逐个处理
}
💡 小贴士:
$返回的是ElementHandle,它是元素的"句柄",不是元素本身。要读取内容,还需要进一步操作。
二、拿到元素后,怎么读信息?
这才是重头戏。jvppeteer 提供了三种主流方式,各有适用场景。
方式一:getProperty + jsonValue
最"底层"的方式,直接取 DOM 属性:
ElementHandle el = page.$("h1.title");
String text = (String) el.getProperty("textContent").jsonValue();
String href = (String) el.getProperty("href").jsonValue();
适合场景: 你已经拿到了 ElementHandle,需要读取它的某个属性。
方式二:page.$eval — 一行搞定
最优雅的方式,在页面上下文里执行函数:
// 取文本
String text = (String) page.$eval("h1.title", "el => el.textContent");
// 取链接
String href = (String) page.$eval("a.link", "el => el.getAttribute('href')");
// 取输入框的值
String value = (String) page.$eval("#username", "el => el.value");
适合场景: 你只关心一个元素的某个值,不想多写一行代码。
方式三:page.$$eval — 批量收割
当你需要一次性拿到所有匹配元素的数据时:
// 获取所有标题文本
List<String> titles = (List<String>) page.$$eval(
"li.item",
"els => els.map(el => el.textContent)"
);
// 获取所有链接
List<String> links = (List<String>) page.$$eval(
"a.nav-link",
"els => els.map(el => el.getAttribute('href'))"
);
适合场景: 列表页、表格数据采集,批量操作效率拉满。
三、几个高频实战场景
判断元素是否存在
ElementHandle el = page.$(".maybe-exists");
if (el != null) {
System.out.println("找到了");
} else {
System.out.println("没有这个元素");
}
获取元素位置和尺寸
做截图标注、模拟点击时经常用到:
ElementHandle el = page.$("#target");
BoundingBox box = el.boundingBox();
double x = box.getX();
double y = box.getY();
double width = box.getWidth();
double height = box.getHeight();
等待懒加载元素出现
实际项目中,元素可能异步加载。直接获取会返回 null,必须先等:
page.waitForSelector("div.lazy-loaded", new WaitForSelectorOptions()
.setVisible(true)
.setTimeout(10000) // 最多等 10 秒
);
String text = (String) page.$eval("div.lazy-loaded", "el => el.textContent");
⚠️ 踩坑提醒: 生产环境里,
waitForSelector基本是必选项,别省。
四、在 iframe 里操作元素
现代网页里 iframe 很常见(广告、嵌入内容等)。jvppeteer 处理起来也很简单:
// 切换到 iframe
Frame frame = page.frames().get(1);
// 或按 URL 等待
Frame frame = page.waitForFrame("https://example.com/iframe");
// 然后用同样的 API
String text = (String) frame.$eval(".inside-iframe", "el => el.textContent");
五、完整示例:抓取 Hacker News 热榜
把上面的知识串起来,看一个完整例子:
import com.ruiyun.jvppeteer.core.Puppeteer;
import com.ruiyun.jvppeteer.core.browser.Browser;
import com.ruiyun.jvppeteer.core.page.ElementHandle;
import com.ruiyun.jvppeteer.core.page.Page;
import com.ruiyun.jvppeteer.options.LaunchOptions;
import com.ruiyun.jvppeteer.options.LaunchOptionsBuilder;
import java.util.Arrays;
import java.util.List;
public class SelectorDemo {
public static void main(String[] args) throws Exception {
LaunchOptions options = new LaunchOptionsBuilder()
.withArgs(Arrays.asList("--no-sandbox", "--disable-setuid-sandbox"))
.withHeadless(true)
.build();
Browser browser = Puppeteer.launch(options);
Page page = browser.newPage();
page.goTo("https://news.ycombinator.com");
// 获取所有标题
List<String> titles = (List<String>) page.$$eval(
".titleline > a",
"els => els.map(el => el.textContent)"
);
titles.forEach(System.out::println);
// 获取第一个链接
String firstLink = (String) page.$eval(
".titleline > a",
"el => el.getAttribute('href')"
);
System.out.println("First link: " + firstLink);
page.close();
browser.close();
}
}
速查表
收藏这张表,忘了随时翻:
| 你想做什么 | 用这个方法 |
|---|---|
| 拿单个元素句柄 | page.$("selector") |
| 拿所有匹配元素 | page.$$("selector") |
| 读文本内容 | page.$eval("sel", "el => el.textContent") |
| 读属性值 | page.$eval("sel", "el => el.getAttribute('xxx')") |
| 批量取值 | page.$$eval("sel", "els => els.map(...)") |
| 等元素出现 | page.waitForSelector("sel") |
| 元素位置尺寸 | element.boundingBox() |
写在最后
jvppeteer 的选择器 API 设计得很直觉,基本上你会 CSS 选择器就能上手。核心就三个方法:$、$eval、$$eval,覆盖了绝大多数场景。
记住一个原则:简单场景用 $eval,复杂交互拿 ElementHandle,异步页面先 waitForSelector。
你在用 jvppeteer 时踩过什么坑?评论区聊聊👇
觉得有用?点个「在看」支持一下~
Q.E.D.


