前言
周末闲着没事,准备练习一下爬虫技术,同时也拓展一下视野(欣赏一下美好的东西),练习使用WebMagic爬取美女图片实战。 废话不多说,直接进入主题。
第一步 分析网站数据结构
以妹子网为例:
- 进入首页可以看到分成了几个栏目;
- 进入栏目页面后是美女列表;
- 点击列表中美女图片后进入单个美女的相册页面,页面中显示单张图片,同时带有分页信息;
- 最后两个栏目里是直接分页显示美女图片;
根据上面的分析,基本了解了该网站的基本结构,接下来就可以写爬虫程序了。
第二步 代码实现
1.Maven仓库配置
代码如下:
<properties>
<httpclient.version>4.5.13</httpclient.version>
<webmagic.version>0.7.4</webmagic.version>
<hutool.version>5.6.4</hutool.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>${webmagic.version}</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>${webmagic.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
2.PageProcessor代码
代码如下:
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;
import java.io.File;
import java.net.InetAddress;
public class MzituPageProcessor implements PageProcessor {
/**
* 定义域名
*/
private static final String HOST = "https://www.mzitu.com/";
/**
* 图片保证路径
*/
private final String basePath;
private final Site site = Site.me().setRetryTimes(5).setTimeOut(5000).setSleepTime(200)
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0");
public MzituPageProcessor(String basePath) {
this.basePath = basePath;
}
@Override
public void process(Page page) {
String url = page.getUrl().get();
Document document = Jsoup.parse(page.getHtml().get());
if (url.contains("/page/")) {
// xinggan japan mm 列表页
// 获取列表 #pins > li:nth-child(1) > a
Elements listElements = document.select("#pins > li > a");
listElements.forEach(element -> {
System.out.println("list Element =" + element.attr("href"));
// 进入二级页面
page.addTargetRequest(element.attr("href"));
});
// 获取下一页
Elements pageElements = document.select("div.pagination > div > a.next");
if (null != pageElements) {
System.out.println("下一页 =" + pageElements.get(0).attr("href"));
// 进入下一页
page.addTargetRequest(pageElements.get(0).attr("href"));
}
} else if (url.contains("/#comments")) {
// zipai jiepai 列表页
String relativePath = document.select("div.currentpath").text().replace("当前位置: 妹子图 » ", "");
// #comments > ul > li > div > p > img
Elements imgElements = document.select("#comments > ul > li > div > p > img");
imgElements.forEach(element -> {
String imgName = element.attr("data-original").substring(element.attr("data-original").lastIndexOf("/") + 1);
// 下载图片
HttpUtil.downloadFile(element.attr("data-original"), String.format("%s%s\\%s", basePath, relativePath, imgName));
});
// 获取下一页
Elements pageElements = document.select("#comments > div > a.prev.page-numbers");
if (null != pageElements) {
// 进入下一页
page.addTargetRequest(pageElements.get(0).attr("href"));
}
} else if (page.getUrl().regex("[a-zA-z]+://www.mzitu.com/\\d{1,8}$").match()) {
// 详情页
// 获取文件名
String relativePath = document.select("div.currentpath").text().replace("当前位置: 妹子图 » ", "").replace(" » ", "-").replaceAll("[.,,。?“”]", "-");
String imgUrl = document.select("div.main-image > p > a > img").attr("src");
String imgName = String.format("%s%s", url.replace(HOST, "").replace("/", "-"), imgUrl.substring(imgUrl.lastIndexOf(".")));
// 下载图片
HttpUtil.downloadFile(imgUrl, String.format("%s%s\\%s", basePath, relativePath, imgName));
// 获取分页信息
Elements pageElements = document.select("div.pagenavi > a");
int maxPage = 1, startPage = 2;
for (int i = 0; i < pageElements.size(); i++) {
Element pageElement = pageElements.get(i);
if (i > 1 && NumberUtil.isNumber(pageElement.attr("href").substring(pageElement.attr("href").lastIndexOf("/") + 1))) {
int nowPage = Integer.parseInt(pageElement.attr("href").substring(pageElement.attr("href").lastIndexOf("/") + 1));
if (nowPage > maxPage) {
maxPage = nowPage;
}
}
}
// 检查是否已经下载了,检查文件夹是否存,检查文件夹中图片数量
String imgPath = String.format("%s%s", basePath, relativePath);
if (FileUtil.exist(imgPath)) {
// 文件夹已存在
startPage = 1 + new File(imgPath).listFiles().length;
} else {
// 文件夹不存在
startPage = 2;
}
for (int i = startPage; i <= maxPage; i++) {
System.out.println("详情页 下一页 =" + url + "/" + i);
page.addTargetRequest(url + "/" + i);
}
} else {
// 详情页
// 获取文件名
String relativePath = document.select("div.currentpath").text().replace("当前位置: 妹子图 » ", "").replace(" » ", "-").replaceAll("[.,,。?“”]", "-");
String imgUrl = document.select("div.main-image > p > a > img").attr("src");
String imgName = String.format("%s%s", url.replace(HOST, "").replace("/", "-"), imgUrl.substring(imgUrl.lastIndexOf(".")));
// 下载图片
HttpUtil.downloadFile(imgUrl, String.format("%s%s\\%s", basePath, relativePath, imgName));
}
}
@Override
public Site getSite() {
return site;
}
}
3.主程序
代码如下:
@SneakyThrows
public static void main(String[] args) {
Spider.create(new MzituPageProcessor("D:\\mzitu\\", localAddress)).addUrl(
"https://www.mzitu.com/xinggan/page/1",
"https://www.mzitu.com/japan/page/1",
"https://www.mzitu.com/mm/page/1",
"https://www.mzitu.com/zipai/#comments",
"https://www.mzitu.com/jiepai/#comments"
)
.thread(3)
.run();
}
第三步 打完收工
接下来就尽情欣赏吧!当然要考虑人家网站的承受能力,大家在满足自己的诉求时也要考虑人家网站的承受能力,下载时候不要太快,保障其他人也能够正常使用,独乐乐不如众乐乐是吧!