【java爬虫】使用selenium获取某交易所公司半年报数据

news2024/11/19 5:52:25

引言

上市公司的财报数据一般都会进行公开,我们可以在某交易所的官方网站上查看这些数据,由于数据很多,如果只是手动收集的话可能会比较耗时耗力,我们可以采用爬虫的方法进行数据的获取。

本文就介绍采用selenium框架进行公司财报数据获取的方法,网页的地址是

上市公司经营业绩概览 | 上海证券交易所

首先来看一下运行的效果

编程环境搭建

本文采用springboot进行开发,首先来看一下pom.xml的内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.12</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>FinanceSpider</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>FinanceSpider</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>


		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.0</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.26</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<!-- 爬虫相关的包 -->
		<dependency>
			<groupId>com.squareup.okhttp3</groupId>
			<artifactId>okhttp</artifactId>
			<version>3.10.0</version>
		</dependency>


		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.11.3</version>
		</dependency>

		<dependency>
			<!-- fastjson -->
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>


		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-core</artifactId>
			<version>5.6.5</version>
		</dependency>


		<dependency>
			<groupId>net.lightbody.bmp</groupId>
			<artifactId>browsermob-core</artifactId>
			<version>2.1.5</version>
		</dependency>

		<dependency>
			<groupId>net.lightbody.bmp</groupId>
			<artifactId>browsermob-legacy</artifactId>
			<version>2.1.5</version>
		</dependency>

		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>4.1.1</version>
			<!--            <version>3.141.59</version>-->
		</dependency>

		<dependency>
			<groupId>io.github.bonigarcia</groupId>
			<artifactId>webdrivermanager</artifactId>
			<version>5.0.3</version>
		</dependency>

		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>31.0.1-jre</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<version>2.4.3</version>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.22.2</version>
				<configuration>
					<skipTests>true</skipTests>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

数据库方面采用的是mysql,下面是建表语句

use finance_db;

