java爬虫入门第三弹——正则表达式简单应用(抓取豆瓣读书信息并以文本文件输出)

news/2024/7/7 19:25:03 标签: java, java入门, Java爬虫, 正则表达式, 爬虫

简单介绍一下:

老王是个新人,心血来潮想用java试试写爬虫,完全零基础,搜了很多教程,往往因为作者水平太高,不能一下子理解大佬代码中的深意,并且有些看似很简单的东西,对于我这种菜鸟来说,其实是很难解决的错误或者是异常。故,在稍有心得后,写下此篇。从最基础开始。一步一步,从小菜鸟,成为稍微大一点的菜鸟,给初学者带来一点启示。

如果只需要全部的代码,请直接拉至最后

如果转载,请注明出处:https://blog.csdn.net/qq_37893828/article/details/88133686 ——会飞的王浩然

如有疏漏之处,欢迎留言指出

往期列表:更多详细解释请从第一弹翻阅

  • java爬虫入门第一弹——从百度首页开始(欢迎浏览)
  • java爬虫入门第二弹——通过URL下载图片(以下载百度logo为例)(欢迎浏览)
  • java爬虫入门第三弹——正则表达式简单应用(抓取豆瓣读书信息并以文本文件输出)(欢迎浏览)

这一节我们的目标

根据下载到的网页源代码即HTML页面代码,获取相应的文字图片等信息,我们以豆瓣读书为例

java_17">从最基础开始,我首先假设你什么都不会,只会一点点的C语言和java编程




  • 正则表达式的简单作用:
  • 正则表达式定义了字符串的模式,可以用来搜索、编辑和处理文本。
  • 举个小栗子: 给一串字符串:"…1243253565adfGDse24456585…",假如有这样一个字符串,很多很多的数字(省略号表示还有很多数字),我们知道这串字符串里面有几个连续的字母,具体有几个不清楚,字母是什么也不清楚,但是我们想找到这几个字母,我们该怎么办?
  • 在不知道正则表达式之前,我们是这样做的:,遍历字符串,对字符串中的每一个字符,判断它的ASCII码的值是不是在65-90或者97-122之间 (因为字母有大写小写之分) ,这样解决起来还不算麻烦,也就做个判断,每个都做个遍历即可
  • 但是如果字符串修改为:其中有很多很多的数字,也有很多很多的字母,这些字母既有不连续的一个字母,也有接连两个,接连三个、四个、五个都是字母,我们只要其中连续三个都是字母的子字符串,这时候该怎么解决?也可以通过遍历,但是遍历的次数一下子翻了很多倍,代码长,运算多,何况很多复杂的问题并不仅仅是数字跟字母。这时候我们要怎么做呢?答案就是通过正则表达式匹配
  • 请看下面的代码:这是一个正则表达式简单应用的例子
java">public static void main(String[] args){
		// TODO Auto-generated method stub
		
		//定义我们原始字符串
		String ssrString="121412252364636abCDE87897sfsrDF98778";
		
		//定义正则表达式模板
		String patternString="([a-z,A-Z]+)";
		
		//进行匹配
		Matcher matcher=Pattern.compile(patternString).matcher(ssrString);
		
		//表示输出所有符合条件的子字符串
		while(matcher.find()) {
			System.out.println(matcher.group());
		}
	}
  • 输出结果为:

  • 在字符串ssrString中含有两个全部由字母组成的子串,我们通过正则表达式将其找出来只需要简单的一行代码:“Matcher matcher=Pattern.compile(patternString).matcher(ssrString);”

  • patternString是我们定义的正则表达式形式它的含义是:“[a-z,A-Z]”表示匹配英文大小写字母,“+”表示匹配的数量是一个或者多个。(更多语法参看上面的菜鸟教程)

  • Pattern:一个Pattern对象表示一个正则表达式经编译后的表现模式。

  • patternString 这个字符串中的内容即为我们所定义的正则表达式模板

  • Pattern.compile 的含义是:将给定的正则表达式编译并赋予Pattern类

  • Matcher:一个Matcher对象是一个状态机器,它依据Pattern对象作为匹配模式对字符串展开匹配检查;它的参数是要进行匹配的字符串。

  • 上面一行代码拆开来看即为:

java">	Pattern pattern=Pattern.compile(patternString);
	Matcher matcher=pattern.matcher(ssrString);


  • 第二步: 下载豆瓣读书首页源代码,并对该源代码首页进行分析。
  • 我们现在浏览器中打开豆瓣读书,我们希望将豆瓣读书首页上的"最受关注图书榜"中的图书和作者给保存到文本文件中去,无论我们什么时候需要这个排行榜的内容,只需要运行一下代码即可得知,而不需要再自己记录。
  • 右键网页空白处,选择查看网页源代码,在2246行,我们找到“最受关注排行榜”字样
    -在这里插入图片描述
    查看豆瓣读书首页,发现"最受关注图书榜"下面的模块是“豆瓣书店”
    在这里插入图片描述
    在代码中我们发现豆瓣书店也在最受关注排行榜的后面,而我们要爬取的图书名和作者名信息就在2246行和2264行中间,我们需要的是对这中间的代码进行分析,为了避免无关的干扰,我们可以在下载了“豆瓣读书”首页源代码之后,对这串字符串进行分割。让它以“最受关注图书排行榜”开始,“豆瓣书店”为结束;
