最近在找工作,在这个过程中我感到很迷茫,投了很多简历,被查看的却很少,其中也有到现场去面试,结果也很不理想(╥╯^╰╥)。
哈哈,跑题了,我在看之前所做的项目时,在我的收藏夹中看到了以前收藏的有关爬虫的文章,点开后又重新学习了一下。
下面是这两篇文章的链接
java实现网络爬虫:https://www.cnblogs.com/1996swg/p/7355577.html
Jsoup教程:https://www.jianshu.com/p/fd5caaaa950d
接下来,我通过Jsoup来实现爬取彼岸桌面里面的图片进行爬虫学习!!!
我用的开发工具是IDEA,jdk是1.7版本,项目结构大致如下所示:
一、页面分析
首先来分析一下彼岸桌面的网页的结构:
我们第一个看到的是网站的域名为http://www.netbian.com/,它有如上所示的分类,我们尝试着点开一些分类去看一下他的链接。
通过点击每个分类,发现不同的分类下,地址栏显示为域名后面拼接这对应分类的拼音,但在分类为王者荣耀之后的拼接的确是“s/分类拼音”。这样我们可以创建一个枚举类,将所有分类集中管理。在common包下创建一个Kind枚举类:
package com.asahi.common;
/**
* 分类的枚举
*/
public enum Kind {
RILI("rili"), DONGMAN("dongman"), FENGJING("fengjing"), MEINV("meinv"), YOUXI("youxi"), YINGSHI("yingshi"),
DONGTAI("dongtai"), WEIMEI("weimei"), SHEJI("sheji"), KEAI("keai"), QICHE("qiche"), HUAHUI("huahui"),
DONGWU("dongwu"), JIERI("jieri"), RENWU("renwu"), MEISHI("meishi"), SHUIGUO("shuiguo"), JIANZHU("jianzhu"),
TIYU("tiyu"), JUNSHI("junshi"), FEIZHULIU("feizhuliu"), QITA("qita"), WANGZHERONGYAO("s/wangzherongyao"), HUYAN("s/huyan"), LOL("s/lol");
String kind;
Kind(String kind) {
this.kind = kind;
}
public static boolean contains(String test) {
for (Kind c : Kind.values()) {
if (c.kind.equals(test)) {
return true;
}
}
return false;
}
}
这里我添加了一个比较的方法供之后判断输入的分类名是否包含在这些分类里面。
接下来我们在分析分类面的展示情况,以美女分类页面为例(●´∀`●),最下边有分页,如果只获取这个页面的图片并不能获取所有美女图,我们还需要点击每一个分页,从分页中获取所有的图片。通过分析发现,第一页的链接是在原有链接基础上拼接“/index.htm”,从第二页之后拼接的是“/index_页号.htm”。
这样我们只需要获取总页数在依次遍历拼接就可以了,现在的问题是如何获取总页数,我一开始的想法是获取分页中“共167页”这个标签后再只保留数字就可以个,但发现运行后获取不到该元素节点,经过排查了解到这个标签是通过js生成的,于是我转换了思路,通过获取最后一个页号来得到一共分了多少页
Document root_doc = Jsoup.connect("http://www.netbian.com/" + kind + "/").get();
Elements els = root_doc.select("#main .page a");
//这里els.eq(els.size() - 2的原因是后边确定按钮用的是a标签要去掉,再去掉一个“下一页”标签
Integer page = Integer.parseInt(els.eq(els.size() - 2).text());
分类页中图片所在的标签结构为:
分类页面下的图片不是我们想要的,我们想要的是点击进去详细页的高清大图,所以需要获取a标签的链接,再从这个链接中获取真正想要的图片。
详细页中图片所在的标签结构为:
二、代码实现
到这里分类页分析的差不多了,我们通过代码来进行获取图片。首先导入Jsoup的jar包:jsoup-1.12.1.jar,如果采用Maven请导入下边的依赖。
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
在utils创建JsoupPic类,并添加getPic方法,代码如下:
public static void getPic(String kind) throws Exception {
//get请求方式进行请求
Document root_doc = Jsoup.connect("http://www.netbian.com/" + kind + "/").get();
//获取分页标签,用于获取总页数
Elements els = root_doc.select("#main .page a");
Integer page = Integer.parseInt(els.eq(els.size() - 2).text());
for (int i = 1; i < page; i++) {
Document document = null;
//这里判断的是当前页号是否为1,如果为1就不拼页号,否则拼上对应的页号
if (i == 1) {
document = Jsoup.connect("http://www.netbian.com/" + kind + "/index.htm").get();
} else {
document = Jsoup.connect("http://www.netbian.com/" + kind + "/index_" + i + ".htm").get();
}
//获取每个分页链接里面a标签的链接,进入链接页面获取当前图拼的大尺寸图片
Elements elements = document.select("#main .list li a");
for (Element element : elements) {
String href = element.attr("href");
String picUrl = "http://www.netbian.com" + href;
Document document1 = Jsoup.connect(picUrl).get();
Elements elements1 = document1.select(".endpage .pic p a img");
//获取所有图片的链接
System.out.println(elements1);
}
}
}
在分类页中有一个隐藏的问题图片:
正常的图片链接都是以“/”开头,以“.htm”结尾,而每个分类下的第三张图片的链接都是“http://pic.netbian.com/”,如果不过滤的话会报如下错误:
所以这里必须要判断一下:
Elements elements = document.select("#main .list li a");
for (Element element : elements) {
String href = element.attr("href");
//判断是否是以“/”开头
if (href.startsWith("/")) {
String picUrl = "http://www.netbian.com" + href;
Document document1 = Jsoup.connect(picUrl).get();
Elements elements1 = document1.select(".endpage .pic p a img");
System.out.println(elements1);
}
}
到这里,页面就已经分析好了,问题基本上已经解决了,接下来我们需要将图片存到我们的系统里,这里我将图片保存到我的电脑桌面上,并按照分类来存储图片。
首先是要获取桌面路径,在utils包下创建Download类,添加getDesktop方法,代码如下:
public static File getDesktop(){
FileSystemView fsv = FileSystemView.getFileSystemView();
File path=fsv.getHomeDirectory();
return path;
}
接着我们再该类中添加下载图片的方法:
//urlPath为网络图片的路径,savePath为要保存的本地路径(这里指定为桌面下的images文件夹)
public static void download(String urlPath,String savePath) throws Exception {
// 构造URL
URL url = new URL(urlPath);
// 打开连接
URLConnection con = url.openConnection();
//设置请求超时为5s
con.setConnectTimeout(5*1000);
// 输入流
InputStream is = con.getInputStream();
// 1K的数据缓冲
byte[] bs = new byte[1024];
// 读取到的数据长度
int len;
// 输出的文件流
File sf=new File(savePath);
int randomNo=(int)(Math.random()*1000000);
String filename=urlPath.substring(urlPath.lastIndexOf("/")+1,urlPath.length());//获取服务器上图片的名称
filename=new java.text.SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())+randomNo+filename;//时间+随机数防止重复
OutputStream os = new FileOutputStream(sf.getPath()+"\\"+filename);
// 开始读取
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
// 完毕,关闭所有链接
os.close();
is.close();
}
写好后,我们再完善一下JsouPic中的getPic方法。
public static void getPic(String kind) throws Exception {
//get请求方式进行请求
Document root_doc = Jsoup.connect("http://www.netbian.com/" + kind + "/").get();
//获取分页标签,用于获取总页数
Elements els = root_doc.select("#main .page a");
Integer page = Integer.parseInt(els.eq(els.size() - 2).text());
for (int i = 1; i < page; i++) {
Document document = null;
//这里判断的是当前页号是否为1,如果为1就不拼页号,否则拼上对应的页号
if (i == 1) {
document = Jsoup.connect("http://www.netbian.com/" + kind + "/index.htm").get();
} else {
document = Jsoup.connect("http://www.netbian.com/" + kind + "/index_" + i + ".htm").get();
}
File desktop = Download.getDesktop();
Download.checkPath(desktop.getPath() + "\\images\\" + kind);
//获取每个分页链接里面a标签的链接,进入链接页面获取当前图拼的大尺寸图片
Elements elements = document.select("#main .list li a");
for (Element element : elements) {
String href = element.attr("href");
if (href.startsWith("/")) {
String picUrl = "http://www.netbian.com" + href;
Document document1 = Jsoup.connect(picUrl).get();
Elements elements1 = document1.select(".endpage .pic p a img");
Download.download(elements1.attr("src"), desktop.getPath() + "\\images\\" + kind);
}
}
}
}
在Download类中,我添加了checkPath方法,用于判断目录是否存在,不存在就创建一个。
public static void checkPath(String savePath) throws Exception {
File file = new File(savePath);
if (!file.exists()){
file.mkdirs();
}
}
最后在mainapp包内创建PullPic类,并添加主方法。
package com.asahi.mainapp;
import com.asahi.common.Kind;
import com.asahi.common.PrintLog;
import com.asahi.utils.JsoupPic;
import java.util.Scanner;
public class PullPic {
public static void main(String[] args) throws Exception {
new PullPic().downloadPic();
}
public void downloadPic() throws Exception {
System.out.println("启动程序>>\n请输入所爬取的分类:");
Scanner scanner = new Scanner(System.in);
String kind = scanner.next();
while(!Kind.contains(kind)){
System.out.println("分类不存在,请重新输入:");
kind = scanner.next();
}
System.out.println("分类输入正确!");
System.out.println("开始下载>>");
JsoupPic.getPic(kind);
}
}
三、成果展示
最终的运行结果如下:
最终的代码已上传到我的github中,点击“我的github”进行查看。
在学习Java爬虫的过程中,我收获了很多,一开始做的时候确实遇到了很多困难,这次写的获取图片也是最基础的,还可以继续深入。本来我想写一个通过多线程来获取图片来着,也尝试着去写了一下,越写越跑偏,暂时先放着不处理吧,等以后有时间再来弄,我想问题应该不大,只是考虑的东西有很多。希望大家多多指点不足,有哪些需要改进的地方,我也好多学习学习๑乛◡乛๑。