Java 爬虫实战(一) :爬取斗图社所有 gif

最近开始玩爬虫 , 还是挺有意思的 , 虽然写爬虫一般都是用 Python 比较方便,但是也没有必要为了写爬虫再学一门语言 , 虽然也挺简单,但是还是对 Java 比较习惯,后面可能会学 Python 但是目前还是先用 java 写着玩玩。

目标
斗图社 上所有的图片。
oss

技术选择
Jsoup,最开始看见这个我看成了 Jsonp。我寻思着不是解决跨域的那个么?还能搞爬虫?这么牛掰的么😄
关于 Jsoup 网上也有很多文档 参考资料
还有一些其他的技术比如 httpclient+Xpath 还有 htmlunit 还有 selenium 等等,等以后学了后再来秀一秀 😁


其实我写的第一个爬虫是 copy 的别人的博客上的,不过它爬取的是京东的,我爬的是淘宝的
我也只是想参考下他的结构,但是我感觉他的有些类没什么实际意义。然后我就直接自己写了。
结构如下:

  • boot :爬虫的入口
  • dao :dao
  • handle :封装的处理查询结果集的类(这里没用)
  • model :爬取的数据的模型
  • parse : 解析 html 的类
  • util : Jsoup 工具类和 dao 的工具类和 DB 模板类

首先建立数据模型 DoutuModel

public class DoutuModel {
private Long id;
private String topic;
private String imgUrl;
private String title;

//数据库 id 自增
public DoutuModel(String topic, String imgUrl, String title) {
this.topic = topic;
this.imgUrl = imgUrl;
this.title = title;
}

public String getTopic() {
return topic;
}

public void setTopic(String topic) {
this.topic = topic;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getImgUrl() {
return imgUrl;
}

public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}

主要记录下 parse 类

public static List<DoutuModel> getData2(String url) throws Exception {
//获取的数据,存放在集合中
List<DoutuModel> data = new ArrayList();
//采用 Jsoup 解析
//String url="https://doutushe.com/portal/article/index/id/5gK";
String preurl = "https://doutushe.com";
//取到当前页的 document
//取到内容页的所有图片
int page = 1;
while (true) {
Document doc = JsoupUtils.getHtmlDocument(url);
Elements imgList = doc.select("div[class=col-xs-12 col-sm-8 col-lg-9]").select("img.lazy");
String topic = doc.select("blockquote>p").text();
for (Element imgelement : imgList) {
//异步的坏处体现出来了,这个明显是懒加载,要找就找数据源,直接获取 src 获取不到
//String imgUrl= imgelement.attr("src");
String imgUrl = imgelement.attr("data-original");
String title = imgelement.attr("title");
data.add(new DoutuModel(topic, imgUrl, title));
//System.out.println(topic + ":" + imgUrl + ":" + title);
}
Elements pageUrls = doc.select("ul.pager").select("a");
//爬一页休息 1 秒
if (page % 10 == 0) {
Thread.sleep(1000);
System.out.println("第" + (page/10) + "页采集完 , 暂停-------");
}
//最后一页也有两个按钮。看来要多观察页面
/*if (pageUrls.size() < 2) {
//说明到最后一页了
break;
}*/
url = preurl + pageUrls.get(1).attr("href");
if (!url.matches(preurl + "/portal/article/index/id/[a-zA-Z0-9_]*")) {
break;
}
page++;
}
//返回数据
return data;
}

其实一开始写的一个版本是从主页面爬的先获取每一页的链接,再获取每一页的主题的链接,再获取每个主题下的图片链接,一个三重 for 循环,速度确实比较慢。

后来发现每一页都有下一页的链接。然后就可以直接从页面上爬,两个循环就可以了,但是一开始我判断边界的时候用的是在下面的链接的数量小于 2 但是一开始爬了好长时间结果报错了。 然后我去看了下最后一页发现也有两个链接后面一个是全部的链接。 。

oss
而且和上面的 css 是一样的但是和前面的每一页的链接的后缀不一样,所以就直接用正则表达式匹配 url 的后缀是否匹配
Regex :https://doutushe.com/portal/article/index/id/[a-zA-Z0-9_]* 后面的就是直接匹配任意视频
而最后一页的全部链接是 : https://doutushe.com/portal/index/index 所以就匹配不上直接 break

爬图片的小细节 , 一开始没注意,他这个图片是懒加载的,也就是随着页面用 js 加载的, 直接 src 获取肯定获取不到的,因为 jsoup 是不支持异步的,用 js 操作的东西肯定爬不到。所以只能通过 data-orginal 获取。

github 仓库地址


结果
oss
4243 张图片,后面可以自己写个脚本把图片全部下载到本地或者用迅雷之类的下载工具。


后面会尝试下更多爬虫技术像 htmlunit 这种支持异步的或者 selenium 这种直接操作浏览器的工具