首页 > 经验记录 > 网络爬虫实现-拔取数据为bangumi动画排行榜(共4586部动画)

网络爬虫实现-拔取数据为bangumi动画排行榜(共4586部动画)

  以bangumi动画为例= =
成果,总共四千多个(最后一名是雷锋的故事

这个图是我给扒下来的数据保存到mysql数据库了在导出的excel文件
还有用IO流输出的txt文件和DOM转换的xml文件就懒得放了
废话不多说,上代码
 


Anime类
private int id;//排名
private String name;//名字
private String oldName;//原名
private String message;//信息
private double rating;//分数
private int number;//评分人数
private String imagePath;//图片地址
还有getset方法和toString方法,就不贴了



完成思路:
按照bangumi动画排行榜的域名,设定的符合查找标准的for循环。
其域名的标识是直接从1-191顺序排列,故建立一个从1开始循环到191的for循环。
建立一个只有单个线程的线程池,虽然速度比多线程有所降低,但是由于其只有一个线程,所以会按照顺序从1-191页读取数据
避免了多线程导致的顺序混乱问题。
根据面向对象的思想,咱是个指挥官,你怎么完成是你的事情,只要你能达到效果,我只管调用方法就是了
我需要的结果就是,这个for循环给我转完后,我的List集合里就存了[bangumi动画排行榜里的所有的动画对象]
直接让线程池中的线程执行写好了的AnimeSpider线程,把要扒的域名、接收数据的集合、闭锁给传过去。完事。
List集合拿到了,也就是四千多个动画对象拿到了,然后就可以随意操作了
调用我写好了的WriteAnimeTXT.writeAnime(animes);WriteAnimeXML.writeAnime(animes);
MybaseInsert.insert(animes);writeImage(animes);这些我写好了的方法,做到输出为txt、xml、保存到数据库、下载图片等等
方法我都写进来了,共四页
解释下闭锁:
CountDownLatch类,创建时可以指定一个int类型的数字,在数字到0之前,await()将一直阻塞,使程序停止在这个位置。直到数字归零
在这使用是为了让线程都走完,也就是说要让List集合里已经存了bangumi动画排行的所有数据后,在执行后面的方法
使用CountDownLatch中的方法countDown()可以使数字减一

主类

package bangumi;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestAnime {
	public static void main(String[] args) {
		System.out.println("开始检索bangumi动画排行榜...");
		//要传的集合
		List<Anime> animes = new ArrayList<Anime>();
		//闭锁
		CountDownLatch cdl = new CountDownLatch(191);
		//单个线程的线程池,为了集合中的数据顺序排列
		ExecutorService pool = Executors.newSingleThreadExecutor();
		for(int i = 1 ; i <= 191 ;i++){
			String url = "http://bangumi.tv/anime/browser?sort=rank&page="+i;
			pool.execute(new AnimeSpider(url,animes,cdl));
		}
		//关池子
		pool.shutdown();
		try {
			//开锁
			cdl.await();
			System.out.println("检索bangumi动画排行榜完毕,已全部储存...");
			System.out.println("正在准备输出所有排行信息:");
			System.out.print("5\t");
			Thread.sleep(1000);
			System.out.print("4\t");
			Thread.sleep(1000);
			System.out.print("3\t");
			Thread.sleep(1000);
			System.out.print("2\t");
			Thread.sleep(1000);
			System.out.print("1\t");
			Thread.sleep(1000);
			//遍历
			for (Anime a : animes) {
			    System.out.println(a);
			}
			//写入txt文件
			//WriteAnimeTXT.writeAnime(animes);
			//写入XML文件
			//WriteAnimeXML.writeAnime(animes);
			//写入数据库
			//MybaseInsert.insert(animes);
			//下载图片
			//writeImage(animes);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	//下载图片的方法
	public static void writeImage(List<Anime> animes){
		ExecutorService pool = Executors.newFixedThreadPool(10);
		for (Anime a : animes) {
			//调用ImgLoader线程传个Anime对象过去
			pool.execute(new ImgLoader(a));
		}
		pool.shutdown();
	}
}

 
 
AnimeSpider类,即在主方法中new了191次的线程

package bangumi;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//这些是json包中的!!
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class AnimeSpider implements Runnable {
	private String url;//接受的地址
	private List<Anime> animes;//接受的集合
	private CountDownLatch cdl;//接受的闭锁
	private static int id = 0;//id,id之所以没有去网页中获取,因为让他每次自动增长是和网页中扒下来的一样的
	public AnimeSpider() {
		super();
	}
	public AnimeSpider(String url) {
		super();
		this.url = url;
	}
	public AnimeSpider(String url, List<Anime> animes, CountDownLatch cdl) {
		super();
		this.url = url;
		this.animes = animes;
		this.cdl = cdl;
	}
	@Override
	public void run() {
		try {
			Document doc = Jsoup.connect(url).get();//通过传进来的url获取网页的html文档对象
			//System.out.println(doc);这里可以直接打印出整个网页的html
			Elements elems = doc.select(".browserFull li");//获取根节点,查找范围为:class = “browserFull”的标签中的<li>标签
			for(Element e:elems){
				String name = e.select("a").text();//查找范围为: <a>标签中的文本标签
				String oldName = e.select(".grey").text();//class = “grey”的标签中的文本标签
				String message = e.select("p").first().text();//<p>标签中的第一个的文本标签
				Double rating = new Double(e.select(".fade").text());
				String number = e.select(".tip_j").text();
				String imgPath = e.select(".cover").attr("src");//class = “cover”的标签中的属性为 src的内容
				//这一段是将评分人数中的数字提取出来,保存到int类型中,因为提取出来的评分人数是这样的:(****人评分)
				String str = number;
				String regEx="[^0-9]";
				Pattern p = Pattern.compile(regEx);
				Matcher m = p.matcher(str);
				number = m.replaceAll("");
				int num = new Integer(number);
				//把数据传到anime对象里,在塞到集合中去
				animes.add(new Anime(++id,name, oldName, message, rating, num,imgPath));
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			cdl.countDown();//之前提到过的,使闭锁-1.这个线程每完成一次就会让闭锁-1,运行191次之后就归零了
		}
	}
}

 
 把集合中的数据转换为TXT文件保存在工程的根目录

package bangumi;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.List;
public class WriteAnimeTXT {
	public static void writeAnime(List<Anime> animes) {
		System.out.println("正在写入TXT文件...");
		BufferedWriter bw = null;
		File file = new File("anime.txt");
		try {
			for (Anime a : animes) {
				 bw = new BufferedWriter(new FileWriter(file,true));
				 bw.write(a.toString());
			     bw.newLine();
			     bw.flush();
			     System.out.println(a);
			}
			System.out.println("写入完成!");
			System.out.println();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			try {
				bw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

 
把集合中的数据保存为XML文件,用的是DOM创建节点的方式,一个个创建节点并赋值,给予其层次关系
再将已经创建好的Document对象树转换为XML

package bangumi;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class WriteAnimeXML {
	public static void writeAnime(List<Anime> animes) {
		System.out.println("正在保存为XML文件...请稍后");
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
			Document doc = documentBuilder.newDocument();
			Element root = doc.createElement("animes");//创建根节点,名字叫animes
			// 遍历
			for (Anime a : animes) {
				/*
				 *
				 * //排名 private int id; //名字 private String name; //原名 private
				 * String oldName; //信息 private String message; //分数 private
				 * double rating; //评分人数 private int number;
				 */
				Element anime = doc.createElement("anime");//创建子节点,名字叫anime
				Element id = doc.createElement("id");//这也是一个子节点,叫做id,等会把这个子节点塞进anime这个节点里面去
				//往id的文本节点里塞遍历出来的a的id,要用文档对象的createTextNode方法,传个字符串
				id.appendChild(doc.createTextNode(a.getId()+""));
				Element name = doc.createElement("name");
				name.appendChild(doc.createTextNode(a.getName()));
				Element oldName = doc.createElement("oldName");
				oldName.appendChild(doc.createTextNode(a.getOldName()));
				Element message = doc.createElement("message");
				message.appendChild(doc.createTextNode(a.getMessage()));
				Element rating = doc.createElement("rating");
				rating.appendChild(doc.createTextNode(a.getRating()+""));
				Element number = doc.createElement("number");
				number.appendChild(doc.createTextNode(a.getNumber()+""));
				//往anime节点里塞这些玩意
				anime.appendChild(id);
				anime.appendChild(name);
				anime.appendChild(oldName);
				anime.appendChild(message);
				anime.appendChild(rating);
				anime.appendChild(number);
				root.appendChild(anime);//塞完后把anime节点塞进animes节点
			}
			doc.appendChild(root);
			//document --> xml文件
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer t = tf.newTransformer();
			//设置编码
			t.setOutputProperty("encoding", "UTF-8");
			//源头
			DOMSource source = new DOMSource(doc);
			//传输的位置
			Result result = new StreamResult("bangumi动画排行.xml");
			t.transform(source, result);
			System.out.println("已经保存为XML文件");
		} catch (Exception e) {
		}
	}
}

 
把图片下载到电脑里

package bangumi;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
class ImgLoader implements Runnable {
	private Anime anime;
	public ImgLoader() {
		super();
		// TODO Auto-generated constructor stub
	}
	public ImgLoader(Anime anime) {
		super();
		this.anime = anime;
	}
	static{
		System.out.println();
		System.out.println("正在下载图片...请稍后");
	}
	@Override
	public void run() {
		// 创建存放图片的文件夹
		File path = new File("bangumi动画排行榜");
		if (!path.exists()) {
			path.mkdir();
		}
		//创建图片的名字
		String name = String.format(anime.getId()+"_"+anime.getName()+".jpg");
		BufferedOutputStream out =null;
		try {
			//输出流
			out = new BufferedOutputStream
					(new FileOutputStream(new File(path, name)));
			byte[] data = new OkHttpClient.Builder()
					.connectTimeout(60, TimeUnit.SECONDS)
					.readTimeout(60, TimeUnit.SECONDS)
					.writeTimeout(60, TimeUnit.SECONDS)
					.build()
					.newCall(new Request.Builder()
					.url(anime.getImagePath()).build())
					.execute().body().bytes();
			out.write(data);
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				out.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 

           


CAPTCHAis initialing...

1 COMMENT

EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00