java">	
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub

		String urlString = "https://book.douban.com/";

		//将第一弹内容打包成页面下载函数
		String doubanBookString = downloadHtml(urlString);
		
		//String类自带的获取子串的方法。
		
		//表示从获取"最受关注排行榜"这个子串的开始位置一直到结束的子串
		doubanBookString=doubanBookString.substring(doubanBookString.indexOf("最受关注图书榜"));
		//表示获取从头到"豆瓣书店"这个子串的开始位置的子串
		doubanBookString=doubanBookString.substring(0,doubanBookString.indexOf("豆瓣书店"));
	}
  • 继续对这串分割后的字符串进行分析,我们发现每一个<img>标签都对应一张排行榜里面一本图书的图片,而且img标签里面还有这本书的名字。而每一个class=“author” 的<p>标签,里面都有对应的图书的作者名称。发现了这个规律,那么接下来的操作就是显而易见的了。
  • 首先构造能够匹配到<img>标签和“作者”的正则表达式
java">	//构造匹配<img>标签的正则表达式
	String bookNamePatternString="<img(.+?)>";
	//构造匹配class="author"的<p>标签
	String bookAuthorPatternString="author(.+?)</p>";

两个表达式分别匹配到的结果为:
在这里插入图片描述



  • 第三步: 对第二步中匹配到的书名和作者的两个子串继续进行匹配,以便获得书名和作者名称,并将其写入book.txt中,一个完整的步骤如下所示:
java">	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub

		//构造文件输出流
		PrintStream output=new PrintStream(new File("book.txt"));
		
		// 豆瓣读书的链接
		String urlString = "https://book.douban.com/";
		// 下载豆瓣读书的页面并且以字符串形式进行存储
		String doubanBookString = downloadHtml(urlString);
		
		// 获取"最受关注排行榜"这个子串的开始位置一直到结束的子串
		doubanBookString = doubanBookString.substring(doubanBookString.indexOf("最受关注图书榜"));
		// 获取从头到"豆瓣书店"这个子串的开始位置的子串
		doubanBookString = doubanBookString.substring(0, doubanBookString.indexOf("豆瓣书店"));
		
		Matcher matcher = null;

		// 构造匹配中文的字符串
		String chinesePatternString = "[\\u4E00-\\u9FA5]+";

		// 构造匹配<img>标签的正则表达式
		String bookNamePatternString = "<img(.+?)>";
		
		String bookNameString = null;
		//获得<img>标签中的内容
		matcher = Pattern.compile(bookNamePatternString).matcher(doubanBookString);
		if (matcher.find())
			bookNameString = matcher.group();
		//获得<img>标签中的中文即书名
		matcher = Pattern.compile(chinesePatternString).matcher(bookNameString);
		if (matcher.find())
			//将书名写入book.txt中
			output.println("书名:《"+matcher.group()+"》");

		// 构造匹配class="author"的<p>标签的正则表达式
		String bookAuthorPatternString = "author(.+?)</p>";
		
		String bookAuthorString = null;
		//获得含有作者名字的子字符串
		matcher = Pattern.compile(bookAuthorPatternString).matcher(doubanBookString);
		if (matcher.find())
			bookAuthorString = matcher.group();
		//构造匹配作者名称的正则表达式
		bookAuthorPatternString="作者(.+?)(<)";
		matcher = Pattern.compile(bookAuthorPatternString).matcher(bookAuthorString);
		if (matcher.find()) {
			//由于匹配到的作者名称会在最后多一个字符"<"所以我们要去掉这个字符
			output.println(matcher.group().substring(0,matcher.group().length()-1));
		}
	}
  • 得到的结果为:
    在这里插入图片描述
  • 这里只保存了一本书的书名和作者名,如果需要保存全部的书名和作者名,我们可以使用字符串列表,将书名和作者名字符串列表中到后面统一输出。具体请参考全部代码


全部代码

