SuperSpider(简书爬虫JAVA版)

Java基础

浏览数:96

2019-9-11

AD:资源代下载服务

* 建站数据SuperSpider(简书)
* 本项目目的:
* 为练习web开发提供相关的数据;
* 主要数据包括:
* 简书热门专题模块信息、对应模块下的热门文章、
* 文章的详细信息、作者信息、
* 评论区详细信息、评论者信息等…
* 最后存储mysql数据库.

想学习爬虫的同学也可以瞧瞧

整个项目跑完花了近十个小时, 足见数据之多, 个人web开发练习用来充当建站数据也是绰绰有余的(~ ̄▽ ̄)~

代码注释写的挺详细的,我就直接上代码了。

主要代码:

  1 ​
  2 
  3 /**
  4  * 此类对简书文章内容页进行了详细的解析爬取;
  5  * 封装后的唯一入口函数 {@link ArticleSpider#run(String, int)}.
  6  *
  7  * author As_
  8  * date 2018-08-21 12:34:23
  9  * github https://github.com/apknet
 10  */
 11 
 12 
 13 public class ArticleSpider {
 14 
 15     /**    长度为4 :阅读量、评论数、点赞数、文章对应的评论id */
 16     private List<Integer> readComLikeList = new ArrayList<>();
 17 
 18     /**   文章Id  */
 19     private long articleId;
 20 
 21     /**
 22      * 此类的入口函数;
 23      * 爬虫范围包括文章的详情信息、评论的详细信息;
 24      * 并同时持久化数据;
 25      *  key实例:443219930c5b
 26      *
 27      * @param key
 28      * @param flag_type 文章所属分类的索引
 29      */
 30 
 31     public void run(String key, int flag_type){
 32 
 33         try {
 34             articleSpider(key, flag_type);
 35 
 36             //    以下参数由articleSpeder()函数获得
 37             String comUrl = String.format("https://www.jianshu.com/notes/%d/comments?comment_id=&author_only=false&since_id=0&max_id=1586510606000&order_by=desc&page=1", readComLikeList.get(3));
 38 
 39             comSpider(comUrl);
 40 
 41         } catch (Exception e) {
 42             e.printStackTrace();
 43         }
 44     }
 45 
 46     /**
 47      * 链接实例 https://www.jianshu.com/p/443219930c5b
 48      * 故只接受最后一段关键字
 49      * @param key
 50      * @param flag_type 文章所属分类的索引
 51      * @throws Exception
 52      */
 53     private void articleSpider(String key, int flag_type) throws Exception {
 54 
 55         String url = "https://www.jianshu.com/p/" + key;
 56 
 57         Document document = getDocument(url);
 58 
 59         IdUtils idUtils = new IdUtils();
 60 
 61         List<Integer> list =  getReadComLikeList(document);
 62 
 63         articleId = idUtils.genArticleId();
 64         String title = getTitle(document);
 65         String time = getTime(document);
 66         String imgCover = getImgCover(document);
 67         String brief = getBrief(document);
 68         String content = getContent(document);
 69         String author = idUtils.genUserId();
 70         int type = flag_type;
 71         int readNum = list.get(0);
 72         int comNum = list.get(1);
 73         int likeNum = list.get(2);
 74 
 75         //   数据库添加对应对象数据
 76         Article article = new Article(articleId, title, time, imgCover, brief, content, author, type, readNum, comNum, likeNum);
 77         new ArticleDao().addArticle(article);
 78 
 79         User user = new User();
 80         user.setUser(idUtils.genUserId());
 81         user.setName(getAuthorName(document));
 82         user.setImgAvatar(getAuthorAvatar(document));
 83         new UserDao().addUser(user);
 84 
 85     }
 86 
 87     private Document getDocument(String url) throws IOException {
 88 
 89         Document document = Jsoup.connect(url)
 90                 .header("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0")
 91                 .header("Content-Type", "application/json; charset=utf-8")
 92                 .header("Cookie", "_m7e_session=2e930226548924f569cb27ba833346db;locale=zh-CN;default_font=font2;read_mode=day")
 93                 .get();
 94         return document;
 95     }
 96 
 97     private String getTitle(Document doc){
 98         return doc.select("h1[class=title]").text();
 99     }
100 
101     private String getTime(Document doc){
102         Element element = doc.select("span[class=publish-time]").first();
103         return element.text();
104     }
105 
106 
107     private String getImgCover(Document doc){
108         Element element = doc.select("img[data-original-format=image/jpeg]").first();
109         if (element.hasAttr("data-original-src")){
110             String url = element.attr("data-original-src").trim();
111            return SpiderUtils.handleUrl(url);
112         }
113         return null;
114     }
115 
116     private String getBrief(Document doc){
117         return doc.select("meta[name=description]").first().attr("content");
118     }
119 
120     private String getContent(Document doc){
121         Element element = doc.select("div[class=show-content-free]").first();
122         //        System.out.println(element.html());
123 
124         Elements eles = element.select("div[class=image-package]");
125         for(Element ele: eles){
126             Element imgEle = ele.select("img").first();
127             ele.replaceWith(imgEle);
128         }
129 
130         String result = element.html().replaceAll("data-original-", "");
131         return result;
132     }
133 
134     private String getAuthorAvatar(Document doc){
135         String url = doc.select("div[class=author]").select("img").attr("src");
136         return SpiderUtils.handleUrl(url);
137     }
138 
139     private String getAuthorName(Document doc){
140         Element element = doc.select("script[data-name=page-data]").first();
141         JSONObject jsonObject = new JSONObject(element.html()).getJSONObject("note").getJSONObject("author");
142         return jsonObject.getString("nickname");
143     }
144 
145     private List<Integer> getReadComLikeList(Document doc){
146         Element element = doc.select("script[data-name=page-data]").first();
147         JSONObject jsonObject = new JSONObject(element.html()).getJSONObject("note");
148 
149         readComLikeList.add(jsonObject.getInt("views_count"));
150         readComLikeList.add(jsonObject.getInt("comments_count"));
151         readComLikeList.add(jsonObject.getInt("likes_count"));
152         readComLikeList.add(jsonObject.getInt("id"));
153 
154         System.out.println(Arrays.toString(readComLikeList.toArray()));
155         return readComLikeList;
156     }
157 
158     /**
159      *  评论区爬虫---包括:
160      *  评论楼层、时间、内容、评论者、评论回复信息等
161      *  具体可查看{@link Comment}
162      *
163      * @param url
164      * @throws Exception
165      */
166     private void comSpider(String url) throws Exception {
167         URLConnection connection = new URL(url).openConnection();
168 
169         //  需加上Accept header,不然导致406
170         connection.setRequestProperty("Accept", "*/*");
171 
172         BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
173 
174         String line;
175         StringBuilder str = new StringBuilder();
176 
177         while ((line = reader.readLine()) != null) {
178             str.append(line);
179         }
180 
181         System.out.println(str.toString());
182 
183         JSONObject jb = new JSONObject(str.toString());
184 
185         boolean hasComment = jb.getBoolean("comment_exist");
186         if(hasComment){
187             int pages = jb.getInt("total_pages");
188             if(pages > 20){
189             //  某些热门文章评论成千上万, 此时没必要全部爬取
190                 pages = 20;
191             }
192             int curPage = jb.getInt("page");
193 
194 
195             JSONArray jsonArray = jb.getJSONArray("comments");
196             Iterator iterator = jsonArray.iterator();
197             while(iterator.hasNext()){
198                 JSONObject comment = (JSONObject) iterator.next();
199                 JSONArray comChildren = comment.getJSONArray("children");
200                 JSONObject userObj = comment.getJSONObject("user");
201                 IdUtils idUtils = new IdUtils();
202                 CommentDao commentDao = new CommentDao();
203                 UserDao userDao = new UserDao();
204 
205 
206                 int commentId = idUtils.genCommentId();
207                 String userId = idUtils.genUserId();
208 
209 
210                 System.out.println(comment.toString());
211                 //    评论内容相关
212                 String content = comment.getString("compiled_content");
213                 String time = comment.getString("created_at");
214                 int floor = comment.getInt("floor");
215                 int likeNum = comment.getInt("likes_count");
216                 int parentId = 0;
217 
218                 //    评论者信息
219                 String name = userObj.getString("nickname");
220                 String avatar = userObj.getString("avatar");
221 
222                 Comment newCom = new Comment(commentId, articleId, content, time, floor, likeNum, parentId, userId, avatar, name);
223                 commentDao.addComment(newCom);
224 
225                 User user = new User();
226                 user.setUser(userId);
227                 user.setName(name);
228                 user.setImgAvatar(avatar);
229 
230                 userDao.addUser(user);
231 
232                 //    爬取评论中的回复
233                 Iterator childrenIte = comChildren.iterator();
234                 while(childrenIte.hasNext()){
235                     JSONObject child = (JSONObject) childrenIte.next();
236 
237                     Comment childCom = new Comment();
238                     childCom.setId(idUtils.genCommentId());
239                     childCom.setArticleId(articleId);
240                     childCom.setComment(child.getString("compiled_content"));
241                     childCom.setTime(child.getString("created_at"));
242                     childCom.setParentId(child.getInt("parent_id"));
243                     childCom.setFloor(floor);
244                     childCom.setNameAuthor(child.getJSONObject("user").getString("nickname"));
245 
246                     commentDao.addComment(childCom);
247 
248                 }
249             }
250 
251             //  实现自动翻页
252             if(curPage == 1){
253                 for(int i = 2; i <= pages; i++){
254                     System.out.println("page-------------------> " + i);
255                     int index = url.indexOf("page=");
256                     String sub_url = url.substring(0, index + 5);
257                     String nextPage = sub_url + i;
258                     comSpider(nextPage);
259                 }
260             }
261         }
262     }
263 }
264 
265 ​

