您好,登錄后才能下訂單哦!
這篇文章主要介紹了怎么使用Java爬取漫畫的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇怎么使用Java爬取漫畫文章都會有所收獲,下面我們一起來看看吧。
程序運行效果
獲取的文件目錄信息
文件的總信息
注: 這里有一個小問題,獲取的文件可能有的沒有后綴名,但是可以以圖片的方式打開觀看,具體原因我也不知道,因為不影響,也就不去管它了。(或者自己使用代碼,給文件重命名。)
這里以一部漫畫為例,首先看上面的編號,那個編號表示漫畫的目錄頁。這是很重要的,在這一頁有漫畫的目錄。然后依次點擊目錄中的章節,可以看到每一章的漫畫信息。
這里這個分頁很奇怪,因為每一章節的頁數不是一樣的,但是它確實直接可以選擇的,說明這個應該是提前加載或者異步加載的(我其實不會前端的知識,只是聽說了一些。)后來通過查看源(我用眼睛發現的)發現確實是提前加載所有漫畫頁的鏈接。不是異步加載的。
這里我點擊漫畫圖片獲取圖片的地址,然后再和自己發現的鏈接比對一下,就看出來了,然后拼接一下 url,就獲取到所有的鏈接了。
在相應的章節頁中,使用瀏覽器的查看源,就可以發現這樣一段腳本了。經過分析,腳本中的數組里面的信息,就是對應的每一頁漫畫的信息。
上面的截圖是一個大概的結構信息,所以獲取流程是: 目錄頁–>章節頁–>漫畫頁
對于這里,獲取到這段腳本作為字符串,然后以 “[” 和 “]” 獲取字串,然后使用 fastjson 將其轉化為一個 List 集合。
// 獲取的script 無法直接解析,必須先將 page url 取出來, // 這里以 [ ] 為界限,分割字符串。 String pageUrls = script.data(); int start = pageUrls.indexOf("["); int end = pageUrls.indexOf("]") + 1; String urls = pageUrls.substring(start, end); //json 轉 集合,這個可以總結一下,不熟悉。 List<String> urlList = JSONArray.parseArray(urls, String.class);
這里強調一點:Element對象的 text 方法是獲取可見信息,而 data 方法是獲取不可見信息。腳本信息是不可直接看見的,所以我使用 data 方法獲取它。所謂可見和不可見大概就是網頁上可以顯示和通過查看源可以獲取的信息的意思。比如轉義字符,通過t ext 獲取就變成轉義的字符了。
使用HttpClient連接池來管理連接,但是我沒有使用多線程,因為我只有一個ip地址,萬一被封了,很麻煩。當線程的時間還是可以 接受的,畢竟一部漫畫,大概也就是十來分鐘吧。(以600話為例)
package com.comic; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; public class HttpClientUtil { private static final int TIME_OUT = 10 * 1000; private static PoolingHttpClientConnectionManager pcm; //HttpClient 連接池管理類 private static RequestConfig requestConfig; static { requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(TIME_OUT) .setConnectTimeout(TIME_OUT) .setSocketTimeout(TIME_OUT).build(); pcm = new PoolingHttpClientConnectionManager(); pcm.setMaxTotal(50); pcm.setDefaultMaxPerRoute(10); //這里可能用不到這個東西。 } public static CloseableHttpClient getHttpClient() { return HttpClients.custom() .setConnectionManager(pcm) .setDefaultRequestConfig(requestConfig) .build(); } }
最重要的一個類,用來解析HTML頁面獲取鏈接數據。 注意:這里的 DIR_PATH 是硬編碼路徑,所以你想要測試,還請自己創建相關目錄。
package com.comic; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import org.apache.http.HttpEntity; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.alibaba.fastjson.JSONArray; public class ComicSpider { private static final String DIR_PATH = "D:/DBC/comic/"; private String url; private String root; private CloseableHttpClient httpClient; public ComicSpider(String url, String root) { this.url = url; // 這里不做非空校驗,或者使用下面這個。 // Objects.requireNonNull(root); if (root.charAt(root.length()-1) == '/') { root = root.substring(0, root.length()-1); } this.root = root; this.httpClient = HttpClients.createDefault(); } public void start() { try { String html = this.getHtml(url); //獲取漫畫主頁數據 List<Chapter> chapterList = this.mapChapters(html); //解析數據,得到各話的地址 this.download(chapterList); //依次下載各話。 } catch (IOException e) { e.printStackTrace(); } } /** * 從url中獲取原始的網頁數據 * @throws IOException * @throws ClientProtocolException * */ private String getHtml(String url) throws ClientProtocolException, IOException { HttpGet get = new HttpGet(url); //下面這兩句,是因為總是報一個 Invalid cookie header,然后我在網上找到的解決方法。(去掉的話,不影響使用)。 RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build(); get.setConfig(defaultConfig); //因為是初學,而且我這里只是請求一次數據即可,這里就簡單設置一下 UA get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"); HttpEntity entity = null; String html = null; try (CloseableHttpResponse response = httpClient.execute(get)) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 200) { entity = response.getEntity(); if (entity != null) { html = EntityUtils.toString(entity, "UTF-8"); } } } return html; } //獲取章節名 鏈接地址 private List<Chapter> mapChapters(String html) { Document doc = Jsoup.parse(html, "UTF-8"); Elements name_urls = doc.select("#chapter-list-1 > li > a"); /* 不采用直接返回map的方式,封裝一下。 return name_urls.stream() .collect(Collectors.toMap(Element::text, name_url->root+name_url.attr("href"))); */ return name_urls.stream() .map(name_url->new Chapter(name_url.text(), root+name_url.attr("href"))) .collect(Collectors.toList()); } /** * 依次下載對應的章節 * 我使用當線程來下載,這種網站,多線程一般容易引起一些問題。 * 方法說明: * 使用循環迭代的方法,以 name 創建文件夾,然后依次下載漫畫。 * */ public void download(List<Chapter> chapterList) { chapterList.forEach(chapter->{ //按照章節創建文件夾,每一個章節一個文件夾存放。 File dir = new File(DIR_PATH, chapter.getName()); if (!dir.exists()) { if (!dir.mkdir()) { try { throw new FileNotFoundException("無法創建指定文件夾"+dir); } catch (FileNotFoundException e) { e.printStackTrace(); } } //開始按照章節下載 try { List<ComicPage> urlList = this.getPageUrl(chapter); urlList.forEach(page->{ SinglePictureDownloader downloader = new SinglePictureDownloader(page, dir.getAbsolutePath()); downloader.download(); }); } catch (IOException e) { e.printStackTrace(); } } }); } //獲取每一個頁漫畫的位置 private List<ComicPage> getPageUrl(Chapter chapter) throws IOException { String html = this.getHtml(chapter.getUrl()); Document doc = Jsoup.parse(html, "UTF-8"); Element script = doc.getElementsByTag("script").get(2); //獲取第三個腳本的數據 // 獲取的script 無法直接解析,必須先將 page url 取出來, // 這里以 [ ] 為界限,分割字符串。 String pageUrls = script.data(); int start = pageUrls.indexOf("["); int end = pageUrls.indexOf("]") + 1; String urls = pageUrls.substring(start, end); //json 轉 集合,這個可以總結一下,不熟悉。 List<String> urlList = JSONArray.parseArray(urls, String.class); AtomicInteger index=new AtomicInteger(0); //我無法使用索引,這是別人推薦的方式 return urlList.stream() //注意這里拼接的不是 root 路徑,而是一個新的路徑 .map(url->new ComicPage(index.getAndIncrement(),"https://restp.dongqiniqin.com//"+url)) .collect(Collectors.toList()); } }
注意: 這里我的思路是,所有的漫畫都存放到 DIR_PATH 目錄中。 然后每一章節是一個子目錄(以章節名來命名),然后每一個章節的漫畫放到一個目錄中,但是這里會遇到一個問題。因為實際上漫畫是一頁一頁觀看的,所以漫畫就有一個順序的問題(畢竟一堆亂序漫畫,看起來也很費勁,雖然我這里不是為了看漫畫)。所以我就給每一個漫畫頁一個編號,按照上面腳本上的順序,進行編號。但是由于我使用了Java8的 Lambda 表達式,所以我無法使用索引。(這涉及到另一個問題了)。 這里的解決辦法是我看別人推薦的: 每次調用 index的 getAndIncrement
方法就可以增加 index 的值,非常方便。
AtomicInteger index=new AtomicInteger(0); //我無法使用索引,這是別人推薦的方式 return urlList.stream() //注意這里拼接的不是 root 路徑,而是一個新的路徑 .map(url->new ComicPage(index.getAndIncrement(),"https://restp.dongqiniqin.com//"+url)) .collect(Collectors.toList());
兩個實體類,因為是面向對象嘛,我就設計了兩個簡單的實體類來封裝一下信息,這樣操作比較方便一點。
Chapter 類代表的是目錄中的每一個章節的信息,章節的名字和章節的鏈接。 ComicPage 類代表的是每一個章節中的每一頁漫畫信息,每一頁的編號和鏈接地址。
package com.comic; public class Chapter { private String name; //章節名 private String url; //對應章節的鏈接 public Chapter(String name, String url) { this.name = name; this.url = url; } public String getName() { return name; } public String getUrl() { return url; } @Override public String toString() { return "Chapter [name=" + name + ", url=" + url + "]"; } }
package com.comic; public class ComicPage { private int number; //每一頁的序號 private String url; //每一頁的鏈接 public ComicPage(int number, String url) { this.number = number; this.url = url; } public int getNumber() { return number; } public String getUrl() { return url; } }
因為前幾天使用多線程下載類爬取圖片,發現速度太快了,ip 好像被封了,所以就又寫了一個當線程的下載類。 它的邏輯很簡單,主要是獲取對應的漫畫頁鏈接,然后使用get請求,將它保存到對應的文件夾中。(它的功能大概和獲取網絡中的一張圖片類似,既然你可以獲取一張,那么成千上百也沒有問題了。)
package com.comic; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Random; import org.apache.http.HttpEntity; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import com.m3u8.HttpClientUtil; public class SinglePictureDownloader { private CloseableHttpClient httpClient; private ComicPage page; private String filePath; private String[] headers = { "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11", "Opera/9.25 (Windows NT 5.1; U; en)", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Kubuntu)", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12", "Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.2.9", "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Ubuntu/11.04 Chromium/16.0.912.77 Chrome/16.0.912.77 Safari/535.7", "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0 " }; public SinglePictureDownloader(ComicPage page, String filePath) { this.httpClient = HttpClientUtil.getHttpClient(); this.page = page; this.filePath = filePath; } public void download() { HttpGet get = new HttpGet(page.getUrl()); String url = page.getUrl(); //取文件的擴展名 String prefix = url.substring(url.lastIndexOf(".")); Random rand = new Random(); //設置請求頭 get.setHeader("User-Agent", headers[rand.nextInt(headers.length)]); HttpEntity entity = null; try (CloseableHttpResponse response = httpClient.execute(get)) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 200) { entity = response.getEntity(); if (entity != null) { File picFile = new File(filePath, page.getNumber()+prefix); try (OutputStream out = new BufferedOutputStream(new FileOutputStream(picFile))) { entity.writeTo(out); System.out.println("下載完畢:" + picFile.getAbsolutePath()); } } } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { //關閉實體,關于 httpClient 的關閉資源,有點不太了解。 EntityUtils.consume(entity); } catch (IOException e) { e.printStackTrace(); } } } }
package com.comic; public class Main { public static void main(String[] args) { String root = "https://www.manhuaniu.com/"; //網站根路徑,用于拼接字符串 String url = "https://www.manhuaniu.com/manhua/5830/"; //第一張第一頁的url ComicSpider spider = new ComicSpider(url, root); spider.start(); } }
關于“怎么使用Java爬取漫畫”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“怎么使用Java爬取漫畫”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。