ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java爬虫案例 -- springboot 整合 webmagic 爬取爱站网百度权重数据(保姆级教程)

2022-08-11 22:00:17  阅读:201  来源: 互联网

标签:Java springboot aizhan result org 站网 import com String


简介:本文以爬取 爱站网百度权重 为例子,详细介绍了用 Java 爬虫框架 webmagic 爬取网站数据的方法。

目录

本文对应的代码都会放在 GitHub 仓库 WebMagic-aizhan-java-spider
下,如果你觉得本文以及这个项目对你有用,麻烦在 GitHub 上给我 start 一下!感激不尽

1、本文所用技术介绍

  • Java爬虫框架 Webmagic:我们使用 Java 爬虫框架 Webmagic 来实现爬虫,这个框架的文档为:Webmagic文档 ,详细的使用方法以及代码细节见下文。
  • 项目框架:本项目使用 springboot 整合 mybatis 作为项目的框架,使用 maven 对项目依赖进行管理,具体的项目创建方法,参照我的另一篇文章: 使用 IDEA 搭建 Springboot 整合 mybatis 项目详解,对项目创建熟悉的朋友可以略过。

2、整体项目搭建以及代码解析

2.1 数据库的创建

在本地数据库创建一个存储爬取数据的表 search_result,表的创建代码如下:

CREATE TABLE `search_result_V2` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `key_char` varchar(256) NOT NULL DEFAULT '' COMMENT '关键词',
  `rank` varchar(256) NOT NULL DEFAULT '' COMMENT '排名',
  `pc_search_num` varchar(32) NOT NULL DEFAULT '' COMMENT '(PC)搜索量',
  `include_num` varchar(32) NOT NULL DEFAULT '' COMMENT '收录量',
  `page_title` varchar(255) NOT NULL DEFAULT '' COMMENT '网页标题',
  `search_content` varchar(64) NOT NULL DEFAULT '' COMMENT '用户查询的内容(网址)',
  `search_date` date NOT NULL COMMENT '查询日期',
  PRIMARY KEY (`id`),
  index search_data_index (search_content) # 创建查询内容的索引,加快查询速度
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='爱站网搜索结果(粒度为天)';

表创建成功,结果如下:
在这里插入图片描述
在这里插入图片描述

2.2 MVC架构的搭建以及配置

首先,创建一个 SpringBoot 整合 Mybatis 项目 AizhanBaiduCrawler ,如下图:
在这里插入图片描述
其次,在 pom.xml 文件下,导入相应的依赖,如下:

		<!-- Springboot 以及 mybatis 依赖-->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
		<!-- MySQL 依赖-->
		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- webmagic 依赖-->
        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-core</artifactId>
            <version>0.7.3</version>
        </dependency>

        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-extension</artifactId>
            <version>0.7.3</version>
        </dependency>
		<!-- lombok 依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- slf4j 日志配置-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>

下面我们填写项目总配置文件 application.yml,将 resources 目录下的 application.properties 修改为 application.yml,并填写相应的配置:

# 端口
server:
  port: 8080

### 数据库配置,填写你的数据库以及对应的用户名和密码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/data_crawler?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
    username: xxxx  
    password: xxxx
    driver-class-name: com.mysql.jdbc.Driver

# mybatis 配置内容
mybatis:
  config-location: classpath:mybatis-config.xml  # 配置 MyBatis 配置文件路径
  mapper-locations: classpath:mapper/*.xml  # 配置 Mapper XML 地址
  type-aliases-package: com.crawler.aizhan.dto # 配置数据库实体包路径

# slf4j日志配置
logging:
  config: src/main/resources/logback.xml #日志配置文件的位置
  level:
    com.hl.magic: trace

下面在 resources 下创建日志配置文件 logback.xml,这里需要修改的是你日子的存储地址,我这里是保存在 “src/main/resources/log” 目录下。

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="src/main/resources/log"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/AizhanCrawler_%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="INFO"/>
    <logger name="com.hl.magic" level="INFO"/>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

下面创建 mybatis 的配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!-- 使用驼峰命名法转换字段。 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
        <typeAlias alias="Integer" type="java.lang.Integer"/>
        <typeAlias alias="Long" type="java.lang.Long"/>
        <typeAlias alias="HashMap" type="java.util.HashMap"/>
        <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
        <typeAlias alias="ArrayList" type="java.util.ArrayList"/>
        <typeAlias alias="LinkedList" type="java.util.LinkedList"/>
    </typeAliases>

</configuration>

2.3 详细代码编写及分析

在 com/crawler/aizhan 包下创建一个包 dto ,在 dto 下创建一个 SearchResult 类用于封装爬取的数据,该类 SearchResult 的字段与数据库中 search_result 表的字段是对应的,代码如下:

package com.crawler.aizhan.dto;

import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class SearchResult {
    /**
     * Id
     */
    private Integer id;

    /**
     * 关键字
     */

    private String keyChar;

    /**
     * 排名
     */
    private String rank;

    /**
     * (PC)搜索量
     */
    private String pcSearchNum;

    /**
     * 收录量
     */
    private String includeNum;

    /**
     * 网页标题
     */
    private String pageTitle;

    /**
     * 用户查询的内容(网址)
     */
    private String searchContent;

    /**
     * 查询日期
     */
    private String searchDate;
}

