Java爬取晋江书城的某个分类下小说的简介和评论
写在前面,一开始是因为书荒又找不到自己喜欢的,就打算去晋江书城看看,结果排在前面的也不是我的菜,一本本挑又嫌太麻烦就打算把数据爬下来慢慢的看。分析了一下晋江的网页,发现可以爬下来的数据有书名、作者、类型、简介、标签、收藏、下载、点赞数、评论等,而我已经在晋江的网页上做过分类筛选,且萝卜白菜各有所爱,收藏和下载量高的也不能代表就是我喜欢的,所以我最后选择爬取简介、评论和第一章的内容,简介是一本书大体的概要,可以筛选不喜欢的设定,评论可以筛选一些文笔不好或太狗血的文章,第一章内容可以大致了解一个人的文风····当然后来我因为觉得数据太多的原因没有爬第一章内容。最后的成果是把每一页的小说的名字、简介和评论抓取出来,并生成一个txt文件,然后供自己手动筛选。
1.准备好需要爬取的网页URL:
http://www.jjwxc.net/bookbase.php?fw0=0&fbsj=0&ycx0=0&ycx1=1&xx1=1&sd2=2&sd3=4&lx1=1&lx6=6&lx10=10&lx13=13&lx16=16&fg2=2&sortType=0&isfinish=2&collectiontypes=ors&searchkeywords=&page=1";
2.eclipse新建Dynamic项目JingjiangSpider;
3.在WEB-INF/lib下引入需要的包。
其实不太确定是不是必须的····
4.在src下新建一个包com.guozi.spider,并在包下新建java文件JinjiangSpider.java。
5.先把本页的小说名和链接爬出来封装到map中去。
主要是解析,需要打开网页按F12去看element那部分源码,对着网页找到我们需要提取的那部分节点的id或者class甚至是标签,通过这些我们才能提取到我们所需要的信息。Id唯一所以是我们的第一选择。最后输出测试一下是否得到需要信息。
JinjiangSpider.java:
//把小说名和链接整出来装到map里面去。
public static Map getPageurl(String url){
Map nm=new HashMap();
try {
//从网页获取得到HTML jsoup是网页解析工具
Document document = Jsoup.connect(url).get();
Element body=document.body();
//cytable是分析网页源码得到的节点 通过class得到element
Elements links1 = body.getElementsByClass("cytable");
//得到所有标签的element
Elements links = links1.get(0).getElementsByTag("a");
for (Element link : links) {
//过滤链接 只有以onebook开头才是我需要的
if(link.attr("href").startsWith("onebook")){
//弄到map里面去存着 得到的链接只是相对链接记得加上前边的
nm.put(link.text(), "http://www.jjwxc.net/"+link.attr("href"));
}
}
for(String key : nm.keySet()){ //循环map输出来测试一下
System.out.println( key + "----" + nm.get(key));
}
} catch (IOException e) {
// TODO Auto-generated catch block bn
e.printStackTrace();
}
return nm;
}
public static void main(String[] args) {
String url="http://www.jjwxc.net/bookbase.php?fw0=0&fbsj=0&ycx0=0&ycx1=1&xx1=1&sd2=2&sd3=4&lx1=1&lx6=6&lx10=10&lx13=13&lx16=16&fg2=2&sortType=0&isfinish=2&collectiontypes=ors&searchkeywords=&page=1";
getPageurl(url);
}
6.第五步已经把每本小说的链接拿出来了,这一步就是通过这个链接进到小说的详情页抓取小说的简介和评论。此外我们还需要创建一个对象来保存小说的这些信息。最后我们把抓取到的小说信息保存到一个List中。
因为评论是异步加载的,不能和简介同时抓,所以我F12后分析了一下network,找到了评论的请求链接:
http://s8.static.jjwxc.net/comment_json.php?jsonpCallback=commnet_onebook_140421&novelid=2487981&chapterid=
其中onebook_后边的数字不知道是啥,找了几个小说的评论请求链接看都是一样的就没管了,novelid是小说id,小说链接上就有,截取出来就行了。之后需要用http请求得到数据,发送http请求前边有说过
Java如何发起http和https请求:java如何发起http和https请求
要注意network上边说的请求方式是get,但是!其实是用POST才能取到数据,get是乱码,它这个数据是ascll码,不需要转码,输出就是中文了。这个得到的一串调整一下就是json,之后解析json就行了。这个我前边也有教程:
Json解析:json和java对象的相互转换
NovelEntity.java:
package com.guozi.entity;
public class NovelEntity {
private String name; //书名
private String introduction; //介绍
private String comment; //评论
private String content; //第一章内容
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIntroduction() {
return introduction;
}
public void setIntroduction(String introduction) {
this.introduction = introduction;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
JinjiangSpider.java:
//把每一页小说的简评和评论弄出来
public static List getNovel(Map map){ //参数就是上一步返回的map
List noli=new ArrayList();
for(String key : map.keySet()){
NovelEntity novel=new NovelEntity();
Document document;
try {
String introduction="没有简评";
document = Jsoup.connect(map.get(key)).get(); //小说详情页的html
Element body=document.body();
//解析简评,如果简评存在就输出
Element e=body.getElementById("marknovel_message"); //marknovel_message是简评的id
if(e!=null){
introduction=e.text(); //抽取简评
}
//评论的请求链接 http://s8.static.jjwxc.net/comment_json.php?jsonpCallback=commnet_onebook_140421&novelid=2487981&chapterid=
String bookid=map.get(key).split("novelid=")[1]; //把小说的id弄出来
String baseurl="http://s8.static.jjwxc.net/comment_json.php?jsonpCallback=commnet_onebook_140421&novelid=NOVELID&chapterid="; //得到评论json的url
baseurl=baseurl.replace("NOVELID",bookid);
String s=CommonUtil.httpRequest(baseurl,"POST",null); //通过http请求得到评论json 得到的json不标准解析不出来,需要稍微整理一下
s=s.split("\\(\\{")[1];
s=s.split("\\}\\)")[0];
s="{"+s+"}";
JSONObject jo1=JSONObject.fromObject(s);
JSONArray bodyy=(JSONArray) jo1.get("body"); //解析评论
StringBuffer ss=new StringBuffer();
for(int i=0;i")[0];
p=p.replaceAll("\\","");
ss.append(p+"【^__^】"); //把所有评论弄到一起,然后分隔一下
}
String comment=ss.toString(); //得到评论
novel.setName(key);
novel.setIntroduction(introduction);
novel.setComment(comment);
noli.add(novel);
System.out.println(key);
System.out.println("【简介】"+introduction);
System.out.println("【评论】"+comment);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return noli;
}
public static void main(String[] args) {
String url="http://www.jjwxc.net/bookbase.php?fw0=0&fbsj=0&ycx0=0&ycx1=1&xx1=1&sd2=2&sd3=4&lx1=1&lx6=6&lx10=10&lx13=13&lx16=16&fg2=2&sortType=0&isfinish=2&collectiontypes=ors&searchkeywords=&page=1";
//getPageurl(url);
getNovel(getPageurl(url));
}
CommonUtil.java:
package com.guozi.spider;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
//import java.net.URLEncoder;
import java.net.URLEncoder;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
public class CommonUtil {
//处理http请求 requestUrl为请求地址 requestMethod请求方式,值为"GET"或"POST"
public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
StringBuffer buffer=null;
try{
URL url=new URL(requestUrl);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod(requestMethod);
conn.connect();
//往服务器端写内容
if(null!=outputStr){
OutputStream os=conn.getOutputStream();
os.write(outputStr.getBytes("utf-8"));
os.close();
}
//读取服务器端返回的内容
InputStream is=conn.getInputStream();
InputStreamReader isr=new InputStreamReader(is,"utf-8");
BufferedReader br=new BufferedReader(isr);
buffer=new StringBuffer();
String line=null;
while((line=br.readLine())!=null){
buffer.append(line);
}
}catch(Exception e){
e.printStackTrace();
}
return buffer.toString();
}
结果:
最后没抓第一章内容就是因为简介和评论就已经够多的了,再加上第一章我会看不过来的。
7.把上边list的内容弄成txt方便阅读。
JinjiangSpider.java
//一页弄出来的小说弄成txt
public static void toTxt(List list){
List li=list;
try {
File file = new File("E:/jinjiang/"+li.get(0).getName()+".txt"); //弄一个不重名的文件名
if (file.exists()) {
file.delete();
}
file.createNewFile();
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
for(int i=0;i
差不多到此为止了,只需要换一下baseurl里面page=后边的数字就能一页一页的把它弄出来啦!······什么你问我为什么不一次性把所有页数的都弄出来?当然有啊,不过·····
8.把每一页的小说全部抓出来。
JinjiangSpider.java:
//把分类下所有小说都弄出来
public static void getAllNovel(String url){
/*
* 晋江的bug,下一页没有尽头啊,所以只能抓取页数之后再来循环
* */
String burl=url;
int pagecount=1;
try {
Document document = Jsoup.connect(url).get();
Element body=document.body();
Element pagee=body.getElementById("pageArea").getAllElements().get(4);
pagecount=Integer.valueOf(pagee.attr("href").split("page=")[1]); //得到总页数
System.out.println(pagecount);
for(int i=1;i