使用SpringBoot + selenium-java 做爬虫

本文介绍Selenium工具在Java爬虫中的应用,涵盖Selenium简介、selenium-java使用、chromedriver及phantomjsdriver示例。探讨验证码处理、翻页、滚动条操作、iframe元素获取等常见问题解决方案,并分享基于SpringBoot+Mybatis+Tkmapper+Sqllite+selenium-java的爬虫项目。

题记 最近发现了springboot本身就集成了selenium-java ,就拿来研究了一下,分布式爬虫是很大的一个架构,也比较复杂,没做研究,还是对爬虫的客户端实现有点兴趣,就想着用java做一个小爬虫客户端,其实selenium是做测试的,不过来拿来做小爬虫还是不错的,能真实模拟用户,不用担心异步加载爬取不到,还有一些元素获取不方便,更值得一提的是selenium-java 提供的元素获取的方式真的很方便,根据className、tagName、id 都有,爬取网站的时候会遇到一些验证码,这时候可以使用tess4j做简单的验证码识别,实在不行就等待人工输入也行。下面会提到爬虫验证码处理、frame内容爬取、翻页处理、滚动条等。

1、 Selenium 简介

Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。

2、selenium-java

selenium-java 是 selenium的java 版,根据不同driver,可以驱动不同的浏览区,比如 selenium-chrome-driver、selenium-edge-driver、selenium-firefox-driver、selenium-ie-driver、selenium-opera-driver、phantomjsdriver等等,我用了其中的chromedriver 和 phantomjsdriver,这个能完全模拟真实用户操作,不错的测试框架。

3、 chromedriver 示例

3.1、 下载

以下是chromedriver对应的chrome版本:

驱动对应版本号
2.37v64-66
2.36v63-65
2.35v62-64
2.34v61-63
2.33v60-62
2.32v59-61
2.31v58-60
2.30v58-60
2.29v56-58

驱动的下载地址如下:
http://chromedriver.storage.googleapis.com/index.html
注意:64位向下兼容,直接下载32位的就可以啦,亲测可用。

3.2、 使用

ChromeOptions options = new ChromeOptions();
// 设置允许弹框
options.addArguments("disable-infobars","disable-web-security");
// 设置无gui 开发时还是不要加,可以看到浏览器效果
options.addArguments("--headless");
String driverPath =  "D:\\crawler-plugin\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
RemoteWebDriver driver=  new ChromeDriver(options);
driver.get("http://www.baidu.com");
System.out.println(driver.findElement(By.tagName("body")).getText());

4、 phantomjsdriver示例

4.1、 下载

下载地址 http://phantomjs.org/download.html

4.2、 使用

String driverPath = "D:\\crawler-plugin\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe";
System.setProperty("phantomjs.binary.path", driverPath);//设置PhantomJs访问路径
DesiredCapabilities desiredCapabilities = DesiredCapabilities.phantomjs();
//设置参数
desiredCapabilities.setCapability("phantomjs.page.settings.userAgent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0");
desiredCapabilities.setCapability("phantomjs.page.customHeaders.User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:50.0) Gecko/20100101  Firefox/50.0");
RemoteWebDriver driver =  new PhantomJSDriver(desiredCapabilities);
driver.get("http://www.baidu.com");
System.out.println(driver.findElement(By.tagName("body")).getText());

5、 爬取页面常遇到的问题

5.1、 验证码

网站有时候需要登录,登录时候遇到验证码就非常棘手,tess4j能做简单的验证码识别,复杂的就别想了。。
maven 依赖

 <dependency>
	<groupId>net.sourceforge.tess4j</groupId>
		<artifactId>tess4j</artifactId>
		<version>3.4.0</version>
		<exclusions>
			<exclusion>
				<groupId>com.sun.jna</groupId>
				<artifactId>jna</artifactId>
			</exclusion>
		</exclusions>
	</dependency>

tess4j 配置项

##tess4j config
tess4j.language=chi_sim
tess4j.language.path=D:\\crawler-plugin\\tessdata
tess4j.data.path=D:\\crawler-plugin\\

读取配置文件

@Configuration
public class Tess4jConfig {
    @Value("${tess4j.data.path}")
    @Setter
    @Getter
    private String tess4jDataPath ;

    @Value("${tess4j.language.path}")
    @Setter
    @Getter
    private String tess4jLanguagePath ;