/* 半年报信息表 */
drop table if exists t_report;
create table t_report (
    u_id BIGINT (20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '优惠券id',
    company VARCHAR (50) NOT NULL COMMENT '公司名称',
    stock VARCHAR (20) NOT NULL COMMENT '股票代码',
    income BIGINT (20) NOT NULL COMMENT '营业收入',
    profit1 BIGINT (20) NOT NULL COMMENT '净利润',
    profit2 BIGINT (20) NOT NULL COMMENT '扣非净利润',
    cashflow BIGINT (20) NOT NULL COMMENT '经营现金流',
    rate1 DOUBLE NOT NULL COMMENT '净资产收益率',
    rate2 DOUBLE NOT NULL COMMENT '基本每股收益',
    rate3 DOUBLE NOT NULL COMMENT '资产负债率'
) ENGINE=InnoDB COMMENT '半年报信息表';

对应的mapper类和配置文件如下所示

@Mapper
public interface ReportMapper {

    // 清空表
    public void clearAll();

    // 插入一条数据
    public void insertOneItem(@Param("item")ReportEntity entity);

}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ReportMapper">
    <delete id="clearAll">
        delete from t_report where 1=1
    </delete>

    <insert id="insertOneItem" parameterType="ReportEntity">
        insert into t_report
        (company, stock, income, profit1, profit2, cashflow, rate1, rate2, rate3)
        values
        (#{item.company}, #{item.stock}, #{item.income}, #{item.profit1},
         #{item.profit2}, #{item.cashflow}, #{item.rate1}, #{item.rate2}, #{item.rate3})
    </insert>

</mapper>

除此之外,我们还需要编写一个和数据库表对应的实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReportEntity {

    // 公司名称
    private String Company;
    // 股票代码
    private String stock;
    // 营业收入
    private long income;
    // 净利润
    private long profit1;
    // 扣非净利润
    private long profit2;
    // 经营现金流
    private long cashflow;
    // 净资产收益率
    private double rate1;
    // 基本每股收益
    private double rate2;
    // 资产负债率
    private double rate3;

}

爬虫程序编写

环境搭好后接下来就是最重要的爬虫程序编写的部分了,本文采用的是chrome浏览器,使用selenium框架的时候,需要采用和浏览器版本对应的驱动程序,下面是我的浏览器版本

我下载了对应版本的驱动程序,118版本的驱动可以在这个网址下载

https://googlechromelabs.github.io/chrome-for-testing/#stable

如果你的chrome版本较低,驱动程序应该很好找,直接百度就可以了。

下面来介绍具体的爬虫程序编写逻辑。

实际上某交易所的数据还是比较好获取的,就是有一点需要注意一下,网页都是先于数据渲染的,selenium在网页渲染好后就会开始获取元素信息,这时候可能就会获取不到数据,解决办法就是判断当前有没有获取到数据,如果没有获取到数据就等待一会然后继续获取,直到获取到数据位置,具体的代码如下

@Slf4j
@Service
public class ReportServiceImpl implements ReportService {

    private final String DRIVER_PATH = "E:/视频/电商爬虫/驱动/chromedriver-118.exe";
    private final String START_URL = "http://www.sse.com.cn/disclosure/listedinfo/listedcompanies/";

    @Autowired
    private ReportMapper reportMapper;

    @Override
    public void getReportInfo() {
        reportMapper.clearAll();
        System.setProperty("webdriver.chrome.driver", DRIVER_PATH);
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--remote-allow-origins=*");
        WebDriver driver = new ChromeDriver(options);
        // 设置最长等待时间
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        driver.get(START_URL);
        while(true) {
            WebElement element = driver.findElement(By.className("list-group-flush"));
            WebElement ul = element.findElement(By.tagName("ul"));
            List<WebElement> liList = ul.findElements(By.tagName("li"));
            String firstname = null;
            String cmpname = null;
            for (int i = 0; i < liList.size(); i++) {
                if (i == 0) {
                    firstname = driver.findElement(By.className("js_one_title")).getText();
                }
                // 点击进入新的页面
                liList.get(i).findElement(By.tagName("div")).click();
                List<String> handleList = new ArrayList<>(driver.getWindowHandles());
                driver.switchTo().window(handleList.get(1));

                // 获取新的数据
                WebElement title_lev1 = null;
                title_lev1 = driver.findElement(By.className("title_lev1")).findElement(By.tagName("span"));
                while(title_lev1.getText().split(" ").length == 1) {
                    log.info("等待公司名称加载");
                    sleep(1000);
                    title_lev1 = driver.findElement(By.className("title_lev1")).findElement(By.tagName("span"));
                }
                String tmpstr = title_lev1.getText();
                // System.out.println(tmpstr);
                String title = tmpstr.split(" ")[0];
                String stock = tmpstr.split(" ")[1];

                List<WebElement> table_ele = driver.findElement(By.className("table-hover")).findElements(By.tagName("tr"));
                while(table_ele.get(0).findElements(By.tagName("td")).get(1).getText().equals("-")) {
                    log.info("等待详细信息加载");
                    sleep(2000);
                    table_ele = driver.findElement(By.className("table-hover")).findElements(By.tagName("tr"));
                }
                // 营业收入
                long income = parseLongStr(table_ele.get(0).findElements(By.tagName("td")).get(1).getText());
                // 净利润
                long profit1 = parseLongStr(table_ele.get(0).findElements(By.tagName("td")).get(3).getText());
                // 扣非净利润
                long profit2 = parseLongStr(table_ele.get(2).findElements(By.tagName("td")).get(1).getText());
                // 经营现金流
                long cashflow = parseLongStr(table_ele.get(2).findElements(By.tagName("td")).get(3).getText());
                // 净资产收益率
                double rate1 = parseDoubleStr(table_ele.get(4).findElements(By.tagName("td")).get(1).getText());
                // 基本每股收益
                double rate2 = parseDoubleStr(table_ele.get(4).findElements(By.tagName("td")).get(3).getText());
                // 资产负债率
                double rate3 = parseDoubleStr(table_ele.get(6).findElements(By.tagName("td")).get(1).getText());

                ReportEntity entity = new ReportEntity(title, stock, income, profit1, profit2, cashflow, rate1, rate2, rate3);
                reportMapper.insertOneItem(entity);
                log.info("获取信息=>" + JSON.toJSONString(entity));

                sleep(1000);

                // 关闭新的页面
                closeWindow(driver);
            }
            // 如果有下一页就点击下一页
            if (check(driver, By.className("noNext"))) {
                log.info("已经么有下一页啦");
                break;
            }
            WebElement element1 = driver.findElement(By.className("pagination-box")).findElement(By.className("next"));
            element1.click();
            log.info("点击进入下一页");
            // 等待标签出现变化
            sleep(1000);
            cmpname = driver.findElement(By.className("js_one_title")).getText();
            while(cmpname.equals(firstname)) {
                log.info("继续等待页面加载");
                sleep(1000);
                cmpname = driver.findElement(By.className("js_one_title")).getText();
            }
        }

    }

    // 等待一定时间
    public void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 判断某个元素是否存在
    public boolean check(WebDriver driver, By selector) {
        try {
            driver.findElement(selector);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public double parseDoubleStr(String doublestr) {
        if (doublestr.equals("-")) {
            return 0.0;
        } else {
            return Double.parseDouble(doublestr.replaceAll(",", ""));
        }
    }

    public long parseLongStr(String longstr) {
        // System.out.println("longstr=" + longstr);
        int flag = 1;
        if (longstr.contains("-1")) {
            flag = -1;
        }
        longstr = longstr.replaceAll("-", "");
        longstr = longstr.replaceAll(",", "");
        // 如果有小数点
        if (longstr.contains(".")) {
            longstr = longstr.replaceAll("\\.", "");
            return Long.parseLong(longstr) * 100 * flag;
        } else { // 没有小数点
            return Long.parseLong(longstr) * 10000 * flag;
        }
    }

    // 关闭当前窗口
    public void closeWindow(WebDriver driver) {
        // 获取所有句柄的集合
        List<String> winHandles = new ArrayList<>(driver.getWindowHandles());
        driver.switchTo().window((String) winHandles.get(1));
        driver.close();
        driver.switchTo().window((String) winHandles.get(0));
    }
}

下面是controller层的代码,用于启动爬虫程序,需要开启一个线程进行执行,因为程序运行的时间会很久

@Controller
public class BootController {

    @Autowired
    private ReportService reportService;

    @RequestMapping("start")
    @ResponseBody
    public String bootstart() {
        new Thread(()->{
            reportService.getReportInfo();
        }).start();
        return "success";
    }

}

运行程序后就可以进行数据获取了,下面是获取到的一部分数据

总结

使用爬虫获取数据还是挺快的,也挺方便的。

不过还是要提醒一句,本文分享的内容仅作为学习交流使用,请勿用于任何商业用途!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1120694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器通过scp传送数据,提示验证失败的问题

场景提示如下 当使用scp传送数据时提示这个 分析: 目标服务器云盘被格式化过, 用之前的密钥校验新的系统时发现不匹配了&#xff0c;拒绝登录&#xff01; 解决方法 需要把旧密钥换成新的密钥 先看源服务器已经有的密钥ssh-keygen -l -f ~/.ssh/known_hosts然后重新生成一下…

简道云出现问题及解决方法1

1、在制作仪表盘设计的时候没有统计表链接&#xff0c;点开统计表没有显示。 根据老师的手把手教导还是会出现错误&#xff0c;上网查询再加上多次看录播回放&#xff0c;私以为是不同网页版本的问题&#xff0c;包括一些应用的排版同样不一样。这里的解决办法是把刚才做的表盘…

MySQL binlog集市的项目小结

这是学习笔记的第 2478篇文章 MySQL binlog集市的事情我们做了有一段时间了&#xff0c;最开始的初衷是异常操作的数据恢复&#xff0c;主要的痛点是如果发生了业务误操作&#xff0c;需要紧急恢复数据的时候&#xff0c;通常这些误操作是对于字典配置数据的变更&#xff0c;而…

有哪些适用于 Windows 的PDF 阅读器?免费 PDF 阅读器清单

探索适用于 Windows 10 和 11 的最佳 PDF 阅读器 适用于 Windows 10 和 Windows 11 的最佳 PDF 阅读器让您可以在台式计算机上查看和共享文档。 最好的PDF 编辑器和免费的 PDF 编辑器配备了先进的工具&#xff0c;可以跨不同的操作系统工作。但是&#xff0c;当您只需要查看和…

Docker搭建Plex流媒体服务并播放自己本地视频

Docker搭建Plex流媒体服务 安装Docker创建存储配置文件的目录创建Plex容器配置Plex设置媒体库访问Plex 1 介绍 Plex是一个流媒体服务器&#xff0c;可以轻松地将你的媒体文件库&#xff08;如电影、电视节目和音乐&#xff09;通过网络流式传输到各种设备上。 Plex 是一套媒体…

信号量、互斥锁、计数信号量

大家好&#xff0c;我叫徐锦桐&#xff0c;个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识&#xff0c;还有日常折腾的经验&#xff0c;欢迎大家来访。 信号量(semaphores)一个多进程共享的非负整型全局变量。信号量常用于多进程的进程同步。 介绍 …

零基础学python:错误与异常

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 语法错误 异常&#xff1a;大多数的异常都不会被程序处理&#xff0c;都以错误信息的形式展现在这里 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ pyth…

【STM32】GPIO控制LED(寄存器版)

在开始之前记得先准备好环境&#xff1a; STM32F103核心板下载教程.pdf 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com) 一、STM32的GPIO模块数据手册详解 每个GPIO端口对应16个引脚&#xff0c;例GPIOA&#xff08;PA0~PA15&#xff09;内核cpu就可以通过APB2总线对寄存器…

电路电子技术3 电位的计算受控源在电路分析中的作用

1.计算电压 思路&#xff1a;注意到这个串联电路&#xff0c;有两个电压源&#xff0c;所以我们首先可以将两个电压源串联等效为一个电压源。 即&#xff1a; 故可得答案选B. 2.计算点位 思路&#xff1a;题目说明了B点开路&#xff0c;所以没有流过电阻R的电流&#xff0c;…

光学配件简介

光学配件简介 延长管介绍 延长管&#xff0c;是加在镜头和相机之间的一根通心的环状管&#xff0c;但里面没有任何光学部件&#xff0c;也没有放大作用。它不会改变光圈值&#xff0c;但因为延长了镜头&#xff0c;光线会减弱。延长管可以使镜头的对焦距离缩短&#xff0c;也就…

Uniapp 酷炫钱包页面模板 直接引用

使用教程 直接引用Vue页面 即可 <template><view><TCqianbao></TCqianbao></view> </template> <script>import TCqianbao from /uni_modules/TC-qianbao/pages/index.vueexport default {components:{TCqianbao},} </script&…

【斗破年番】彩鳞送老公新挂,薰儿霸气回归,萧炎招揽古河,邀请美杜莎战魂殿

【侵权联系删除】【文/郑尔巴金】 斗破苍穹年番动画已经更新了&#xff0c;萧炎与美杜莎女王一起回娘家&#xff0c;并且也与小医仙制定了同盟计划&#xff0c;准备对金雁宗与慕兰谷斩草除根。从官方公布的第69集预告来看&#xff0c;慕兰三老与雁落天已经陨落&#xff0c;美杜…

Warning: [antd: Switch] `value` is not a valid prop, do you mean `checked`?

解决方案&#xff1a; <Form.Item label"更多设置" name"moreSetting" valuePropName"checked"><Switch onChange{handleMoreSetting} /></Form.Item> 参考链接&#xff1a; https://chat.xutongbao.top/

医药保健品经营小程序商城的作用是什么

互联网经济下&#xff0c;线上线下商家难以生存&#xff0c;自营平台成为各行业商家的选择&#xff0c;摆脱平台限制及线下门店地域的限制&#xff0c;多渠道线上获客引流转化营销、留存复购裂变等&#xff0c;更利于直接触达消费者&#xff0c;无其它商家夺流及坑位费、流量费…

最详细STM32,cubeMX 定时器

这篇文章将详细介绍 STM32,cubeMX 定时器的配置和使用。 文章目录 前言一、定时器基础知识二、cubeMX 配置三、定时时长四、自动生成代码讲解五、实验程序总结 前言 实验开发板&#xff1a;STM32F103C8T6。所需软件&#xff1a;keil5 &#xff0c; cubeMX 。实验目的&#xff…

探索未来的视觉革命:卷积神经网络的崭新时代(二)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

自主武器系统对伦理和法律的挑战

【第十届北京香山论坛是中国军事科学学会、中国国际战略学会联合主办的论坛&#xff0c;将于2023年10月29日至31日在北京举行。本次论坛的主题是“共同安全、持久和平”&#xff0c;聚焦全球安全倡议重点合作方向&#xff0c;为各方共议安全难题、求解安全之策、促进安全合作提…

Leetcode1971. 寻找图中是否存在路径

Every day a Leetcode 题目来源&#xff1a;1971. 寻找图中是否存在路径 解法1&#xff1a;并查集 并查集介绍&#xff1a;并查集详解 代码&#xff1a; /** lc appleetcode.cn id1971 langcpp** [1971] 寻找图中是否存在路径*/// lc codestart class UnionFind {vector&…

Node-RED系列教程-27node-red操作邮件节点

提前注册好一个163邮箱: 安装以下节点: 演示发送邮件的功能

ESXI6.5安装教程

设置从IPMI Virtual Disk 3000启动&#xff0c;出现如下界面&#xff1a; 默认选择第一项&#xff0c;回车安装 安装程序正在检测服务器硬件信息&#xff0c;如果不满足系统安装条件会跳出错误提示。 检测完成之后会出现下面界面 回车 按F11 这里列出了服务器硬盘信息&#…