and:

  1 ​
  2 
  3 /**
  4  * 模块(简书的专题)爬虫
  5  * 入口:https://www.jianshu.com/recommendations/collections
  6  * 模块实例: https://www.jianshu.com/c/V2CqjW
  7  * 文章实例: https://www.jianshu.com/p/443219930c5b
  8  * 故此爬虫多处直接传递链接后缀的关键字符串(如‘V2CqjW’、‘443219930c5b’)
  9  *
 10  * @author As_
 11  * @date 2018-08-21 12:31:35
 12  * @github https://github.com/apknet
 13  *
 14  */
 15 
 16 public class ModuleSpider {
 17 
 18     public void run() {
 19         try {
 20 
 21             List<String> moduleList = getModuleList("https://www.jianshu.com/recommendations/collections");
 22 
 23             for (String key: moduleList) {
 24                 //  每个Module爬取10页内容的文章
 25                 System.out.println((moduleList.indexOf(key) + 1) + "." + key);
 26                 for (int page = 1; page < 11; page++) {
 27                     String moduleUrl = String.format("https://www.jianshu.com/c/%s?order_by=top&page=%d", key, page);
 28 
 29                     List<String> articleList = getArticleList(moduleUrl);
 30 
 31                     for (String articlekey: articleList) {
 32                         new ArticleSpider().run(articlekey, moduleList.indexOf(key) + 1);
 33                     }
 34                 }
 35             }
 36 
 37         } catch (Exception e) {
 38             e.printStackTrace();
 39         }
 40     }
 41 
 42     /**
 43      * 返回Module 关键字符段
 44      * @param url
 45      * @return
 46      * @throws IOException
 47      * @throws SQLException
 48      */
 49 
 50     private List<String> getModuleList(String url) throws IOException, SQLException {
 51 
 52         List<String> moduleList = new ArrayList<>();
 53         int i = 0;
 54 
 55         Document document = Jsoup.connect(url).get();
 56         Element container = document.selectFirst("div[id=list-container]");
 57         Elements modules = container.children();
 58         for(Element ele: modules){
 59             String relUrl = ele.select("a[target=_blank]").attr("href");
 60             String moduleKey = relUrl.substring(relUrl.indexOf("/c/") + 3);
 61             moduleList.add(moduleKey);
 62 
 63 //             System.out.println(moduleKey);
 64 //              以下爬取数据创建Module对象并持久化
 65 
 66             String name = ele.select("h4[class=name]").text();
 67             String brief = ele.selectFirst("p[class=collection-description]").text();
 68             String imgcover = SpiderUtils.handleUrl(ele.selectFirst("img[class=avatar-collection]").attr("src"));
 69 
 70             String articleNu = ele.select("a[target=_blank]").get(1).text();
 71             int articleNum  = Integer.parseInt(articleNu.substring(0, articleNu.indexOf("篇")));
 72 
 73             String userNu = ele.select("div[class=count]").text().replace(articleNu, "");
 74             Matcher matcher = Pattern.compile("(\\D*)(\\d*\\.\\d*)?(\\D*)").matcher(userNu);
 75             matcher.find();
 76             int userNum = (int)(Float.parseFloat(matcher.group(2)) * 1000);
 77 
 78             Module module = new Module((++i), name, brief, imgcover, userNum, articleNum, "apknet");
 79             new ModuleDao().addModule(module);
 80 
 81             System.out.println(name + "------------------>");
 82         }
 83         return moduleList;
 84     }
 85 
 86 
 87 
 88     /**
 89      * 文章链接实例 https://www.jianshu.com/p/443219930c5b
 90      * 故此处返回List的String为url后的那段字符串
 91      *
 92      * @param url
 93      * @return
 94      */
 95     private List<String> getArticleList(String url) {
 96 
 97         System.out.println("getArticleList:  --------------------->");
 98 
 99         List<String> keyList = new ArrayList<>();
100 
101         Document document = null;
102         try {
103             document = Jsoup.connect(url).get();
104         } catch (Exception e) {
105             e.printStackTrace();
106         }
107         Element ulElement = document.select("ul[class=note-list]").first();
108 
109         Elements elements = ulElement.select("li[id]");
110         for (Element ele : elements) {
111             String relUrl = ele.select("a[class=title]").attr("href");
112             String key = relUrl.substring(relUrl.indexOf("/p/") + 3);
113             keyList.add(key);
114             System.out.println(key);
115         }
116 
117         return keyList;
118     }
119 
120 }
121 
122 ​

最后附上实验截图:

​​

完整项目地址:

https://github.com/apknet/SuperSpider

 

作者:Asche