网上冲浪🏂,经常会遇到想要批量保存的沙雕表情包、Coser 套图、二次元图片等素材资源......那什么能实现快捷有效的批量下载呢?爬虫程序。
说起爬虫,我的第一反应就是 Python
语言,然后想到它的相关工具:Scrapy、Selenium......但是我没有系统学习过 Python
(学过一点皮毛),而且对它也不怎么感兴趣啊😟。
对于不会 Python
爬虫的我,难不成只能靠鼠标在目标网页上点击右键 -> “将图像另存为”?效率低下。
幸好我找到了一款优秀的浏览器扩展插件——图片助手,商店中显示它的用户高达 30W+,而且评分不低!
简单来说,它可以快速提取网页图片并批量下载,标签页中将整个页面的图片都以缩略图的形式展示出来,并且可以对缩略图添加筛选条件(过滤一些尺寸较小的图片)。
使用的方法很简单,启用后,在目标网页上点击扩展的“提取网页图片”功能即可。
以哔哩哔哩首页为例,提取后可以看到图片的格式类型、分辨率大小、筛选条件等设置,点击选中下载即可。
既然我不会 Python
的爬虫,那就选 Java
的吧(大聪明)。
WebMagic
是一个简单灵活的 Java 爬虫框架,官方中文文档:WebMagic in Action。
使用 Maven
来安装 WebMagic
,在 Spring Boot
项目中引入。
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.7.5</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.5</version>
</dependency>
作为一个 SP(我不老),爬取的网站一般都是那些写真套图网站,例如:美之图。
例行分析这个网站,每次按 F12
打开浏览器的控制台,都显示一个“已在调试程序中暂停
”的提示,这个有点碍眼,有两种方法可以解决。
Ctrl
+ F8
),再点击“前进”(F8
)。点击“写真馆”,进入“https://mmzztt.com/photo/”,点击“检查元素”,开始寻找规律。
查看多张封面图的 src
属性后,就可以得知链接都是如下的形式:
域名地址就不用说了,封面图的文件名都是 960.jpg
,而变的只是 thumb
后面的参数,这个变化的参数就是对应的专辑。
也就是说,在“https://mmzztt.com/photo/”后加上对应的专辑数字,就可以进入对应的专辑套图中去。举个例子,点击“https://mmzztt.com/photo/51490”就会进入专辑的内容页面,而这个数字“51490”可以从封面图中获取。
遗憾的是,并不能直接看出具体图片的命名方式,因为不可能采取从“960.jpg”递减到“001.jpg”这么直白朴素(愚蠢)的命名方案。
点击进入专辑“51490”,继续检查图片的来源,分析规律:
从这个能看出来:前面的域名地址不变,中间是变化的有规律的日期格式——“2021/12/13”,后面的是变化的图片命名——可能固定的首字母 + “当前的图片数(第 X 张)” + 随机的末字母。
一开始我以为是首字母固定是 b
,直到另一套是首字母是 a
,但随机又看了十几套,基本都是首字母 a
或 b
,还没看到首字母为 c
的(可能是我没发现,或者就是没有)。
首末的随机字母是不是有某种对应的规律(算法生成),暂时看不出来,有可能是真的随机——外层循环随机使用字母 a
或 b
,内层循环从字母 a
到字母 z
随机挑选。
当我连续点击套图的“下一张”,到第 16 张时,网站就会自动跳转到下载 APP 的页面。后来这个现象又没了。
根据官方文档的说明,PageProcessor
的定制分为三个部分,分别是爬虫的配置、页面元素的抽取和链接的发现。
针对“写真馆”界面的套图进行抽取元素,这部分需要懂一点 XPath 规则以及正则表达式。
为了简单起见,直接抽取套图专辑的文字标题以及首页图的链接。
@Override
public void process(Page page) {
// 列表页
if (page.getUrl().regex(URL_LIST).match()) {
/**
* 运行时抛出异常:Links can not apply to plain text.
* Please check whether you use a previous xpath with attribute select (/@href etc).
* 原因:使用过正则匹配后就不能使用 XPath 抽取了,因为正则取出来的没有办法保证是 Html,当做是是纯文本处理了
*/
page.addTargetRequests(page.getHtml().xpath("//a[@class='uk-inline']/@href").regex(URL_ARTICLE).all());
page.addTargetRequests(page.getHtml().links().regex(URL_LIST).all());
} else {
// 标题名
String imgTitle = page.getHtml().xpath("//h1[@class='uk-article-title uk-text-truncate']/text()").toString();
// 套图第一张的链接
String imgUrl = page.getHtml().xpath("//figure[@class='uk-inline']/img/@src").toString();
MztImage image = new MztImage();
image.setId(counter.get());
atomicAdd();
image.setName(imgTitle);
image.setAvatar(imgUrl);
page.putField("result", image);
}
}
Pipeline
其实就是将 PageProcessor
抽取的结果,继续进行了处理,主要用于抽取结果的保存。WebMagic 中已经提供了将结果输出到控制台、保存到文件和 JSON 格式保存的几个 Pipeline ,我们也可以自定义 Pileline 实现想要的功能。
自定义实现:将文字标题作为 TXT 文本文件的文件名,首页图链接作为文本中的内容。
import com.example.common.MztImage;
import com.example.tool.HttpFileTool;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
/**
* @author yyt
* @date 2021年12月21日 19:30
*/
public class DownloadImgPipeline implements Pipeline {
/**
* 图片保存的路径
*/
private static final String BASE_IMG_FILE_PATH = "D:" + File.separator + "Desktop" + File.separator + "MZT" + File.separator;
@Override
public void process(ResultItems resultItems, Task task) {
MztImage newImage = resultItems.get("result");
if(newImage != null){
// 根据标题创建对应的文件
String dirStr = BASE_IMG_FILE_PATH + newImage.getName() + ".txt";
File file = new File(dirStr);
if (!file.getParentFile().exists()){
// 包含父文件夹
file.getParentFile().mkdirs();
}
// 根据链接下载图片
String imageUrl = newImage.getAvatar();
// 获取图片的文件名
// String fileName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
try {
// 下载图片
// HttpFileTool.download(imageUrl, dirStr + imageUrl);
Writer writer = new FileWriter(file, true);
writer.write(imageUrl + "\r\n");
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
System.out.println("DownloadImgPipeline 运行出现异常...");
}
}
}
}
因为下载保存图片会出现 403
错误,所以改为保存图片的链接。
java.io.IOException: Server returned HTTP response code: 403 for URL: https://p.iimzt.com/2021/12/20b01c.jpg
查看了一下网页元素,不出意外应该是“同源策略”导致的——<img>
标签中设置了独立的请求策略 referrerpolicy="origin"
。而 WebMagic 也有对应的方案:通过设置 Site 对象来进行配置编码、HTTP 头、超时时间、重试策略等、代理等信息,那可以考虑使用 Fiddler
抓包,再使用 addHeader(String, String)
方法和 addCookie(String, String)
方法伪装。
但经过测试,依然不行(我太菜了)。😫
开始漫长的等待。⏳⏳⏳
打开文件夹一看,爬取成功了!!!🎉🎉🎉