第3节---爬取知乎‘美女’

这一节,我们来实现爬取知乎‘美女’话题下的子问题以及相关问题回答的赞同数前两名。


上两节,我们爬取了百度首页的源码,实现了百度LOGO的抓取和下载。这一次我们的目标是知乎。


先来看下程序运行效果











可以看到,美女话题下一些数据被我们爬取下来了。

下面来简要的讲一下实现,程序数据的分析工作由jsoup实现,用到了IOnet的知识。算是一个比较综合的小程序。


首先确定我们需要爬取的东西。

1.话题链接

2.子问题属性

3.回答者属性

4.回答赞同数

5.回答内容(包括图片)


这里可以分为五个类

1.ZhihuSpider 类,负责抓取话题下每个子问题块

2.Zhihu 类,负责抓取存储相应子问题

3.Author 类,负责储存回答者的信息

4.WriteIntiFile 类,负责把数据写入文件

5.ZhihuMain 类,负责调用


如图所示,调用关系


通俗点说,就是ZhihuMain调用ZhihuSpider中的getZhihu静态方法,通过jsoup分析话题首页,获得每个子问题的url,每个url通过构造方法新建一个Zhihu对象,在Zhihu的构造方法中,使用jsoup抓取问题回答的的信息,得到的数据构造初始化一个Author对象,同时调用getImgUrls静态方法,获得每个图片的url地址,最后,利用WriteIntoFile的两个静态方法,把文本和图片写入到对应的文件夹中。

后面是具体实现,大部分都有注释

public class ZhihuMain {

	//定义静态文件路径名
	static String grandFilePath;	//第一层
	static String parentFilePath;	//第二层
	static String childFilePath;	//第三层
	