java">import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

	// 网页下载函数
	public static String downloadHtml(String urlString) throws IOException {

		// 构造URL对象
		URL url = new URL(urlString);

		// 连接到URL
		HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

		// 创建数据流
		// 获取数据,并以字节的形式放在缓冲区
		InputStream inputStream = httpURLConnection.getInputStream();
		// 使用指定字符集将字节流转换到字符流
		InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
		// 将字符流放入缓存里,如果缓存满了,就读入内存,这个类存在的作用是为了提高读的效率
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

		// 从字符流中读取数据
		String lineString;
		String htmlString = "";
		while ((lineString = bufferedReader.readLine()) != null) {
			htmlString += lineString;
		}

		return htmlString;
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub

		// 构造文件输出流
		PrintStream output = new PrintStream(new File("book.txt"));

		// 构建书本名称列表
		ArrayList<String> bookNameList = new ArrayList<String>();
		// 构建作者名字列表
		ArrayList<String> authorNameList = new ArrayList<String>();

		// 豆瓣读书的链接
		String urlString = "https://book.douban.com/";
		// 下载豆瓣读书的页面并且以字符串形式进行存储
		String doubanBookString = downloadHtml(urlString);

		// 获取"最受关注排行榜"这个子串的开始位置一直到结束的子串
		doubanBookString = doubanBookString.substring(doubanBookString.indexOf("最受关注图书榜"));
		// 获取从头到"豆瓣书店"这个子串的开始位置的子串
		doubanBookString = doubanBookString.substring(0, doubanBookString.indexOf("豆瓣书店"));

		Matcher matcher = null;

		// 构造匹配中文的字符串
		String chinesePatternString = "[\\u4E00-\\u9FA5]+";

		// 构造匹配<img>标签的正则表达式
		String bookNamePatternString = "<img(.+?)>";

		String arrString = null;
		// 获得<img>标签中的内容
		matcher = Pattern.compile(bookNamePatternString).matcher(doubanBookString);
		while (matcher.find()) {
			arrString = matcher.group();
			// 获得<img>标签中的中文即书名
			Matcher matcher1 = Pattern.compile(chinesePatternString).matcher(arrString);
			if (matcher1.find())
				// 将书名写入book.txt中
				bookNameList.add("书名:《" + matcher1.group() + "》");
		}
			
		// 构造匹配class="author"的<p>标签的正则表达式
		String bookAuthorPatternString = "author(.+?)</p>";

		// 获得含有作者名字的子字符串
		matcher = Pattern.compile(bookAuthorPatternString).matcher(doubanBookString);
		while (matcher.find()) {
			arrString = matcher.group();
			// 构造匹配作者名称的正则表达式
			bookAuthorPatternString = "作者(.+?)(<)";
			Matcher matcher1 = Pattern.compile(bookAuthorPatternString).matcher(arrString);
			if (matcher1.find()) {
				// 由于匹配到的作者名称会在最后多一个字符"<"所以我们要去掉这个字符
				authorNameList.add(matcher1.group().substring(0, matcher1.group().length() - 1));
			}
		}
			
		for(int i=0;i<bookNameList.size();i++) {
			output.println(bookNameList.get(i));
			output.println(authorNameList.get(i));
		}
	}

}

输出结果:

在这里插入图片描述


http://www.niftyadmin.cn/n/917658.html

相关文章

Java学习基础知识

J2SEJava的标准开发平台。是J2EE和J2ME的基础,Core Java开发基于该平台 J2EEJava的企业己开发平台,Java的主要应用方向,大型系统的开发都是在该平台的完成,Web开发也属于其中的一部分 J2MEJava的微系统开发平台,是一个正在成长的平台,主要用于资源有限的设备上的Java的…

为Windows 7的winsxs目录瘦身,谨慎。

刚使用Win7 系统不久&#xff0c;前段时间在清理系统垃圾时发现&#xff0c;win7系统的windows文件夹下的winsxs 文件夹占用空间很大&#xff0c;想清理之&#xff0c;却提示无权限无法清理。随即在网上查了个到底&#xff0c;原来winsxs是一个超大的文件仓库&#xff0c;系统所…

Java 反射基础

语言对自身的一个检验实现: import java.lang.reflect.Method;import java.util.Stack; public class HelloWord

codevs1145

题目描述 Description 给定A、B、C三根足够长的细柱&#xff0c;在A柱上放有2n个中间有孔的圆盘&#xff0c;共有n个不同的尺寸&#xff0c;每个尺寸都有两个相同的圆盘&#xff0c;注意这两个圆盘是不加区分的&#xff08;下图为n3的情…

Java format date to String or String to date

参考类: java.text 类 DateFormat java.lang.Objectjava.text.Formatjava.text.DateFormat所有已实现的接口: Serializable, Cloneable 直接已知子类: SimpleDateFormat 构造方法摘要protected DateFormat() 创建一个新的 DateFormat。

TRUNCATE TABLE (Transact-SQL)

删除表中的所有行&#xff0c;而不记录单个行删除操作。 TRUNCATE TABLE 与没有 WHERE 子句的 DELETE 语句类似&#xff1b;但是&#xff0c;TRUNCATE TABLE 速度更快&#xff0c;使用的系统资源和事务日志资源更少。 与 DELETE 语句相比&#xff0c;TRUNCATE TABLE 具有以下优…

Java Calendar getInstance

public static void main(String[] args) throws ParseException

JavaScript的检测属性、属性特性、枚举属性

JavaScript的检测属性、属性特性、枚举属性 /* 检测属性 检测属性可以通过三种方式 1.通过in运算符 2.通过hasOwnPerperty() 如果给定的属性是继承属性将返回false 3.通过propertyIsEnumerable()&#xff1a;只有检测到的自有属性且这个属性的可枚举性为true时它才返回true,某…