基于webmagic爬虫实现网站图片下载
要使用webmagic爬虫首先要对该框架有一定了解,以下链接是webmagic的详细介绍和入门手册
WebMagic分为四个组件部分
- 1.Downloader
负责从互联网上下载页面,一般不用配置 - 2.PageProcessor
负责解析页面,抽取有用信息,以及发现新的链接。是我们业务实现的主要核心部件 - 3.Scheduler
- 4.Pipeline
负责对页面抽取到的结果和链接进行处理
pom.xml文件所需依赖
<dependencies>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.7.3</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-selenium</artifactId>
<version>0.7.3</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-chrome-driver -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
<version>3.141.59</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-server -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-server</artifactId>
<version>3.141.59</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
ImagePageProcessor
为了实现网站爬虫,我们实现pageprocessor接口对代码进行扩展
public class ImagePageProcessor implements PageProcessor {
@Override
public void process(Page page) {
/* 第一步,从当前页面中发现指定格式链接添加进目标用来进行下一步爬取 */
page.addTargetRequests(page.getHtml().links().regex("https://www\\.****\\.com/.*\\.html").all());
/* 第二步,从当前页面爬取数据 */
// 获取title标题(使用xpah获取指定数据)
String title=page.getHtml().xpath("//h1[@class='title']/text()").toString();
// 判断是否有标题,无标题跳过当前页
if (StringUtils.isEmpty(title)) {
// 跳过当前页,pipeline不会进行输出
page.setSkip(true);
}
//页面title装入page中的resultItems
page.putField("title", title);
//页面中得到指定位置的照片url,即img元素的src属性(使用xpah获取指定数据)
List<String> PicUrl = page.getHtml().xpath("//div[@class='image']/p/span").css("img", "src").all();
//另一种写法
//List<String> PicUrl = page.getHtml().xpath("//div[@class='image']/p/span/img/@src").all();
//照片路径装入page中的resultItems
page.putField("PicUrl",PicUrl);
// console打印当前页面全部内容(测试时用)
//System.out.println(page.getHtml().toString());
}
@Override
public Site getSite() {
return Site.me().setRetryTimes(1).setSleepTime(2000);
}
//程序执行主程序入口
public static void main(String[] args) {
// 当需要使用seleniudownloader类时,加载webmaigc定义的属性参数,自定义指定路径
System.getProperties().setProperty("selenuim_config","E:\\***\\src\\main\\resources\\config.ini");
//spider创建ImagePageProcessor核心对象
Spider.create(new ImagePageProcessor())
//填写带照片的目标网址
.addUrl("https://www.****.com/***/***.html")
//实现console打印page中加入的resultItems元素
.addPipeline(new ConsolePipeline())
/*实现自定义ImagePiple打印page中加入的resultItems元素
*保存至E:\\spider文件下
*/
.addPipeline(new ImagePipeline("E:\\spider"))
//一般不配置,不使用
//.setScheduler(new QueueScheduler())
//设置浏览器核心,当网页使用动态加载图片时需要配置seleniumDownloader
.setDownloader(new SeleniumDownloader("E:\\***\\src\\main\\resources\\chromedriver.exe"))
//设置线程数
.thread(2)
.run();
}
/**
*SeleniumDownloader功能测试
*该代码使用的是chrome浏览器,chromedriver百度自行下载
*chrome和chromedriver版本保持严格匹配,我因为用错版本一直报错
*/
@Test
public void test2(){
System.setProperty("webdriver.chrome.driver",
"E:\\***\\src\\main\\resources\\chromedriver.exe");
// 第二步:初始化驱动
WebDriver driver = new ChromeDriver();
// 第三步:获取目标网页
driver.get("https://blog.csdn.net/qqzjyywx1/article/details/107702834");
// 第四步:解析。以下就可以进行解了。使用webMagic等进行必要的解析。
System.out.println("Page title is: " + driver.getTitle());
System.out.println("Page title is: " + driver.getPageSource());
}
}
ImagePipeline
接下来是执行文件保存功能的ImagePipeline,继承了FilePersistentBase类,实现了Pipeline接口
public class ImagePipeline extends FilePersistentBase implements Pipeline{
private Logger logger = LoggerFactory.getLogger(getClass());
//文件保存路径,之后配置
String savepath=null;
/**
* create a default path"/data/webmagic/"
*/
public ImagePipeline() {
setPath("/data/webmagic/");
}
public ImagePipeline(String path) {
setPath(path);
}
@Override
public void process(ResultItems resultItems, Task task) {
//保存路径我设置为 自定义路径/网站名/标题名/文件名.png
savepath = this.path+task.getUUID()+PATH_SEPERATOR+resultItems.get("title")+ PATH_SEPERATOR;
List<String> PicUrl=resultItems.get("PicUrl");
// 当图片链接存在,生成savepath路径的文件夹,然后下载图片
if(PicUrl.size()>0) {
checkAndMakeParentDirecotry(savepath);
downloadPicture(PicUrl);
}else{
//当图片链接不存在
System.out.println("该page图片链接不存在:" + resultItems.get("title"));
}
}
//下载图片链接方法代码
public void downloadPicture(List<String> urlList) {
URL url = null;
for (int i = 0; i < urlList.size(); i++) {
try {
String[] files = urlList.get(i).split("/");
String name = files[files.length - 1];
url = new URL(urlList.get(i));
File file = new File(savepath + name);
// 文件不存在
if(!file.exists()){
DataInputStream dataInputStream = new DataInputStream(url.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] buffer = new byte[1024 * 50];
int length;
while ((length = dataInputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, length);
}
System.out.println("已经下载:" + savepath + name);
dataInputStream.close();
fileOutputStream.close();
}else{
System.out.println("该文件已存在" + savepath + name);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
System.out.println("下载文件路径不存在:" + url);
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
SeleniumDownloader
当网站没有动态数据,不使用
接下来是seleniumDownloader的config.ini文件配置,从GitHub上下载config.ini的源文件,配置相应的浏览器driver即可
# What WebDriver to use for the tests
#driver=phantomjs
#driver=firefox
driver=chrome
#driver=http://localhost:8910
#driver=http://localhost:4444/wd/hub
# PhantomJS specific config (change according to your installation)
#phantomjs_exec_path=/Users/Bingo/bin/phantomjs-qt5
phantomjs_exec_path=/Users/Bingo/Downloads/phantomjs-1.9.8-macosx/bin/phantomjs
#phantomjs_driver_path=/Users/Bingo/Documents/workspace/webmagic/webmagic-selenium/src/main.js
#phantomjs_driver_loglevel=DEBUG
执行ImagePageProcessor中test2方法,出现下图则配置正确
执行代码
最后执行main方法,实现图片下载
就是上图这种效果
referer防盗链
在对新的网站下载图片时,发现下载的都是同一张图,经过对图片的request请求查看,发现多了一个referer的请求header,据此便可知该网站使用了防盗链技术防止图片被盗,那我们该怎么办呢?
前文中imagePipeline中我们使用
DataInputStream dataInputStream = new DataInputStream(url.openStream());
直接通过url获取图片流信息现在已经是不可以了,需要通过url打开urlConnection,在connection中添加请求头信息,请求header信息可以通过打开浏览器查看图片请求得到,具体代码如下
UrlConnection urlConnection=url.openConnection()
urlConnection.setRequestProperty("referer","xxxurl");
DataInputStream dataInputStream = new DataInputStream(urlConnection.getInputStream());