随后,在 com/crawler/aizhan 包下创建一个 mapper 包,在该包下创建一个 Mapper 层的接口 AizhanMapper.java,这个接口包含2个方法,其代码如下:

package com.crawler.aizhan.mapper;

import com.crawler.aizhan.dto.SearchResult;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
 * @Author ThinkingOverflow
 * @Description
 */
@Mapper
@Repository
public interface AizhanMapper {
    /**
     * 将爬取的数据添加到数据库
     * @param result
     * @return
     */
    int addSearchData(SearchResult result);

    /**
     * 该日期对应的该查询关键词是否存在
     * @param searchContent
     * @param searchDate
     * @return
     */
    int selectByDateAndContent(String searchContent , String searchDate);
}

然后,我们在 resources 目录下添加一个新的文件夹 mapper,在该mapper创建一个 AizhanMapper.xml 配置文件,该文件对应的就是 AizhanMapper.java 的配置文件,代码如下:

<?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">
<!-- 注意命名空间 namespace 的包名 -->
<mapper namespace="com.crawler.aizhan.mapper.AizhanMapper">
    <!-- 添加数据 -->
    <insert id="addSearchData" parameterType="com.crawler.aizhan.dto.SearchResult">
        insert into search_result( key_char , rank , pc_search_num , include_num , page_title , search_content , search_date) values (#{keyChar} , #{rank},#{pcSearchNum},#{includeNum},#{pageTitle},#{searchContent},#{searchDate})
    </insert>
    <!--  该日期对应的该查询关键词是否存在  -->
    <select id="selectByDateAndContent" resultType="Integer" parameterType="string">
        select count(*) from search_result where search_content = #{searchContent} and search_date = #{searchDate};
    </select>

</mapper>

下面在 com/crawler/aizhan 包下创建一个包 service,在 service 再创建一个 process 包,在该包下创建 AizhanProcessor.java ,这个类的代码用于爬取数据,代码如下:

package com.crawler.aizhan.service.process;

import com.crawler.aizhan.dto.SearchResult;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Selectable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author ThinkingOverflow
 * @Description
 */
@Component
public class AizhanProcessor implements PageProcessor {
    
    private Site site = Site.me().setDomain("baidurank.aizhan.com")
            .setRetryTimes(3).setSleepTime(1000)
            .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
            //查询输入userid的 cookie 用于登录(不登录无法拉取到数据)
            .addCookie("userId" , "xxxxxx");

    @Override
    public void process(Page page) {
        //获取封装信息对应的 tr
        List<Selectable> trList = page.getHtml().xpath("//div[@class='baidurank-list']/table/tbody/tr").nodes();
        //获取查询的域名
        String searchContent = page.getHtml().xpath("//div[@class='search-wrap']/form/input[@type='text']/@value").toString();
        //将“www"去除避免重复将数据插入数据库
        if(searchContent.contains("www")){
            searchContent = searchContent.substring(4 , searchContent.length());
        }
        ArrayList<SearchResult> aizhanInfoList = Lists.newArrayList();

        //换个简单点的遍历方法
        for (Selectable tr : trList) {
            List<Selectable> tdList = tr.xpath("//td").nodes();
            if(tdList.size()==2 && "未找到信息!".equals(tdList.get(1).xpath("/td/text()").toString())){
                //拉取的url对应的信息不存在不存在,直接结束程序
                return;
            }
            int size = tdList.size();
            Selectable keyCharSele = tdList.get(size-5).xpath("//a/text()");
            Selectable rankSele = tdList.get(size-4).xpath("//span/text()");
            Selectable pcSearchNumSele = tdList.get(size-3).xpath("//a/text()");
            Selectable includeNumSele = tdList.get(size-2).xpath("//a/text()");
            Selectable pageTitleSele = tdList.get(size-1).xpath("//a/text()");

            String keyChar = keyCharSele==null ? "" : keyCharSele.toString().trim();
            String rank = rankSele==null ? "" : rankSele.toString().trim();
            String pcSearchNum = pcSearchNumSele==null ? "" : pcSearchNumSele.toString().trim();
            String includeNum = includeNumSele==null ? "" : includeNumSele.toString().trim();
            String pageTitle = pageTitleSele==null ? "" : pageTitleSele.toString().trim();

            SearchResult result = new SearchResult();
            result.setKeyChar(keyChar);
            result.setRank(rank);
            result.setPcSearchNum(pcSearchNum);
            result.setIncludeNum(includeNum);
            result.setPageTitle(pageTitle);
            result.setSearchContent(searchContent);
            //设置日期格式化样式为:yyyy-MM-dd (mysql中date类型对应 yyyy-MM-dd 格式)
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String formatDate = simpleDateFormat.format(new Date());
            result.setSearchDate(formatDate);

            aizhanInfoList.add(result);
        }

        //原来的遍历方法
//        for (int i = 0; i < trList.size(); i++) {
//            Selectable selectable = trList.get(i);
//            //先判断拉取的网站数据是否存在
//            Selectable notFindInfo = selectable.xpath("/tr/td[2]/text()");
//            if(Objects.nonNull(notFindInfo) && "未找到信息!".contains(notFindInfo.toString())){
//                //拉取的url对应的信息不存在不存在,直接结束程序
//                return;
//            }
//
//            //这个坐标用于解决第一个 <tr> 内有6个 <td> 的情况(其他都只有5个 <td>)
//            int coordinate = 1;
//
//            //遍历第一个 <tr> 的时候,从第二个 <td> 开始
//            if(i==0){
//                coordinate = 2;
//            }
//            Selectable keyCharSele = selectable.xpath("/tr/td[" + coordinate + "]/a[@class='gray']/text()");
//            Selectable rankSele = selectable.xpath("/tr/td[" + (coordinate+1) + "]/span[@class='blue']/text()");
//            Selectable pcSearchNumSele = selectable.xpath("/tr/td[" + (coordinate+2) + "]/a[@rel='nofollow']/text()");
//            Selectable includeNumSele = selectable.xpath("/tr/td[" + (coordinate+3) + "]/a[@class='gray']/text()");
//            Selectable pageTitleSele = selectable.xpath("/tr/td[" + (coordinate+4) + "]/a[@name='baiduLink']/text()");
//
//            String keyChar = keyCharSele==null ? "" : keyCharSele.toString();
//            String rank = rankSele==null ? "" : rankSele.toString();
//            String pcSearchNum = pcSearchNumSele==null ? "" : pcSearchNumSele.toString();
//            String includeNum = includeNumSele==null ? "" : includeNumSele.toString();
//            String pageTitle = pageTitleSele==null ? "" : pageTitleSele.toString();
//
//            SearchResult result = new SearchResult();
//            result.setKeyChar(keyChar);
//            result.setRank(rank);
//            result.setPcSearchNum(pcSearchNum);
//            result.setIncludeNum(includeNum);
//            result.setPageTitle(pageTitle);
//            result.setSearchContent(searchContent);
//            //设置日期格式化样式为:yyyy-MM-dd (mysql中date类型对应 yyyy-MM-dd 格式)
//            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
//            String formatDate = simpleDateFormat.format(new Date());
//            result.setSearchDate(formatDate);
//
//            aizhanInfoList.add(result);
//        }

        //每次加载完一页,先把数据添加到 field 中,这样 pipeline 就可以拿到数据(等 process 执行完就会跳转到 pipeline 中去执行插入数据)
        page.putField("aizhanInfoList" , aizhanInfoList);

        //下面代码的功能:将下一页的数据放入查询链接列表
        List<Selectable> aNodes = page.getHtml().xpath("//div[@class='baidurank-pager']/ul/a").nodes();
        int nextPage = -1;
        for (int i = 0; i < aNodes.size(); i++) {
            Selectable classVal = aNodes.get(i).$("a", "class");
            if(StringUtils.isNotEmpty(classVal.toString())){
                nextPage = i +1;
                break;
            }
        }
        //不是第一页也没有超过最后一页(第一页重新进来的时候已经查找)
        if(nextPage > 0 && nextPage < aNodes.size()){
            page.addTargetRequest(aNodes.get(nextPage).$("a" , "href").toString());
        }else{
            System.out.println("finish");
        }


    }

    @Override
    public Site getSite() {
        return site;
    }
}

这个类有几个点需要注意,首先是 Site 处需要添加 cookie 用来保存用户的登录态(没有登录的用户无法查询爱站网百度权重数据),这里需要 addCookie("userId" , "xxxxxx"); ,此处 userId 需要你先注册登录爱站网,然后按 F12 进入开发者模式,找到“应用”,然后找到 Cookie,找到 userId 即可。
在这里插入图片描述

在代码方面,有几个地方需要注意,都是获取元素值的代码,如:

//获取封装信息对应的 tr
List<Selectable> trList = page.getHtml().xpath("//div[@class='baidurank-list']/table/tbody/tr").nodes();

这行代码获取的是“ class 属性值为 baidurank-list 的 div 元素下的 table 下的 tbody 下的 tr 元素的集合 ”,即先获取包含每一个要爬取记录的行对象,其元素结构如下图:
在这里插入图片描述
如下代码,获取的是“ class 属性值为 search-wrap 的 div 元素下的 table 下的 from 下的 type 属性值为 text 的 input 元素的值 ”,对应的元素结构如下图

//获取查询的域名
String searchContent = page.getHtml().xpath("//div[@class='search-wrap']/form/input[@type='text']/@value").toString();

在这里插入图片描述
对于下面代码,主要是为了排除查询 url 不存在的情况,如搜索 xxx.com,元素结构如下图:

if(tdList.size()==2 && "未找到信息!".equals(tdList.get(1).xpath("/td/text()").toString())){
      //拉取的url对应的信息不存在不存在,直接结束程序
      return;
}

在这里插入图片描述
下面代码的主要功能是将下一页的 url 放入 page 对象,下一次抓取才能进行。如下结构图,先遍历获取 class 属性值不为空的 a 元素,找到当前页数和下一页,然后才能找到下一页的 a 元素的 href 属性值,也就是下一页的 url。

	//下面代码的功能:将下一页的数据放入查询链接列表
    List<Selectable> aNodes = page.getHtml().xpath("//div[@class='baidurank-pager']/ul/a").nodes();
    int nextPage = -1;
    for (int i = 0; i < aNodes.size(); i++) {
        Selectable classVal = aNodes.get(i).$("a", "class");
        if(StringUtils.isNotEmpty(classVal.toString())){
            nextPage = i +1;
            break;
        }
    }
    //不是第一页也没有超过最后一页(第一页重新进来的时候已经查找)
    if(nextPage > 0 && nextPage < aNodes.size()){
        page.addTargetRequest(aNodes.get(nextPage).$("a" , "href").toString());
    }else{
        System.out.println("finish");
    }

在这里插入图片描述


更加详细的 webmagic 爬虫页面元素的抽取方法可以参考我的另一篇文章:《待写》

再在 service 再创建一个 pipeline 包,在该包下创建 AizhanPipeline.java ,这个类的代码用于爬取数据,代码如下,这个类主要用于将爬取的数据插入数据库里面。

package com.crawler.aizhan.service.pipeline;

import com.crawler.aizhan.dto.SearchResult;
import com.crawler.aizhan.mapper.AizhanMapper;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

import java.util.ArrayList;

/**
 * @Author ThinkingOverflow
 * @Description
 */
@Component
public class AizhanPipeline implements Pipeline {
    @Autowired
    private AizhanMapper aizhanMapper;

    @Override
    public void process(ResultItems resultItems, Task task) {
        ArrayList<SearchResult> aizhanInfoList = resultItems.get("aizhanInfoList");
        if(CollectionUtils.isNotEmpty(aizhanInfoList)){
            aizhanInfoList.stream().forEach(SearchResult -> {
                aizhanMapper.addSearchData(SearchResult);
            });
        }
    }
}

随后在该 service 包下创建一个类 AizhanService.java,代码如下。需要说明的是,我们查询的粒度是“天”,因此这里先查询当天该查询域名是否已经爬取过数据,爬取过则无需重复爬取数据到数据库。

package com.crawler.aizhan.service;

import com.crawler.aizhan.mapper.AizhanMapper;
import com.crawler.aizhan.service.pipeline.AizhanPipeline;
import com.crawler.aizhan.service.process.AizhanProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import us.codecraft.webmagic.Spider;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import java.util.regex.Pattern;


/**
 * @Author ThinkingOverflow
 * @Description
 */
@Service
@Slf4j
public class AizhanService {
    @Autowired
    private AizhanPipeline aizhanPipeline;

    @Autowired
    private AizhanProcessor aizhanProcessor;

    @Autowired
    private AizhanMapper aizhanMapper;

    private final String DOMAIN_NAME_PATTERN = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}$";

    /**
     * 抓取爱站网数据
     */
    public void getSearchData(){
        while (true){
            System.out.println();
            System.out.println("------------------------");
            System.out.print("请输入要查询的域名:");
            Scanner in = new Scanner(System.in);
            String searchContent = in.nextLine();

            if(!isURL(searchContent)){
                log.info("输入的域名无效");
                continue;
            }

            //判断当前日期是否已经查询过该域名,如果查询过,则无须继续查询(我们查询的时间粒度是“天”)
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String searchDate = simpleDateFormat.format(new Date());
            //排除域名前面有 www 的重复情况
            if(searchContent.contains("www")){
                searchContent = searchContent.substring(4 , searchContent.length());
            }
            int count = aizhanMapper.selectByDateAndContent(searchContent, searchDate);
            if(count!=0){
                log.info("当天已经查询过" + searchContent + ",请去数据库查询数据,无须重复抓取爱站网数据");
                continue;
            }

            //先查找第一页数据
            String url = "https://baidurank.aizhan.com/baidu/" + searchContent;
            Spider.create(aizhanProcessor)
                    .addUrl(url)
                    .addPipeline(aizhanPipeline)
                    .thread(1)
                    .run();
        }

    }

    /**
     * 验证输入的字符串是否是有效域名
     * @param str
     * @return
     */
    public boolean isURL(String str){
        Pattern pattern = Pattern.compile(DOMAIN_NAME_PATTERN);
        return pattern.matcher(str).find();
    }

}

