我这边找了个小说网站:

基本套路:

第一步:获取小说每一章的url地址



第二步:获取章节url内容并使用正则表达式提取需要的内容



第三步:多线程封装,实现如下效果



最后测试。

代码:

内容获取封装:

public class WebSpider {
//<a href="/35/35971/13555631.html"> 第1章:边哨惨案 </a> -->{"/35/35971/13555631.html","第1章:边哨惨案"}
// 存放所有章节列表和标题
private List<String[]> urlList;
// 指定下载的跟目录
private String rootDir;
// 指定编码
private String encoding;
public WebSpider() {
urlList = new ArrayList<String[]>();
}
public WebSpider(String titleUrl, Map<String, String> regexMap, String rootDir, String encoding) {
this();
this.rootDir = rootDir;
this.encoding = encoding;
initUrlList(titleUrl, regexMap);
} /**
* 初始化小说所有章节列表 在构造方法中调用
* @param url
* @param regexMap
*/
private void initUrlList(String url, Map<String, String> regexMap) {
StringBuffer sb = getContent(url, this.encoding);
int urlIndex = Integer.parseInt(regexMap.get("urlIndex"));
int titleIndex = Integer.parseInt(regexMap.get("titleIndex"));
Pattern p = Pattern.compile(regexMap.get("regex"));
Matcher m = p.matcher(sb);
while (m.find()) {
String[] strs = { m.group(urlIndex), m.group(titleIndex) };
this.urlList.add(strs);
}
} /**
* 获取文本内容
*
* @param urlPath
* @param enc
* @return
*/
public StringBuffer getContent(String urlPath, String enc) {
StringBuffer strBuf;
class Result{
StringBuffer sb;
public Result() {
BufferedReader reader = null;
HttpURLConnection conn = null;
try {
URL url = new URL(urlPath);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36");
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), enc));
sb = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\r\n");
}
} catch (Exception e) {
// e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
for(int i = 1; ; i++) {
strBuf = new Result().sb;
if(strBuf == null) {
try {
Thread.sleep(5000 * i);
System.out.println("=====================等待 --》" + (5000 * i));
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
break;
}
return strBuf;
} /**
* 获取正则表达式匹配后内容
* @param url
* @param enc
* @param regex
* @param indexGroup
* @return
*/
public String getDestTxt(String url, String enc, String regex, int indexGroup) {
StringBuffer sb = getContent(url, enc);
StringBuffer result = new StringBuffer();
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(sb);
while (m.find()) {
result.append(m.group(indexGroup).replace("<br />", "\r\n"));
}
return result.toString();
} public String getRootDir() {
return rootDir;
} public List<String[]> getUrlList() {
return urlList;
}
}

多线程封装:

class Down implements Runnable {// 线程类
private WebSpider spider;
private String enc;// 编码 这个网站的标题列表编码和正文编码不一样
private String bookName;// 书名
// 分卷下载,指定开始和结束
private int start;
private int end;
private Map<String, String> regexMap; public Down(WebSpider spider, Map<String, String> regexMap, String enc, String bookName, int start, int end) {
this.spider = spider;
this.regexMap = regexMap;
this.enc = enc;
this.bookName = bookName;
this.start = start;
this.end = end;
} @Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始启动下载>>>>>");
List<String[]> list = this.spider.getUrlList();
String[] arr = null;
for (int i = start; i < end; i++) {
arr = list.get(i);
String url = "https://www.luocs.cn" + arr[0];
String title = arr[1];
this.writeTOFile(url, this.regexMap.get("regex"), Integer.parseInt(this.regexMap.get("groupIndex")), title);
if (i % 10 % 3 == 0) {
System.out.println(Thread.currentThread().getName() + "下载进度-->" + (i % 10) + "0%");
}
// 暂停1~3秒爬取下一章节
try {
Thread.sleep((long) (1000L + Math.random() * 20000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "下载完成!"); } /**
* 写入到文件
* @param url 正文url 地址
* @param regex 正文匹配的正则表达式
* @param indexGroup 正文匹配的正则表达式分组
* @param title 每个章节的标题
*/
private void writeTOFile(String url, String regex, int indexGroup, String title) {
String src = this.spider.getDestTxt(url, this.enc, regex, indexGroup);
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(
new File(this.spider.getRootDir() + this.bookName + this.start + "--" + this.end + ".txt"), true));
// 简单美化一下格式
writer.append("*****").append(title).append("*****");
writer.newLine();
writer.append(src, 0, src.length());
writer.newLine();
writer.append("====================");
writer.newLine();
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

测试:

我这边直接写main方法测试:

public static void main(String[] args) {
String url = "https://www.luocs.cn/35/35971/0.html";
Map<String, String> map = new HashMap<String, String>();
map.put("regex", "href='(.+html)'>(.+:{1}.+)</a>");
map.put("urlIndex", "1");
map.put("titleIndex", "2");
WebSpider spider = new WebSpider(url, map, "f:/Game/", "gbk");
Map<String, String> regexMap = new HashMap<String, String>();
regexMap.put("regex", "(&nbsp;){4}(.+)");
regexMap.put("groupIndex", "2");
for (int i = 0; i < 2; i++) {
Down d = new Down(spider, regexMap, "gb2312", "最强兵王", i * 10, (i + 1) * 10);
new Thread(d, "线程 " + (i + 1) + "[" + (i * 10 + 1) + "~~" + ((i + 1) * 10 - 1) + "]:").start();
}
}

效果:

503失败后,等待一段时间再试



最后写到本地效果



如果把文本导入到手机,用UC或者QQ 浏览器插件打开,会自动区分章节,在配上背景色,那样阅读起来就真的是清爽无弹窗了。

这个网站不怎么好爬,大家可以找个好爬点的网站爬取。当然这个503处理也不是很好,各位路边的大神如果有更好的办法,请指教。

Java 使用正则表达式和IO实现爬虫以及503解决的更多相关文章

  1. Java面向对象 正则表达式

     Java面向对象 正则表达式 知识概要:                (1)正则表达式的特点 (2)正则表达的匹配 (3)正则表达式的切割,替换,获取 (4)正则表达式的练习 正则表达式:符合 ...

  2. Java与正则表达式

    Java与正则表达式 标签: Java基础 正则 正如正则的名字所显示的是描述了一个规则, 通过这个规则去匹配字符串. 学习正则就是学习正则表达式的语法规则 正则语法 普通字符 字母, 数字, 汉字, ...

  3. Java实现一个简单的网络爬虫

    Java实现一个简单的网络爬虫 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWri ...

  4. java中正则表达式基本用法

    正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串.正则表达式作为 ...

  5. java基础---->java中正则表达式二

    跟正则表达式相关的类有:Pattern.Matcher和String.今天我们就开始Java中正则表达式的学习. Pattern和Matcher的理解 一.正则表达式的使用方法 一般推荐使用的方式如下 ...

  6. Java socket中关闭IO流后,发生什么事?(以关闭输出流为例)

    声明:该博文以socket中,关闭输出流为例进行说明. 为了方便讲解,我们把DataOutputstream dout = new DataOutputStream(new BufferedOutpu ...

  7. Java的正则表达式

    package RegexTest; /** * Created by hu on 2016/3/29. */ /* * Java的正则表达式 在正则表达式中,用\d表示一位数字,如果在其它语言中使用 ...

  8. Java中正则表达式去除html标签

    Java中正则表达式去除html的标签,主要目的更精确的显示内容,比如前一段时间在做类似于博客中发布文章功能,当编辑器中输入内容后会将样式标签也传入后台并且保存数据库,但是在显示摘要的时候,比如显示正 ...

  9. Java 常用正则表达式,Java正则表达式,Java身份证校验,最新手机号码正则表达式

    Java 常用正则表达式,Java正则表达式,Java身份证校验,最新手机号码校验正则表达式 ============================== ©Copyright 蕃薯耀 2017年11 ...

随机推荐

  1. [noip模拟]改造二叉树<LIS>

    1.改造二叉树 [题目描述] 小Y在学树论时看到了有关二叉树的介绍:在计算机科学中,二叉树是每个结点最多有两个子结点的有序树.通常子结点被称作“左孩子”和“右孩子”.二叉树被用作二叉搜索树和二叉堆.随 ...

  2. Java序列化机制剖析

    本文转载自longdick的博文<Java序列化算法透析>,原文地址:http://longdick.iteye.com Java序列化算法透析 Serialization(序列化)是一种 ...

  3. Celery动态添加定时任务

    背景 业务需求:用户可创建多个多人任务,需要在任务截止时间前一天提醒所有参与者 技术选型: Celery:分布式任务队列.实现异步与定时 django-celery-beat:实现动态添加定时任务,即 ...

  4. 《Three.js 入门指南》3.0 - 代码构建的最基本结构。

    3.0 代码构建的最基本结构 说明: 我们必需首先知道,Three.js 的一些入门级概念: 我们需要知道,OpenGL 是一套三维实现的标准,为什么说是标准,因为它是跨平台,跨语言的.甚至CAD以及 ...

  5. 1062 Talent and Virtue (25分)(水)

    About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about ...

  6. 手把手教你分析Mysql死锁问题

    前言 前几天跟一位朋友分析了一个死锁问题,所以有了这篇图文详细的博文,哈哈~ 发生死锁了,如何排查和解决呢?本文将跟你一起探讨这个问题 准备好数据环境 模拟死锁案发 分析死锁日志 分析死锁结果 环境准 ...

  7. 【Mongodb】聚合查询 && 固定集合

    概述 数据存储是为了可查询,统计.若数据只需存储,不需要查询,这种数据也没有多大价值 本篇介绍Mongodb 聚合查询(Aggregation) 固定集合(Capped Collections) 准备 ...

  8. laravel使用创建的request作为表单验证类

    1.使用命令行工具创建request php artisan make request:validateLoginRequest 2.创建后进入app/Http/Requests目录下找到创建的文件 ...

  9. 曹工说Redis源码(4)-- 通过redis server源码来理解 listen 函数中的 backlog 参数

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  10. Java并发编程实战 01并发编程的Bug源头

    摘要 编写正确的并发程序对我来说是一件极其困难的事情,由于知识不足,只知道synchronized这个修饰符进行同步. 本文为学习极客时间:Java并发编程实战 01的总结,文章取图也是来自于该文章 ...