    @Value("${tess4j.language}")
    @Setter
    @Getter
    private String tess4jLanguage ;
}

工具类


import com.cdchen.crawler.config.Tess4jConfig;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.util.LoggHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

@Slf4j
public class Tess4jUtil {

    private static final Logger logger                   = LoggerFactory.getLogger(new LoggHelper().toString());
    static final             double MINIMUM_DESKEW_THRESHOLD = 0.05d;
    private static ITesseract instance;

    private static  String datapath = "D:\\crawler-plugin\\";
    private static  String testResourcesLanguagePath = "D:\\crawler-plugin\\tessdata";
    private static  String language = "chi_sim";

    private  static ITesseract getInstance(){

        Tess4jConfig config = SpringBeanUtil.getBean(Tess4jConfig.class);
        if(config != null){
            datapath = config.getTess4jDataPath();
            language = config.getTess4jLanguage();
            testResourcesLanguagePath = config.getTess4jLanguagePath();
        }

        if(datapath == null){
            log.error("必须在properties配置tess4jdata.path,否则验证码无法识别");
            return null;
        }
        if(testResourcesLanguagePath == null){
            log.error("必须在properties配置tess4jlanguage.path,否则验证码无法识别");
            return null;
        }
        if(language == null){
            log.error("必须在properties配置tess4jlanguage,否则验证码无法识别");
            return null;
        }

        if(instance == null){
            instance = new Tesseract();
            instance.setDatapath(new File(datapath).getPath());
            //set language
            instance.setDatapath(testResourcesLanguagePath);
            instance.setLanguage(language);
        }
        return instance;
    }

    public static String  doOcr(File file) throws  Exception{
        String result = getInstance().doOCR(file);
        return result;
    }

}

5.2、 翻页

翻页相对来就很简单了,有很多种解决方法,举例2种
1 找到翻页url规律,替换对应的页码
2 找到翻页按钮,模拟点击
我用的第二种,一下代码为爬取qiushi百科时候的翻页代码,供参考:

private void jumpPageNum(int pageNum){
        if(WebElementUtils.doesWebElementExist(driver,By.className("pagination"))){
            WebElement pagination = driver.findElement(By.className("pagination"));
            String currentText = pagination.findElement(By.className("current")).getText();
            int currentPageNum = Integer.parseInt(currentText);
            while (currentPageNum != pageNum){
                List<WebElement> pageNums = pagination.findElements(By.className("page-numbers"));
                for (int i = 0; i < pageNums.size(); i++) {
                    String pageNumText = pageNums.get(i).getText();
                    if(pageNumText.equals(pageNum+"")){
                        pageNums.get(i).click();
                        scrollBar.toPageEnd();
                        break;
                    }else{
                       if(i == (pageNums.size()-1)){
                           pageNums.get(i).click();
                       }
                    }
                }
                pagination = driver.findElement(By.className("pagination"));
                currentText = pagination.findElement(By.className("current")).getText();
                currentPageNum = Integer.parseInt(currentText);
            }
        }
    }

5.3、 滚动条

滚动条就比较麻烦了,因为Driver没有对应的api操作滚动条(或许我没有找到。。),我用了曲线救国的方法去实现,而且用同样的思路可以解决很多类似的问题。思路就是:使用JavaScript去操作滚动条
实现步骤是:

  1. 向页面body内添加script tag,并把想执行的js function 插入进去
  2. 使用driver的js执行引擎去执行js实现效果

贴出来我写的工具类:


