您好,欢迎访问一九零五行业门户网

怎么使用Java爬虫批量爬取图片

爬取思路对于这种图片的获取,其实本质上就是就是文件的下载(httpclient)。但是因为不只是获取一张图片,所以还会有一个页面解析的处理过程(jsoup)。
jsoup:解析html页面,获取图片的链接。
httpclient:请求图片的链接,保存图片到本地。
具体步骤首先进入首页分析,主要有以下几个分类(这里不是全部分类,但是这几个也足够了,这只是学习技术而已。),我们的目标就是获取每个分类下的图片。
这里来分析一下网站的结构,我这里就简单一点吧。 下面这张图片是大致的结构,这里选取一个分类标签进行说明。 一个分类标签页含有多个标题页,然后每个标题页含有多个图片页。(对应标题页的几十张图片)
具体代码导入项目依赖jar包坐标或者直接下载对应的jar包,导入项目也可。
<dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpclient</artifactid> <version>4.5.6</version></dependency> <dependency> <groupid>org.jsoup</groupid> <artifactid>jsoup</artifactid> <version>1.11.3</version></dependency>
实体类 picture 和 工具类 headerutil实体类:把属性封装成一个对象,这样调用方便一点。
package com.picture;public class picture { private string title; private string url; public picture(string title, string url) { this.title = title; this.url = url; } public string gettitle() { return this.title; } public string geturl() { return this.url; }}
工具类:不断变换 ua(我也不知道有没有用,不过我是使用自己的ip,估计用处不大了)
package com.picture;public class headerutil { public static string[] headers = { "mozilla/5.0 (windows nt 6.3; wow64) applewebkit/537.36 (khtml, like gecko) chrome/39.0.2171.95 safari/537.36", "mozilla/5.0 (macintosh; intel mac os x 10_9_2) applewebkit/537.36 (khtml, like gecko) chrome/35.0.1916.153 safari/537.36", "mozilla/5.0 (windows nt 6.1; wow64; rv:30.0) gecko/20100101 firefox/30.0", "mozilla/5.0 (macintosh; intel mac os x 10_9_2) applewebkit/537.75.14 (khtml, like gecko) version/7.0.3 safari/537.75.14", "mozilla/5.0 (compatible; msie 10.0; windows nt 6.2; win64; x64; trident/6.0)", "mozilla/5.0 (windows; u; windows nt 5.1; it; rv:1.8.1.11) gecko/20071127 firefox/2.0.0.11", "opera/9.25 (windows nt 5.1; u; en)", "mozilla/4.0 (compatible; msie 6.0; windows nt 5.1; sv1; .net clr 1.1.4322; .net clr 2.0.50727)", "mozilla/5.0 (compatible; konqueror/3.5; linux) khtml/3.5.5 (like gecko) (kubuntu)", "mozilla/5.0 (x11; u; linux i686; en-us; rv:1.8.0.12) gecko/20070731 ubuntu/dapper-security firefox/1.5.0.12", "lynx/2.8.5rel.1 libwww-fm/2.14 ssl-mm/1.4.1 gnutls/1.2.9", "mozilla/5.0 (x11; linux i686) applewebkit/535.7 (khtml, like gecko) ubuntu/11.04 chromium/16.0.912.77 chrome/16.0.912.77 safari/535.7", "mozilla/5.0 (x11; ubuntu; linux i686; rv:10.0) gecko/20100101 firefox/10.0 " };}
下载类多线程实在是太快了,再加上我只有一个ip,没有代理ip可以用(我也不太了解),使用多线程被封ip是很快的。
package com.picture;import java.io.bufferedoutputstream;import java.io.file;import java.io.fileoutputstream;import java.io.ioexception;import java.io.outputstream;import java.util.random;import org.apache.http.httpentity;import org.apache.http.client.clientprotocolexception;import org.apache.http.client.methods.closeablehttpresponse;import org.apache.http.client.methods.httpget;import org.apache.http.impl.client.closeablehttpclient;import org.apache.http.util.entityutils;import com.m3u8.httpclientutil;public class singlepicturedownloader { private string referer; private closeablehttpclient httpclient; private picture picture; private string filepath; public singlepicturedownloader(picture picture, string referer, string filepath) { this.httpclient = httpclientutil.gethttpclient(); this.picture = picture; this.referer = referer; this.filepath = filepath; } public void download() { httpget get = new httpget(picture.geturl()); random rand = new random(); //设置请求头 get.setheader("user-agent", headerutil.headers[rand.nextint(headerutil.headers.length)]); get.setheader("referer", referer); system.out.println(referer); httpentity entity = null; try (closeablehttpresponse response = httpclient.execute(get)) { int statuscode = response.getstatusline().getstatuscode(); if (statuscode == 200) { entity = response.getentity(); if (entity != null) { file picfile = new file(filepath, picture.gettitle()); try (outputstream out = new bufferedoutputstream(new fileoutputstream(picfile))) { entity.writeto(out); system.out.println("下载完毕:" + picfile.getabsolutepath()); } } } } catch (clientprotocolexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { try { //关闭实体,关于 httpclient 的关闭资源,有点不太了解。 entityutils.consume(entity); } catch (ioexception e) { e.printstacktrace(); } } }}
这是获取 httpclient 连接的工具类,避免频繁创建连接的性能消耗。(但是因为我这里是使用单线程来爬取,所以用处就不大了。我就是可以只使用一个httpclient连接来爬取,这是因为我刚开始是使用多线程来爬取的,但是基本获取几张图片就被禁掉了,所以改成单线程爬虫。所以这个连接池也就留下来了。)
package com.m3u8;import org.apache.http.client.config.requestconfig;import org.apache.http.impl.client.closeablehttpclient;import org.apache.http.impl.client.httpclients;import org.apache.http.impl.conn.poolinghttpclientconnectionmanager;public class httpclientutil { private static final int time_out = 10 * 1000; private static poolinghttpclientconnectionmanager pcm; //httpclient 连接池管理类 private static requestconfig requestconfig; static { requestconfig = requestconfig.custom() .setconnectionrequesttimeout(time_out) .setconnecttimeout(time_out) .setsockettimeout(time_out).build(); pcm = new poolinghttpclientconnectionmanager(); pcm.setmaxtotal(50); pcm.setdefaultmaxperroute(10); //这里可能用不到这个东西。 } public static closeablehttpclient gethttpclient() { return httpclients.custom() .setconnectionmanager(pcm) .setdefaultrequestconfig(requestconfig) .build(); }}
最重要的类:解析页面类 picturespiderpackage com.picture;import java.io.file;import java.io.ioexception;import java.util.list;import java.util.map;import java.util.stream.collectors;import org.apache.http.httpentity;import org.apache.http.client.clientprotocolexception;import org.apache.http.client.methods.closeablehttpresponse;import org.apache.http.client.methods.httpget;import org.apache.http.impl.client.closeablehttpclient;import org.apache.http.util.entityutils;import org.jsoup.jsoup;import org.jsoup.nodes.document;import org.jsoup.select.elements;import com.m3u8.httpclientutil;/** * 首先从顶部分类标题开始,依次爬取每一个标题(小分页),每一个标题(大分页。) * */public class picturespider { private closeablehttpclient httpclient; private string referer; private string rootpath; private string filepath; public picturespider() { httpclient = httpclientutil.gethttpclient(); } /** * 开始爬虫爬取! * * 从爬虫队列的第一条开始,依次爬取每一条url。 * * 分页爬取:爬10页 * 每个url属于一个分类,每个分类一个文件夹 * */ public void start(list<string> urllist) { urllist.stream().foreach(url->{ this.referer = url; string dirname = url.substring(22, url.length()-1); //根据标题名字去创建目录 //创建分类目录 file path = new file("d:/dragonfile/dbc/mzt/", dirname); //硬编码路径,需要用户自己指定一个 if (!path.exists()) { path.mkdir(); rootpath = path.tostring(); } for (int i = 1; i <= 10; i++) { //分页获取图片数据,简单获取几页就行了 this.page(url + "page/"+ 1); } }); } /** * 标题分页获取链接 * */ public void page(string url) { system.out.println("url:" + url); string html = this.gethtml(url); //获取页面数据 map<string, string> picmap = this.extracttitleurl(html); //抽取图片的url if (picmap == null) { return ; } //获取标题对应的图片页面数据 this.getpicturehtml(picmap); } private string gethtml(string url) { string html = null; httpget get = new httpget(url); get.setheader("user-agent", "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/60.0.3100.0 safari/537.36"); get.setheader("referer", url); try (closeablehttpresponse response = httpclient.execute(get)) { int statuscode = response.getstatusline().getstatuscode(); if (statuscode == 200) { httpentity entity = response.getentity(); if (entity != null) { html = entityutils.tostring(entity, "utf-8"); //关闭实体? } } else { system.out.println(statuscode); } } catch (clientprotocolexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } return html; } private map<string, string> extracttitleurl(string html) { if (html == null) { return null; } document doc = jsoup.parse(html, "utf-8"); elements pictures = doc.select("ul#pins > li"); //不知为何,无法直接获取 a[0],我不太懂这方面的知识。 //那我就多处理一步,这里先放下。 elements picturea = pictures.stream() .map(pic->pic.getelementsbytag("a").first()) .collect(collectors.tocollection(elements::new)); return picturea.stream().collect(collectors.tomap( pic->pic.getelementsbytag("img").first().attr("alt"), pic->pic.attr("href"))); } /** * 进入每一个标题的链接,再次分页获取图片的链接 * */ private void getpicturehtml(map<string, string> picmap) { //进入标题页,在标题页中再次分页下载。 picmap.foreach((title, url)->{ //分页下载一个系列的图片,每个系列一个文件夹。 file dir = new file(rootpath, title.trim()); if (!dir.exists()) { dir.mkdir(); filepath = dir.tostring(); //这个 filepath 是每一个系列图片的文件夹 } for (int i = 1; i <= 60; i++) { string html = this.gethtml(url + "/" + i); if (html == null) { //每个系列的图片一般没有那么多, //如果返回的页面数据为 null,那就退出这个系列的下载。 return ; } picture picture = this.extractpictureurl(html); system.out.println("开始下载"); //多线程实在是太快了(快并不是好事,我改成单线程爬取吧) singlepicturedownloader downloader = new singlepicturedownloader(picture, referer, filepath); downloader.download(); try { thread.sleep(1500); //不要爬的太快了,这里只是学习爬虫的知识。不要扰乱别人的正常服务。 system.out.println("爬取完一张图片,休息1.5秒。"); } catch (interruptedexception e) { e.printstacktrace(); } } }); } /** * 获取每一页图片的标题和链接 * */ private picture extractpictureurl(string html) { document doc = jsoup.parse(html, "utf-8"); //获取标题作为文件名 string title = doc.getelementsbytag("h3") .first() .text(); //获取图片的链接(img 标签的 src 属性) string url = doc.getelementsbyattributevalue("class", "main-image") .first() .getelementsbytag("img") .attr("src"); //获取图片的文件扩展名 title = title + url.substring(url.lastindexof(".")); return new picture(title, url); }}
启动类 bootstrap这里有一个爬虫队列,但是我最终连第一个都没有爬取完,这是因为我计算失误了,少算了两个数量级。但是,程序的功能是正确的。
package com.picture;import java.util.arraylist;import java.util.arrays;import java.util.list;/** * 爬虫启动类 * */public class bootstrap { public static void main(string[] args) { //反爬措施:ua、refer 简单绕过就行了。 //refer https://www.mzitu.com //使用数组做一个爬虫队列 string[] urls = new string[] { "https://www.mzitu.com/xinggan/", "https://www.mzitu.com/zipai/" }; // 添加初始队列,启动爬虫 list<string> urllist = new arraylist<>(arrays.aslist(urls)); picturespider spider = new picturespider(); spider.start(urllist); }}
爬取结果
注意事项这里有一个计算失误,代码如下:
for (int i = 1; i <= 10; i++) { //分页获取图片数据,简单获取几页就行了 this.page(url + "page/"+ 1); }
这个 i 的取值过大了,因为我计算的时候失误了。如果按照这个情况下载的话,总共会下载:4 * 10 * (30-5) * 60 = 64800 张。(每一页是含有30个标题页,大概5个是广告。) 我一开始以为只有几百张图片! 这是一个估计值,但是真实的下载量和这个不会差太多的(没有数量级的差距)。所以我下载了一会发现只下载了第一个队列里面的图片。当然了,作为一个爬虫学习的程序,它还是很合格的。
这个程序只是用来学习的,我设置每张图片的下载间隔时间是1.5秒,而且是单线程的程序,所以速度上会显得很慢。但是那样也没有关系,只要程序的功能正确就行了,应该没有人会真的等到图片下载完吧。
那估计要好久了:64800*1.5s = 97200s = 27h,这也只是一个粗略的估计值,没有考虑程序的其他运行时间,不过其他时间可以基本忽略了。
以上就是怎么使用java爬虫批量爬取图片的详细内容。
其它类似信息

推荐信息