本帖最后由 wushaominkk 于 2018-5-7 17:05 编辑
看到论坛中有不少爬取网站中美女图片的帖子,不过都是Python版的,于是乎就想给大家发一个JAVA版的网络爬虫教程。
目标网站:http://www.mmjpg.com/hot/
爬虫其实很简单的,只需用到两个外部库 -Jsoup 和 htmlnuit
一般最常用的就是Jsoup,当然了如果碰到了需要执行JavaScript和AJAX的网站那就需要用到 htmlnuit这个强大的库了,这个两个配合那可谓是无可匹敌!
网站分析:这个网站用到了AJAX 所以说两个库都需要用到:
对于这两个库的使用很简单的!
首先看下项目结构:
首先是Jar包: 引用的有:jsoup、htmlnuit、RxJAVA、OkHttp
parse包中主要是网页解析器类
HttpUtil类是网络请求的工具类
RequestThread类作为网络请求给一个URl地址并自动转化为所需要的Bean对象的集合并返回
OkHttp类是作为OkHttp的单例类
BaseParse是一个网页解析器的抽象模板
ParsePicture2 类是作为本次帖子所使用的网页解析器 ,对于1 嘛 嘿嘿老司机自行补脑 :
[Java] 纯文本查看 复制代码 public class ParsePicture2 extends BaseParse<List<PictureBean>, PictureBean> {
private static final String [i]ROOT_URL [/i]= "http://www.mmjpg.com";
public static void main(String[] a) throws Exception {
}
@Override
public int getPager(String html) {
Elements div = Jsoup.parse(html).getElementsByClass("page");
Elements a = div.select("a");
if(a.size()==0)
return 1;
String pager = a.get(a.size() - 1)
.attr("href")
.replaceAll("\\D", "");
return pager == null || pager.isEmpty() ? 1 : Integer.valueOf(pager);
}
@Override
public List<PictureBean> getMessage(String html) {
List<PictureBean> data = new ArrayList<>();
Elements li = Jsoup.parse(html).getElementsByClass("pic").select("li");
for (Element e : li) {
Element a = e.selectFirst("a");
String title = a.selectFirst("img").attr("alt");
String url = a.attr("href");
PictureBean bean = new PictureBean(title, url);
data.add(bean);
}
return data;
}
@Override
public synchronized String getDetail(PictureBean obj) {
String url = obj.getPictureList().get(0);
//初始化参数
WebClient webClient = new WebClient();
webClient.getOptions().setCssEnabled(false);
webClient.getOptions().setJavaScriptEnabled(true);
//设置支持AJAX特TM重要的
webClient.setAjaxController(new NicelyResynchronizingAjaxController());
webClient.getOptions().setTimeout(5000);
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getCookieManager().setCookiesEnabled(true);
webClient.getOptions().setUseInsecureSSL(true);
try {
HtmlPage page = webClient.getPage(url);
//如果第一层(getMessage())没有得到标题那么再这里重新获取
if(obj.getTitle()==null || obj.getTitle().isEmpty()){
List<HtmlElement> list = page.getBody().getElementsByAttribute("div", "class", "article");
String title = list.get(0).getElementsByTagName("h2").get(0).asText();
obj.setTitle(title);
}
HtmlElement element = page.getHtmlElementById("opic");
[i]/*
[/i][i] [/i][i]这里是执行[/i][i]JavaScript[/i][i]的代码[/i][i]
[/i][i] String clickAttribute = element.getOnClickAttribute();
[/i][i] ScriptResult result = page.executeJavaScript(clickAttribute);
[/i][i] HtmlPage page1 = (HtmlPage) result.getNewPage();*/
[/i][i] [/i]HtmlPage page1 = element.click();
List<HtmlElement> content = page1
.getBody()
.getElementsByAttribute("div", "class", "content")
.get(0)
.getElementsByTagName("img");
for (HtmlElement h : content) {
String src = h.getAttribute("src");
obj.getPictureList().add(src);
}
} catch (IOException e) {
System.[i]err[/i].println("出错:" + e.getMessage());
return null;
} finally {
webClient.close();
}
if (obj.getPictureList().size() == 0)
return null;
return "SUCCESS";
}
@Override
public String switchPager(String url, int index) {
//http://www.mmjpg.com/home/20
String path = url.substring(0, url.lastIndexOf("/") + 1);
if (index == 1)
return url;
return path + index;
}
@Override
public void execute() {
ExecutorService service = getExecutorService();
service.execute(new RequestThread(this, "http://www.mmjpg.com/hot/"));
//service.execute(new RequestThread(this, ROOT_URL));
service.shutdown();
}
}
方法的说明:
public int getPager(String html)
此方法作为要获取有多少页码
public List<PictureBean> getMessage(String html)
此方法作为爬取网页中的连接地址以及标题 --也就是说爬的是首页
public synchronized String getDetail(PictureBean obj)
此方法作为根据爬取后的连接地址然后第二次爬取,这次爬取的就是所有的图片了 --就是说爬的是首页中的每一个图集的地址
public String switchPager(String url, int index)
此方法作为根据index 自动返回新的需要爬取的页码地址
public void execute()
执行爬取操作
网络请求 自动转为List对象类:
[Java] 纯文本查看 复制代码 public class RequestThread extends Thread {
private BaseParse mParse;
private String mUrl;
private SimpleDateFormat simpleDateFormat;
public RequestThread(BaseParse parse, String url) {
this.mParse = parse;
this.mUrl = url;
this.simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
}
@Override
public void run() {
try {
execute();
} catch (Exception e) {
e.printStackTrace();
}
}
private void execute() throws Exception {
int pagerCount = mParse.getPager(HttpUtil.doGetForResult(mUrl));
System.[i]out[/i].println("总页数:"+pagerCount);
for (int i = 1; i <= pagerCount; i++) {
String currentUrl = mParse.switchPager(mUrl, i);
Observable.create((ObservableOnSubscribe<List>) observableEmitter -> {
List message = mParse.getMessage(HttpUtil.doGetForResult(currentUrl));
if (message.size() != 0) {
observableEmitter.onNext(message);
observableEmitter.onComplete();
} else {
observableEmitter.onError(new NullPointerException("长度为0,连接:"+currentUrl));
}
})
.map(ts -> {
List data = new ArrayList();
for (Object s : ts) {
String detail = mParse.getDetail(s);
if(detail!=null)
data.add(s);
}
return data;
})
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(List ts) {
String date = simpleDateFormat.format(System.currentTimeMillis());
for (Object s : ts)
System.[i]out[/i].println(date + "--" + s.toString());
Main.DATA.addAll(ts);
}
@Override
public void onError(Throwable throwable) {
System.[i]err[/i].println(throwable.getMessage());
}
@Override
public void onComplete() {
}
});
}
}
}
/**
* 网络请求
*/
Main方法:
[Java] 纯文本查看 复制代码 public static void main(String[] args) throws Exception {
// new ParsePicture1().execute();
new ParsePicture2().execute();
Scanner sc = new Scanner(System.[i]in[/i]);
String in = sc.nextLine();
/**
* 输入exit退出程序
* 输入do 开始下载图片到本地
* 输入其他查看当前爬取了多少条目
*/
while (!in.equals("exit")) {
System.[i]out[/i].println("总条数:" + DATA.size());
if (in.equals("do"))
downloadImage();
in = sc.nextLine();
}
}
爬取过程的部分预览:
部分下载预览:
最后需要补充的 介于大家用的编译器可能不同
所以这次打包就不上传整个项目 而是单独提取出来src 文件 和 lib Jar包文件
大家在使用的时候先自己新建一个JAVA项目然后拷贝这些文件到的项目中去,记得别忘了添加jar包的引入哦!
重要的是 项目中使用的Java 8
还有就是你在爬取的时候在控制台会打印图片的下载地址你直接打开会发现,所有图片都是一样的嘿嘿,因为这个网站加了防盗链 下载的时候因为加入了
con.addRequestProperty("Referer",referer);
一个请求头 引用百度搜的一句话:
{
使用HTTP协议。利用referer做防盗链
我们在网页里访问站外的图片的时候,在图片本站是可以看得,在外头就不能看了
因为header信息中的referer元素。
}
看下我下载的方法会发现这个一句话:
inputStream = HttpUtil.getStream(bean.getPictureList().get(index),
bean.getPictureList().get(0));
就是第二个参数,具体的请自己好好研究下源码吧!
如果您觉得很不错,就请多给点分吧!
百度云下载地址:
链接: https://pan.baidu.com/s/1dHatqhV 密码: tn9v
|