import com.cdchen.crawler.util.SleepUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.remote.RemoteWebDriver;

    /**
     *
     * @description: tb
     *
     * @author: cdchen
     *
     * @create: 2019-04-30 17:08
     **/
    @Data
    @Slf4j
    public class ScrollBar {

        RemoteWebDriver driver = null;


        private static String getScrollTopJs = "function getScrollTop(){"
                                               + "  var scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;"
                                               + "  if(document.body){"
                                               + "    bodyScrollTop = document.body.scrollTop;"
                                               + "  }"
                                               + "  if(document.documentElement){"
                                               + "    documentScrollTop = document.documentElement.scrollTop;"
                                               + "  }"
                                               + "  scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;"
                                               + "  return scrollTop;"
                                               + "};";

        private static String getScrollHeightJs = "function getScrollHeight(){"
                                                  + "  var scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;"
                                                  + "  if(document.body){"
                                                  + "    bodyScrollHeight = document.body.scrollHeight;"
                                                  + "  }"
                                                  + "  if(document.documentElement){"
                                                  + "    documentScrollHeight = document.documentElement.scrollHeight;"
                                                  + "  }"
                                                  + "  scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;"
                                                  + "  return scrollHeight;"
                                                  + "};";

        private static String getWindowHeightJs = "function getWindowHeight(){"
                                                  + "  var windowHeight = 0;"
                                                  + "  if(document.compatMode == \"CSS1Compat\"){"
                                                  + "    windowHeight = document.documentElement.clientHeight;"
                                                  + "  }else{"
                                                  + "    windowHeight = document.body.clientHeight;"
                                                  + "  }"
                                                  + "  return windowHeight;"
                                                  + "};";

        private static String scroollIsOverJs = "function scroollIsOver(){"
                                                + "  if(getScrollTop() + getWindowHeight() == getScrollHeight()){"
                                                + "    return true;"
                                                + "  }else{"
                                                + "    return false;"
                                                + "  }"
                                                + "};";

        private static String insertScriptJs = "var body = document.getElementsByTagName('body')[0];"
                                               + "var newScript = document.createElement('script');"
                                               + "newScript.type = 'text/javascript';"
                                               + "newScript.innerHTML = '"+getScrollTopJs+getScrollHeightJs+getWindowHeightJs+scroollIsOverJs+"';"
                                               + "body.appendChild(newScript);";
        public ScrollBar(RemoteWebDriver dr){
            driver = dr;
        }
        public  void toPageEnd() {
            getDriver().executeScript(insertScriptJs);
            int start = 0;
            boolean scroollIsOver = false;
            while (!scroollIsOver){
                getDriver().executeScript("window.scrollTo("+start+","+(start+500)+")");
                Boolean res = (Boolean)getDriver().executeScript("return  scroollIsOver();");
                if(res != null && res){
                    scroollIsOver = true;
                }
                start = start+500;
                SleepUtil.sleep(1000);
            }
        }

    }

5.4、 iframe 内元素无法获取

有时候页面内置了iframe,想要获取iframe的元素就获取不了,这是就需要把driver切换到iframe内,如下代码:

WebElement iframe = driver.findElement(By.tagName("iframe"));
driver.switchTo().frame(iframe);
// 然后再去获取元素或者其他操作、操作完需切换回来
driver.switchTo().parentFrame();

5.5、 标签页切换

有时候用chromedriver 时候需要开启多个标签页如何操作?

List<String> tabs = new ArrayList<String>(driver.getWindowHandles());
// 切换到第一个标签
driver.switchTo().window(tabs.get(0));
// 切换到第 **n**个标签
driver.switchTo().window(tabs.get(n));

6 数据库问题

爬虫客户短 应该尽量的小,但是有时候需要记录一些 爬取进度信息,不得不使用数据库或者文件之类的记录,所以我就集成了一个sqllite 作为持久化数据库,客户端重启也不用担心丢数据啦。框架 使用的是 SpringBoot + Mybatis+Tkmapper+Sqllite+selenium-java ,使用起来也是相当方便,项目放在了gitee上,地址:https://gitee.com/passionday/crawler

