Java简单爬虫
这篇文章主要是记录近期学习的内容和自己的一些理解,可能不是很全面或者不够严谨。欢迎大家讨论学习。
了解过爬虫的应该都知道,爬虫的原理是获取网页代码,分析其结构,通过URL等资源定位,将目标与我们程序建立连接,最后操作目标资源或下载到本地。
以下是我对某漫画网站写的java爬虫程序,主要功能是将网页上的漫画资源下载到本地。如有不便请联系删除。
这次的简单爬虫,用到了一个jsoup。导包:
<dependencies>
<!-- 爬视频可以用tika-->
<!-- 解析网页-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
</dependencies>
首先是漫画(Book)类:
package com.xuan.pojo;
import java.util.HashMap;
import java.util.Map;
//封装漫画类
public class Book {
private String bid;
private String bname;
private Map<String,String> chapter=new HashMap<String, String>();//用以记录某漫画的目录章节名称及其id
public String getBid() {
return bid;
}
public void setBid(String bid) {
this.bid = bid;
}
public void setChapter(String chaptername,String chapterid){
this.chapter.put(chaptername,chapterid);
}
public Map<String,String> getChapter(){
return this.chapter;
}
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
@Override
public String toString() {
return "Book{" +
"bid='" + bid + '\'' +
", bname='" + bname + '\'' +
", chapter=" + chapter +
'}';
}
}
然后是最重要的工具类(这里我把网站域名改为*,不影响程序的阅读与理解):
package com.xuan.utils;
import com.xuan.pojo.Book;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
public class HtmlParseUtil {
//通过id获取该漫画的简单信息和目录。并返回Book对象。
public Book bookInfo(String bid){
//
Book book=null;
try {
//目标网页的URL
String url="https://******/"+bid+"/";
//jsoup可以通过目标网页的URL返回一个DOM,从而像操作js代码一样,操作对象。
Document document = Jsoup.parse(new URL(url), 30000);
//获取漫画书的名字并创建对象
book = new Book();
//通过class找到使用过该class的所有对象,可能有多个用.get()可以按顺序获取,从零开始。
// 此处的class用以修饰当前漫画的名字有且仅有一个。
Elements ebname = document.getElementsByClass("comic-title j-comic-title");
book.setBid(bid);
book.setBname(ebname.text());
//同理获取当前漫画的目录列表的对象,此处有且仅有一个。
Elements el = document.getElementsByClass("chapter__list-box clearfix hide");
//获取列表对象下的所有li对象。
Elements elements = el.get(0).getElementsByTag("li");
//遍历所有li对象获取目录中每一章的名字及其id,并赋值到book的chapter中。
for (Element e:elements){
String chapterid=e.getElementsByTag("a").attr("data-chapterid");
String chaptername=e.getElementsByTag("a").text();
book.setChapter(chaptername,chapterid);
}
} catch (IOException e) {
e.printStackTrace();
}
return book;
}
//通过输入对象,获取漫画对象的信息,并下载到本地。
// 该方法的主要作用是整理出每一章对应的新的URL,下载调用了重载的方法。
public void download(Book book) throws IOException {
String bid=book.getBid();
String bname=book.getBname();
Map<String, String> chapter = book.getChapter();
//遍历对象中的目录信息chapter,并把每一章下载
String key = null;
String value = null;
Iterator it = chapter.entrySet().iterator();
while (it.hasNext()){
Map.Entry entry = (Map.Entry) it.next();
key = (String) entry.getKey();
value = (String) entry.getValue();
System.out.println("key:" + key + "---" + "value:" + value);
//某一章的地址
URL url=new URL( "https://******/"+bid+"/"+value+".html");
download(url,bname,key);
}
}
//通过URL 下载资源,并存到目标目录中(bname和chaptername用于拼接下载的目标路径)。
public void download(URL url,String bname,String chaptername) throws IOException {
//通过url获取当前章的DOM
Document document = Jsoup.parse(url, 30000);
//获取图片列表的URL
//此处的的elements是每张图片的父级容器的和。
Elements elements = document.getElementsByClass("rd-article-wr clearfix").get(0).getElementsByClass("rd-article__pic hide");
//打印该章节有多少张目标图片
System.out.println(elements.size());
//循环下载。
for (int i=0;i<elements.size();i++){
//获取图片资源的有效地址
String datasrc = elements.get(i).getElementsByTag("img").attr("data-src");
URL dataurl=new URL(datasrc);
//与资源建立链接
HttpURLConnection urlConnection = (HttpURLConnection) dataurl.openConnection();
//获取当前图片的输入流
InputStream is = urlConnection.getInputStream();
//指定文件输出路径
File file = new File("cartoon/"+bname+"/"+chaptername+"/"+(i+1)+".jpg");
//判断文件路径是否存在,不存在就新建。
if(!file.getParentFile().exists()){
boolean result = file.getParentFile().mkdirs();
if(!result){
throw new RuntimeException("创建文件路径失败");
}
}
//初始化输出流
FileOutputStream fos = new FileOutputStream(file);
//3.操作流
byte[] buffer=new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
//4.关闭流
fos.close();
is.close();
urlConnection.disconnect();
}
}
}
运行主程序:
package com.xuan;
import com.xuan.pojo.Book;
import com.xuan.utils.HtmlParseUtil;
import java.io.IOException;
import java.net.URL;
public class Application {
public static void main(String[] args) {
HtmlParseUtil htmlParseUtil = new HtmlParseUtil();
Book book = htmlParseUtil.bookInfo("215815");
System.out.println(book);
try {
htmlParseUtil.download(book);
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出结果:
Book{bid='215815', bname='进击的海王', chapter={第2话 老婆这是我送你的见面礼=921125, 番外 双节快乐!=921224, 预告 论海王的实战经验=919690, 第1话 妹子都是我的!=921067, 第3话 你敢动我老婆?=922853}}
key:第2话 老婆这是我送你的见面礼---value:921125
50
key:番外 双节快乐!---value:921224
7
key:预告 论海王的实战经验---value:919690
15
key:第1话 妹子都是我的!---value:921067
54
key:第3话 你敢动我老婆?---value:922853
57
输出的文件路径:
以上便是所有的代码以及运行结果,至于为什么用书的id而不是名字,这其实无所谓,只要能创建出book对象,已经构建出后续的URL,并没有太大影响。程序写的稍微简陋以及偷懒,仅供大家参考。