Java多线程爬虫案例

Java多线程爬虫案例

由于单线程下性能问题,无法满足快速爬取网页数据,于是采用了多线程爬虫的方式来浅试一下,只尝试了几千条数据,没有尝试大数据,还有很多需要改进的地方。

主要逻辑如下:

  1. 线程池
  2. 数据爬取
  3. 数据保存
  4. 多线程操作

线程池配置类

@Configuration
public class ThreadPoolConfig {

    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 线程池中的线程名前缀
        executor.setThreadNamePrefix("common-asyn-thread-");
        // 设置线程池关闭的时候需要等待所有任务都完成 才能销毁其他的Bean
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
        executor.setAwaitTerminationSeconds(60);
        //核心线程
        executor.setCorePoolSize(10);
        //最大线程
        executor.setMaxPoolSize(300);
        // 任务阻塞队列的大小
        executor.setQueueCapacity(300);

        return executor;
    }
}

使用okhttp可能会有坑,需加入这个到pom文件中,即可解决

    <dependency>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-stdlib</artifactId>
        <version>1.3.72</version>
    </dependency>

核心代码

@Slf4j
@ActiveProfiles("prod")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DdjdApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test01_sys {

    @Resource
    private ThreadPoolConfig theaThreadPoolConfig;
    @Resource
    private ISmStudentService smStudentService;
    private List<SmStudent> dataList = new Vector<>();

    @Test
    public void mutiTheadGetAndSaveDataDemo() {
        // 原子类 线程安全
        AtomicInteger page = new AtomicInteger(0);
        // 闭锁计数器
        CountDownLatch countDownLatch = new CountDownLatch(136);
        for (int i = 1; i <= 136; i++) {
            theaThreadPoolConfig.threadPoolTaskExecutor().execute(() -> {
                try {
                    dealData(page);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 减一操作
                    countDownLatch.countDown();
                }
            });
        }
        try {
            // 阻塞主线程 增加兜底10s,自动释放
            countDownLatch.await(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(dataList);

        // 数据分组处理
        List<List<SmStudent>> studentListAll = Lists.partition(dataList, 200);
        // 闭锁计数器
        CountDownLatch countDownLatchNew = new CountDownLatch(studentListAll.size());
        for (List<SmStudent> students : studentListAll) {
            theaThreadPoolConfig.threadPoolTaskExecutor().execute(() -> {
                try {
                    saveData(students);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 减一操作
                    countDownLatchNew.countDown();
                }
            });
        }
        try {
            // 阻塞主线程 增加兜底10s,自动释放
            countDownLatchNew.await(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            theaThreadPoolConfig.threadPoolTaskExecutor().shutdown();
        }
        log.warn("数据保存成功:{}", dataList.size());
    }

    /**
     * 多线程数据爬取
     *
     * @param page 页面索引
     */
    private void dealData(AtomicInteger page) {
        // 索引自增,使用原子类,考虑到线程安全问题
        int index = page.incrementAndGet();
        // 通过okhttp的方式爬取数据
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                // 网页链接,这里因为隐私隐掉了地址
                .url("url" + index + "&pageSize=20")
                .get()
                // 令牌 例如token
                .addHeader("LoginUser", "")
                .addHeader("cache-control", "no-cache")
                .build();
        try {
            Response response = client.newCall(request).execute();
            // 获取返回数据,并进行处理解析
            String rsp = response.body().string();
            JSONObject jsonRsp = JSONObject.parseObject(rsp);
            JSONObject data = (JSONObject) jsonRsp.get("data");
            JSONArray array = data.getJSONArray("list");
            /*for (Object o : array) {
                // 解析每一条数据
                StudentInfo studentInfo = JSON.toJavaObject((JSON) o, StudentInfo.class);
                System.err.println(studentInfo.getRange());
            }*/
            // 转成java list
            List<SmStudent> list = array.toJavaList(SmStudent.class);
            dataList.addAll(list);
        } catch (IOException e) {
            log.error("数据爬取失败!");
        }
    }

    /**
     * 数据保存
     *
     * @param students
     */
    private void saveData(List<SmStudent> students) {
        smStudentService.saveBatch(students);
    }

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值