<think>好的,我需要帮助用户了解如何用Java实现数据爬取,并在SpringBoot和Vue前后端分离的项目中展示数据。首先,我得理清楚整个流程,分步骤说明。 首先,数据爬取部分。用户提到了Java,可能常用的爬虫库有Jsoup或者Selenium。Jsoup适合处理静态页面,而Selenium可以处理动态加载的内容。根据引用[2]中提到的,用户之前可能接触过使用Java+Selenium+GoogleWebDriver来模拟网页操作,比如爬取图片。所以这里可能需要介绍这两种方式的选择。 接下来是SpringBoot后端的搭建。需要设计API接口,让前端能请求数据。引用[1]提到使用SpringBoot+MyBatis+MySQL,并且处理海量数据时分表存储。这里可能需要说明如何设计数据库表结构,以及如何通过MyBatis操作数据库,存储爬取的数据。同时,引用[3]和[4]都提到了前后端分离中使用Spring Security进行用户认证,但用户的问题可能不需要认证部分,但需要设计Restful API。 然后是Vue前端的数据展示。引用[4]和[5]提到Vue.js用于前端,通过Axios进行异步请求,获取后端数据并展示。需要说明如何在Vue组件中调用API,使用组件化开发,可能结合Element-UI或ECharts进行数据可视化。 整个流程需要分步骤:爬虫实现、后端接口开发、前端展示。每个部分需要代码示例,比如Java爬虫的代码片段,SpringBoot的Controller和Service层,Vue的组件和Axios调用。 可能还需要考虑跨域问题,因为前后端分离项目通常前端和后端不同端口,需要配置CORS。引用[3]中提到了使用Spring Security处理登录,但这里可能需要在后端配置CORS过滤器,或者使用@CrossOrigin注解。 另外,数据存储方面,可能需要定时任务定期爬取数据,比如使用Spring的@Scheduled注解,或者Quartz框架。引用[1]提到分表操作,如果数据量大,可能需要分库分表策略,但用户的问题可能不需要这么复杂,简单存储即可。 最后,数据展示可能需要分页处理,前端表格或图表展示。需要说明如何将后端分页数据传给前端,并使用Vue渲染。 需要确保各部分代码示例正确,比如Java爬虫使用Jsoup或SeleniumSpringBoot的RestController,Vue的Axios调用和组件渲染。同时,注意引用相关的引用来源,比如引用[1]、[2]、[4]、[5]中提到的技术点。</think>### Java实现数据爬取与SpringBoot+Vue展示的实现方法 #### 一、数据爬取实现(Java) 1. **技术选型** - **静态页面**:使用`Jsoup`解析HTML,适合基础数据抓取。 - **动态页面**:使用`Selenium+WebDriver`模拟浏览器操作,处理JavaScript渲染内容[^2]。 2. **代码示例(Jsoup)** ```java // 爬取新闻标题示例 Document doc = Jsoup.connect("https://news.example.com").get(); Elements titles = doc.select(".news-title"); List<String> dataList = titles.stream().map(Element::text).collect(Collectors.toList()); ``` 3. **数据存储** - 使用SpringBoot集成MyBatis或JPA,将数据存入MySQL。 - 表结构设计示例: ```sql CREATE TABLE news ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255), content TEXT, source VARCHAR(100) ); ``` #### 二、SpringBoot后端开发 1. **API接口设计** - 使用`@RestController`定义数据接口: ```java @GetMapping("/api/news") public ResponseEntity<List<News>> getNewsList( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size ) { Pageable pageable = PageRequest.of(page-1, size); return ResponseEntity.ok(newsService.getNews(pageable)); } ``` 2. **跨域配置** - 添加CORS配置类或使用注解: ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8080") // Vue前端地址 .allowedMethods("*"); } } ``` #### 三、Vue前端数据展示 1. **Axios异步请求** - 安装Axios并封装请求: ```javascript // api.js import axios from 'axios'; const service = axios.create({ baseURL: 'http://localhost:8081' }); export const getNews = (page, size) => { return service.get('/api/news', { params: { page, size } }); } ``` 2. **数据绑定与展示** - Vue组件示例(使用Element-UI表格): ```vue <template> <el-table :data="newsList"> <el-table-column prop="title" label="标题"></el-table-column> <el-table-column prop="source" label="来源"></el-table-column> </el-table> <el-pagination @current-change="handlePageChange" :current-page="currentPage" :page-size="pageSize" :total="total"> </el-pagination> </template> <script> import { getNews } from '@/api'; export default { data() { return { newsList: [], currentPage: 1, pageSize: 10, total: 0 } }, mounted() { this.loadData(); }, methods: { async loadData() { const res = await getNews(this.currentPage, this.pageSize); this.newsList = res.data.content; this.total = res.data.totalElements; }, handlePageChange(page) { this.currentPage = page; this.loadData(); } } } </script> ``` #### 四、系统集成与优化 1. **定时爬虫任务** - 使用Spring Scheduled定时执行爬取: ```java @Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行 public void autoCrawlNews() { crawlerService.executeCrawler(); } ``` 2. **性能优化** - 分页查询:后端使用`Pageable`实现分页,避免单次加载过多数据[^1]。 - 缓存机制:集成Redis缓存高频访问数据。 #### 引用说明 - 数据存储分表设计和爬虫实现参考了小说推荐系统的分表策略[^1]。 - 前后端分离架构和Vue交互方式借鉴了新闻聚合平台的技术方案[^4]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值