Java爬虫:HtmlUnit爬取基金排名,AI分析数据(DeepSeek,Dify)

背景

有时候,我们需要爬取一些网页数据给AI分析和推荐时,网页一般是有防爬虫的机制。我们可以通模拟人类方式去访问,通过一个无界面的浏览器,模拟点击,然后模拟等待,然后在爬取网页内容。

HtmlUnit

HtmlUnit 是一个用 Java 编写的、无头浏览器(headless browser) 。它不显示图形界面,但可以模拟浏览器的行为来访问网页内容、解析 HTML/JavaScript、操作表单、处理 cookies 等。

目标

爬取下面天天基金的排名-指数型(3248),保存为csv格式。

思路

主要判断页面是否加载完成,加载完后再执行动作(模拟点击)。我们可以尝试点击按钮(下一页,下一页),可以发现页面没有加载完,页面都会显示一个加载标识,可以通过这个标识存不存在来判断页面是否加载完成。

有人可能说为什么F12拿到地址,直接发请求。cookie呢?还有一些请求头?url上参数呢?

代码

mavney依赖

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.htmlunit</groupId>
        <artifactId>htmlunit</artifactId>
        <version>4.11.1</version>
    </dependency>
    <dependency>
        <groupId>org.htmlunit</groupId>
        <artifactId>htmlunit-cssparser</artifactId>
        <version>4.11.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpmime</artifactId>
        <version>4.5.14</version>
    </dependency>
</dependencies>

下面代码,加载基金排名页面,点击【指数型】,等待页面加载完成,爬取内容。点击【下一页】,等待页面加载完成,爬取内容,循环直到最后一页才返回。

Java 复制代码
public static void main(String[] args) throws Exception {
    try (final WebClient webClient = new WebClient()) {
        final HtmlPage page = webClient.getPage("https://fund.eastmoney.com/data/fundranking.html");
        MyDomChangeListener listener = new MyDomChangeListener();
        HtmlElement li = page.querySelector("#types li[data-typeid='000']");
        if (li != null) {
            HtmlElement targetDiv = page.querySelector(".dbtable");
            targetDiv.addDomChangeListener(listener);
            
            //点击按钮,等待页面加载完成
            listener.await(()->{
                try {
                    HtmlPage newPage = li.click();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            HtmlElement selectLi = page.querySelector("#types .at");
            System.out.println(selectLi);

            boolean header = true;
            while (true){
                HtmlTable table = page.querySelector("#dbtable");
                System.out.println(table);

                List<List<String>> lists = parseTableToList(table);
                if(!header){
                    lists = lists.subList(1,lists.size());
                }else{
                    header = false;
                    List<String> subList = lists.get(0);
                    for (int i = 0; i < subList.size(); i++) {
                        String original = subList.get(i);
                        String replaced = original.replaceAll("\s+", "");
                        subList.set(i, replaced);
                    }
                }
                writeCsv(lists,"out.csv");
                
                //查找当前页
                HtmlElement curElement = page.querySelector("#pagebar .cur");
                // 获取下一页
                DomElement label = curElement.getNextElementSibling();
                
                if (label != null) {
                    
                    String classValue = label.getAttribute("class");
                    //判断最后一页返回
                    if(Arrays.asList(classValue.split(" ")).contains("end")){
                        break;
                    }
                    //点击按钮,等待页面加载完成
                    listener.await(()->{
                        try {
                            label.click();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
                } else {
                    throw new RuntimeException("错误");
                }

            }

        } else {
            System.out.println("未找到目标 <li> 元素");
        }
    }
}

下面代码,通过监听元素的变化实现判断页面加载完成。

Java 复制代码
static class MyDomChangeListener implements DomChangeListener {

    private final ConcurrentLinkedQueue<ArrayBlockingQueue<Long>> queue  = new ConcurrentLinkedQueue<>();

    @Override
    public void nodeAdded(DomChangeEvent domChangeEvent) {
        if(domChangeEvent.getChangedNode() instanceof HtmlTable){
            ArrayBlockingQueue<Long> peek = queue.peek();
            if(peek!=null){
                peek.clear();
                long l = System.currentTimeMillis();
                peek.offer(l);
            }
        }
    }

    @Override
    public void nodeDeleted(DomChangeEvent domChangeEvent) {

    }

    public void await(Runnable action) throws InterruptedException {
        ArrayBlockingQueue<Long> arrayQueue = new ArrayBlockingQueue<>(1);
        queue.clear();
        queue.offer(arrayQueue);
        action.run();
        arrayQueue.poll(5, TimeUnit.SECONDS);
    }

}

完整代码:

运行结果

可以看到日期4-15的指数型基金排名是爬取出来了3263支。

AI使用

通过AI去帮我选择有价值的基金,但是发现文本太长,读不完里面内容。

我们可以通过Dify解决, <math xmlns="http://www.w3.org/1998/Math/MathML"> 3263 ∗ 0.17 = 554.71 3263*0.17=554.71 </math>3263∗0.17=554.71,ds大概一次性能分析500支,要分析7次。可以先将500支选出优质的50支,7次就有 <math xmlns="http://www.w3.org/1998/Math/MathML"> 7 ∗ 50 = 350 7*50=350 </math>7∗50=350,再350中选择10的优质基金,完成任务。

结果看样子和基金排名的前10一样,可能是提示词没有写好,应该打乱循序尝试一下。

投资有风险需谨慎。

相关推荐
魔障阿Q9 分钟前
windows使用bat脚本激活conda环境
人工智能·windows·python·深度学习·conda
Wnq1007212 分钟前
巡检机器人数据处理技术的创新与实践
网络·数据库·人工智能·机器人·巡检机器人
Eric.Lee202131 分钟前
数据集-目标检测系列- 冥想 检测数据集 close_eye>> DataBall
人工智能·目标检测·计算机视觉·yolo检测·眼睛开闭状态检测识别
来自星星的坤1 小时前
Python 爬虫基础入门教程(超详细)
开发语言·爬虫·python
胡乱儿起个名1 小时前
Relay算子注册(在pytorch.py端调用)
c++·人工智能·tvm·编译器·ai编译器
嘉图明2 小时前
《从数据殖民到算法正义:破解AI垄断的伦理与技术路径》
人工智能·算法
shadowtalon2 小时前
基于CNN的猫狗图像分类系统
人工智能·深度学习·神经网络·机器学习·计算机视觉·分类·cnn
蹦蹦跳跳真可爱5892 小时前
Python----神经网络(《Deep Residual Learning for Image Recognition》论文和ResNet网络结构)
人工智能·python·深度学习·神经网络
极术社区2 小时前
【“星睿O6”评测】Armv9.2a、KLEIDIAI及vulkan加速llamacpp部署本地AI
大数据·人工智能
森哥的歌2 小时前
2024年AI发展趋势全面解析:从多模态到AGI的突破
人工智能·agi