网络爬虫实现-拔取数据为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();
}
}
}
}

1 COMMENT