超簡單的JAVA爬蟲爬取晉江小說的簡介和評論
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<String,String> nm=new HashMap<String,String>(); try { //從網頁獲取得到HTML jsoup是網頁解析工具 Document document = Jsoup.connect(url).get(); Element body=document.body(); //cytable是分析網頁原始碼得到的節點 通過class得到element Elements links1 = body.getElementsByClass("cytable"); //得到所有<a>標籤的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,找到了評論的請求連結:
其中onebook_後邊的數字不知道是啥,找了幾個小說的評論請求連結看都是一樣的就沒管了,novelid是小說id,小說連結上就有,截取出來就行了。之後需要用http請求得到資料,傳送http請求前邊有說過
要注意network上邊說的請求方式是get,但是!其實是用POST才能取到資料,get是亂碼,它這個資料是ascll碼,不需要轉碼,輸出就是中文了。這個得到的一串調整一下就是json,之後解析json就行了。這個我前邊也有教程:
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<NovelEntity> getNovel(Map<String,String> map){ //引數就是上一步返回的map
List<NovelEntity> noli=new ArrayList<NovelEntity>();
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<bodyy.size();i++){
JSONObject j=(JSONObject) bodyy.get(i);
String p=j.getString("commentbody");
p=p.split("<br>")[0];
p=p.replaceAll("\\<img.*?\\>","");
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<NovelEntity> list){
List<NovelEntity> 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<li.size();i++){
bw.write(list.get(i).getName()+"\r\n"); //記得換行
bw.write(list.get(i).getIntroduction()+"\r\n");
bw.write(list.get(i).getComment()+"\r\n");
}
bw.close();
System.out.print("弄完了,寶寶去看文件吧"); //提示一下,不然不知道完了沒
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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));
toTxt(getNovel(getPageurl(url)));
}
結果:
差不多到此為止了,只需要換一下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<pagecount;i++){ //從第一頁到最後一頁都抓取一遍
burl=burl.replace(burl.split("page=")[1],String.valueOf(i)); //得到每一頁的連結
toTxt(getNovel(getPageurl(burl))); //把它弄出來
}
System.out.println("主人~我幹完活了~~~~");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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));
//toTxt(getNovel(getPageurl(url)));
getAllNovel(url);
}
結果除了第一頁其他的都抓不到了,就消失了消失了消失了····我也不知道為啥。