	public static void main(String[] args) {
		//话题URL
		String url = "https://www.zhihu.com/topic/19552207/top-answers";
		//静态调用GetZhihu
		ZhihuSpider.GetZhihu(url);
	}

}

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class ZhihuSpider {

	static ArrayList<Zhihu> GetZhihu(String url) {
		ArrayList<Zhihu> results = new ArrayList<Zhihu>();
		Document doc;
		// 创建父文件夹
		ZhihuMain.grandFilePath = "d:/Beauty";
		File file = new File(ZhihuMain.grandFilePath);
		file.mkdir();
		//打印提示信息
		System.out.println("开始抓取知乎-美女-话题\n");
		
		try {
			doc = Jsoup.connect(url).get();
			//通过审查元素得到子问题块所在的标签
			Elements ListDiv = doc.select("h2");
			for (Element element : ListDiv) {
				//href属性在a标签中
				element = element.select("a").first();
				//获得子问题链接
				String linkHref = element.attr("href");
				//获得子问题文本名
				String line = element.text().trim();
				
				ZhihuMain.parentFilePath = line.substring(0, line.length() - 1);
				//打印提示信息
				System.out.println("抓取问题 :" + ZhihuMain.parentFilePath);
				// 创建子问题文件夹
				file = new File(ZhihuMain.grandFilePath + "/" + ZhihuMain.parentFilePath);
				file.mkdir();
				//构造方法初始化Zhihu对象,这时候跳到Zhihu的构造方法中
				Zhihu zhihu = new Zhihu(linkHref);
				results.add(zhihu);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return results;
	}

}


import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Zhihu {
	
	public String questionHeader;
	public String questionDescription;
	public String zhihuURL;
	public Author author;
	public ArrayList<Author> authorList;
	// 构造方法初始化数据

	public Zhihu(String url) {
		questionHeader = "";
		questionDescription = "";
		zhihuURL = "https://www.zhihu.com" + url; // 构造完整的问题链接
		int count = 0;// 计数,仅抓取前2个回答
		Document doc;
		Set<String> set = null;// 图片集合
		try {
			//连接子问题
			doc = Jsoup.connect(zhihuURL).get();
			// 问题标题
			Elements head = doc.getElementsByAttributeValue("class", "QuestionHeader-title");
			questionHeader = head.text().trim();
			// 问题描述
			Elements description = doc.getElementsByAttributeValue("class", "RichText");
			questionDescription = description.text().trim();

			// 问题及描述写入子问题文件夹
			String filePath = ZhihuMain.grandFilePath + "/" + ZhihuMain.parentFilePath;
			WriteIntoFile.textInfoFile(filePath, WritingZhihu());

			// 抓取每个回答块
			Elements ListItem = doc.getElementsByAttributeValue("class", "List-item");
			int size = ListItem.size();
			authorList = new ArrayList<Author>(size);
			for (Element list : ListItem) {
				if (count == 2) // 仅抓取前2个回答
					return;
				count++;
				// 初始化author属性
				String name = list.getElementsByAttributeValue("class", "UserLink-link").text().trim();
				//如果是匿名用户没有姓名
				if ("".equals(name))
					name = "匿名用户";
				String personalDetail = list.getElementsByAttributeValue("class", "AuthorInfo-detail").text().trim();
				String agreement = list.getElementsByAttributeValue("class", "Button VoteButton VoteButton--up").text()
						.trim();
				String personURL = "https://www.zhihu.com"+list.getElementsByAttributeValue("class", "UserLink-link").attr("href");
				String answer = doc.getElementsByAttributeValue("class", "RichContent-inner").text().trim();

				System.out.println("抓取回答者 :" + name);
				// 新建author对象
				author = new Author(name, personalDetail, agreement, personURL, answer);
				// 放入author集合中
				authorList.add(author);

				// 创建回答者子文件夹
				ZhihuMain.childFilePath = name;
				filePath = ZhihuMain.grandFilePath + "/" + ZhihuMain.parentFilePath + "/" + ZhihuMain.childFilePath;
				File file = new File(filePath);
				file.mkdir();

				// Author文本信息写入文件
				WriteIntoFile.textInfoFile(filePath, author.writingAuthor());

				// 把图片地址放进集合
				Elements imgs = list.select("img[src]");
				set = getImgUrls(imgs);
				// 图片写入文件夹
				WriteIntoFile.imgIntoFile(filePath, set);

			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static Set<String> getImgUrls(Elements imgs) {
		Set<String> set = new HashSet<String>();
		for (Element e : imgs) {
				String imgUrl = e.attr("src");
				if(imgUrl.endsWith("whitedot.jpg") || imgUrl.endsWith(".com"))
					continue;
				if(imgUrl.length()-imgUrl.lastIndexOf(".")>4)
					continue;
				set.add(imgUrl);
		}
		return set;
	}

	// 格式化
	public String WritingZhihu() {
		String result = "";
		result += "\r\n问题:" + questionHeader + "\r\n\r\n问题描述:" + questionDescription + "\r\n\r\n问题链接:" + zhihuURL
				+ "\r\n\r\n";

		return result;
	}
}

/*
 * 回答者信息类
 */
public class Author {

	private String name;			//名字
	private String personalDetail;	//个人签名
	private String agreement;		//点赞数
	private String personURL;		//个人主页
	private String answer;			//回答内容
	public Author(){};
	public Author(String name,String personalDetail, String agreement, String personURL,String answer) {
		this.name = name;
		this.personalDetail = personalDetail;
		this.agreement = agreement;
		this.personURL = personURL;
		this.answer = answer;
	}
	
	public String writingAuthor(){
		String result = "";
		result +="\r\n用户名:"+name+"\r\n签名:"+personalDetail+"\r\n赞同数:"+agreement
				+ "\r\n个人主页:"+personURL+"\r\n回答:"+answer+"\r\n";
				
		return result;
		
	}
}

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.Set;

public class WriteIntoFile {
	//这里用的是jdk的net中自带的类
	//写入图片
	public static void imgIntoFile(String filePath,Set<String> set){
		InputStream input = null;
		OutputStream output = null;
		File file = new File(filePath);
		int count = 1;
		try{
			if(set.size()!=0){
				Iterator<String> it = set.iterator();
				while(it.hasNext()){
					//1.获取网址
					String url = it.next();
					URL u = new URL(url);//记得要完整的URL,https://不能少
					//2.打开连接
					URLConnection connection = u.openConnection();
					//3.获得输入流
					input = connection.getInputStream();
					//4.判断文件夹是否存在
					if(!file.exists()){
						file.mkdir();
					}
					//5.写入操作
					byte[]b = new byte[2048];
					int len = 0;
					output = new FileOutputStream(new File(filePath+"/"+count+url.substring(url.lastIndexOf("."))));
					while((len = input.read(b))!=-1){
						output.write(b, 0, len);
					}
					count++;
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭I/o
			try {
				if (output != null)
					output.close();
				if (input != null)
					input.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}
	
	//写入author数据的
	public static void textInfoFile(String filePath,String content){
		BufferedWriter bufr = null;
		File file = new File(filePath);
		try {
			if(!file.exists()){
				file.mkdir();
			}
			bufr = new BufferedWriter(new FileWriter(new File(filePath+"/"+file.getName()+".txt")));
			bufr.write(content);
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(bufr!=null){
				try {
					bufr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值