最后,在 com/crawler/aizhan 包下创建一个包 controller,创建一个 AizhanController 作为程序的入口

package com.crawler.aizhan.controller;

import com.crawler.aizhan.service.AizhanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author ThinkingOverflow
 * @Description
 */
@RestController
public class AizhanController {

    @Autowired
    private AizhanService service;

    /**
     * 抓取爱站网数据
     */
    @RequestMapping("/aizhan")
    public void getSearchData(){
        service.getSearchData();
    }

}

项目额整个目录结构如下图:
在这里插入图片描述

3 项目测试以及相关问题的解决

3.1 项目测试

先启动项目,随后我们进行测试,在浏览器输入 “http://localhost:8080/aizhan” 并回车,然后我们在控制台输入要查询的域名:
(1)baidu.com:我们先查询 “baidu.com”,等待一下发现数据已经加载到数据库,等加载完毕后,在数据库查询SELECT count(*) FROM search_resultwhere search_content = 'baidu.com'; 发现数据量为 1250 条,则说明程序运行正确。

(2)www.baidu.com:我们查询 “www.baidu.com”,逐步运行发现程序会自动去重,如下图:
在这里插入图片描述
(3)baidu.com:再次查询 “baidu.com”,发现同样会自动去重
在这里插入图片描述
(4)bilibili.com:查询其他域名,如“bilibili.com”,依然可以正常查询。
在这里插入图片描述

3.2 可能出现的问题以及解决方法

(1)出现 “java: 错误: 无效的源发行版:15”

参考文章: 解决:java无效的目标发行版: 15(百分百有效)



特别声明
(1)本文涉及的任何代码,仅用于个人学习研究,禁止用于商业用途,本人对任何代码问题概不负责;
(2)本文涉及的所有资源文件,禁止任何公众号、自媒体在未经本人允许的情况下进行任何形式的转载、发布;
(3)如果任何单位或个人认为该项目可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明,我们将在收到认证文件后删除相关代码。


个人水平有限,如有错误,欢迎各位朋友在评论区批评指正

标签:Java,springboot,aizhan,result,org,站网,import,com,String
来源: https://www.cnblogs.com/thinkingoverflow/p/16578051.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有