製作一個聽話的電影種子挖掘器

来源:http://www.cnblogs.com/hwding/archive/2016/06/06/5563009.html
-Advertisement-
Play Games

每次回到宿舍想看部電影才發現很長時間沒有去bt站淘種子了, 然而天天去站上找適合自己類型的電影又是一件費時又費力的事兒, 所以周末花時間寫了一個可配置的爬子, 能夠根據不同人的不同需求去自動下載種子文件, 並且能夠避免不同分類中的重覆電影 後期還會加入下載隊列的功能, 在檢測宿舍無人用網的時候開啟b ...


每次回到宿舍想看部電影才發現很長時間沒有去bt站淘種子了, 然而天天去站上找適合自己類型的電影又是一件費時又費力的事兒, 所以周末花時間寫了一個可配置的爬子, 能夠根據不同人的不同需求去自動下載種子文件, 並且能夠避免不同分類中的重覆電影

後期還會加入下載隊列的功能, 在檢測宿舍無人用網的時候開啟bt下載, 有人接入wifi就暫停

項目地址: https://github.com/hwding/btDigger , 眾人拾柴火焰高, 歡迎共同製作...

(爬子依賴'bt天堂'這個種子站)

運行起來是這個樣子的

 

開始分析這個爬子的製作過程

首先我想讓它具有以下靈活性, 請看config.json:

{
    "regions-banned":[
        "中國大陸"
    ],
    "depth":"2",
    "definition":"1080p",
    "categories":[
        "動作",
        "戰爭",
        "科幻",
        "懸疑",
        "犯罪",
        "恐怖",
        "驚悚",
        "冒險"
    ]
}

能夠屏蔽大陸地區的電影, 每個分類的頁面挖掘深度為2, 優先下載1080p的種子, 並且指定 "動作","戰爭","科幻"... 這幾類

在程式啟動後首先讀入配置文件並解析, 解析器採用單例模式以方便在其它類中載入配置

main方法中:  ConfigLoader.getInstance(); 

ConfigLoader類:

 1 import org.json.JSONArray;
 2 import org.json.JSONObject;
 3 import java.io.*;
 4 import java.util.ArrayList;
 5 
 6 class ConfigLoader {
 7     private static ConfigLoader configLoader = null;
 8     private static final String FILE_NAME = "config.json";
 9     private static ArrayList<String> regions_banned = new ArrayList<>();
10     private static ArrayList<String> categories    = new ArrayList<>();
11     private static int depth;
12     private static String definition;
13 
14     private ConfigLoader() {
15         String jsonString = extractJSON();
16         JSONObject jsonObject = new JSONObject(jsonString);
17         JSONArray regions_banned_array = jsonObject.getJSONArray("regions-banned");
18         JSONArray categories_array = jsonObject.getJSONArray("categories");
19         for (Object each : regions_banned_array)
20             regions_banned.add((String) each);
21         for (Object each : categories_array)
22             categories.add((String) each);
23         depth = Integer.parseInt(jsonObject.getString("depth"));
24         definition = jsonObject.getString("definition");
25     }
26 
27     static synchronized ConfigLoader getInstance() {
28         if (configLoader==null)
29             configLoader = new ConfigLoader();
30         return configLoader;
31     }
32 
33     ArrayList<String> getRegions_banned() {
34         return regions_banned;
35     }
36 
37     ArrayList<String> getCategories() {
38         return categories;
39     }
40 
41     int getDepth() {
42         return depth;
43     }
44 
45     String getDefinition() {
46         return definition;
47     }
48 
49     private String extractJSON() {
50         File file = new File(FILE_NAME);
51         String jsonString = "";
52         String temp;
53         try {
54             BufferedReader bufferedReader = new BufferedReader(
55                                             new InputStreamReader(
56                                             new FileInputStream(file), "UTF-8"));
57             while ((temp = bufferedReader.readLine()) != null)
58                 jsonString+=temp;
59         } catch (FileNotFoundException e) {
60             System.out.println("\n[x] Configuration file not found");
61             System.exit(0);
62         } catch (IOException e) {
63             System.out.println("\n[x] An error occurred when trying to read the configuration file");
64             System.exit(0);
65         }
66         return jsonString;
67     }
68 }

這裡就是簡單地將文件中的屬性賦給對應的類成員變數

然後main方法會啟動頁面解析器:  PageParser.getInstance(); 

頁面解析器在初始化成功後會自動根據配置進行解析

首先檢查能否連接'bt天堂'網站

然後開始在首頁中查找我們需要的電影分類的次級鏈接地址, 在網頁源代碼中是這樣的存在

 2         <div class="Btitle"><a href="/category.php?/%e5%8a%a8%e4%bd%9c/" title="動作電影">動作</a></div>
 3         <div class="Btitle"><a href="/category.php?/%e6%88%98%e4%ba%89/" title="戰爭電影">戰爭</a></div>
 4         <div class="Btitle"><a href="/category.php?/%e5%89%a7%e6%83%85/" title="劇情電影">劇情</a></div>
 5         <div class="Btitle"><a href="/category.php?/%e7%88%b1%e6%83%85/" title="愛情電影">愛情</a></div>
 6         <div class="Btitle"><a href="/category.php?/%e7%a7%91%e5%b9%bb/" title="科幻電影">科幻</a></div>
 7         <div class="Btitle"><a href="/category.php?/%e6%82%ac%e7%96%91/" title="懸疑電影">懸疑</a></div>        
 8         <div class="Btitle"><a href="/category.php?/%e5%ae%b6%e5%ba%ad/" title="家庭電影">家庭</a></div>
 9         <div class="Btitle"><a href="/category.php?/%e7%8a%af%e7%bd%aa/" title="犯罪電影">犯罪</a></div>
10         <div class="Btitle"><a href="/category.php?/%e6%81%90%e6%80%96/" title="恐怖電影">恐怖</a></div>
11         <div class="Btitle"><a href="/category.php?/%e5%8a%a8%e7%94%bb/" title="動畫電影">動畫</a></div>
12         <div class="Btitle"><a href="/category.php?/%e5%96%9c%e5%89%a7" title="喜劇電影">喜劇</a></div>
13         <div class="Btitle"><a href="/category.php?/%e6%83%8a%e6%82%9a" title="驚悚電影">驚悚</a></div>
14         <div class="Btitle"><a href="/category.php?/%e5%86%92%e9%99%a9" title="冒險電影">冒險</a></div>

所以我們只需要收集名為class屬性為Btitle的標簽, 並將標簽的內容與我們的目標分類數組中的每一項一一比對即可

然後再將其中的href屬性的值保存起來用於訪問各個分類

1 Elements bTitles = document.select("div[class=\"Btitle\"]");
2             for (Element each : bTitles) {
3                 Element thisCategory = each.select("a").first();
4                 ArrayList<String> categories = configLoader.getCategories();
5                 if (!categories.contains(thisCategory.text()))
6                     continue;
7                 targetCategoriesSubURLs.add(thisCategory.attr("href"));
8                 System.out.println("[o] \tCategory ["+thisCategory.text()+"] spotted");

完成分類鏈接的收集之後, 開始收集各個分類之中的電影:  parseCategoryPage(); 

這一步的工作就是屏蔽不想要的電影(此配置屏蔽中國大陸地區電影)並且丟棄不同分類下的同一個電影

parseCategoryPage()方法如下

 1  void parseCategoryPage() {
 2         System.out.println("[o] You are banning films from "+configLoader.getRegions_banned().toString());
 3         System.out.println("[o] You want to dig into each category with the depth of: "+configLoader.getDepth());
 4         if (configLoader.getDepth() < 1) {
 5             System.out.println("[x] Depth can not be smaller than 1");
 6             System.exit(0);
 7         }
 8         else if (configLoader.getDepth() > 5) {
 9             System.out.println("[i] Depth may be too large");
10         }
11         System.out.print("[o] Collecting films into each category...");
12         int counterDuplicated = 0;
13         int counterBanned = 0;
14         boolean isBanned;
15         boolean isDuplicated;
16         for (String each : targetCategoriesSubURLs) {
17             for (int i = 1; i < configLoader.getDepth() + 1; i++) {
18                 try {
19                     URL url = new URL(HOST + each + i);
20                     Document document = Jsoup.parse(url, 5000);
21                     Elements filmTitles = document.select("div[class=\"title\"]");
22                     for (Element eachFilmTitle : filmTitles) {
23                         if (!"".equals(eachFilmTitle.select("font").text())) {
24                             isBanned = false;
25                             isDuplicated = false;
26                             for (String eachBannedLocation : configLoader.getRegions_banned()) {
27                                 if (eachFilmTitle.select("p[class=\"des\"]").text().contains(eachBannedLocation)) {
28                                     counterBanned++;
29                                     isBanned = true;
30                                 }
31                             }
32                             for (String eachValidFileTitle : validFilmTitles) {
33                                 if (eachFilmTitle.select("font").text().contains(eachValidFileTitle)) {
34                                     counterDuplicated++;
35                                     isDuplicated = true;
36                                 }
37                             }
38                             if (!isBanned && !isDuplicated) {
39                                 validFilmTitles.add(eachFilmTitle.select("font").text());
40                                 validFilmSubURLs.add(eachFilmTitle.select("a").first().attr("href"));
41                             }
42                         }
43                     }
44                 } catch (MalformedURLException e) {
45                     System.out.println("\n[x] Internal error: MalformedURL");
46                 } catch (IOException e) {
47                     System.out.println("\n[x] An error occurred when trying to read the page");
48                 }
49             }
50         }
51         System.out.println("OK");
52         System.out.println("[o] \t"+counterBanned+" films banned");
53         System.out.println("[o] \t"+counterDuplicated+" films dropped due to duplication");
54         parseFilmPage();
55     }
View Code

註意這裡會先收集分類頁面底部的頁數導航欄, 並且用一個迴圈去根據指定的深度訪問頁面

此處深度為2, 所以會訪問第一頁和第二頁

分類頁面底部的頁數導航欄在網頁源代碼中是這樣的

 1 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/1/'>首頁</a></li>
 2  <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/-1/'>上一頁</a></li>
 3  <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/1/'>1</a></li>
 4 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/2/'>2</a></li>
 5 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/3/'>3</a></li>
 6 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/4/'>4</a></li>
 7 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/5/'>5</a></li>
 8 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/6/'>6</a></li>
 9 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/7/'>7</a></li>
10 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/8/'>8</a></li>
11 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/9/'>9</a></li>
12 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/10/'>10</a></li>
13 <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/11/'>11</a></li>
14  <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/1/'>下一頁</a></li>
15  <li><a href='/category.php?/%E7%8A%AF%E7%BD%AA/102/'>末頁</a></li>

解析的方法同上

完成並收集了符合要求的電影的頁面鏈接之後, 我們將開始進入到每一個電影的詳情頁並找到最好的種子去下載

這裡會調用 parseFilmPage() 方法

每個電影詳情頁會有種子列表, 提供不同清晰度(720p, 1080p, BluRay 720p, BluRay 1080p等), 為了不犧牲清晰度也不讓硬碟爆滿, 配置文件中指定最喜愛的清晰度為1080p

種子列表在網頁源代碼中如下, 解析方式同上上...

1 <div class="tinfo">
2 <a href="/download.php?n=%E6%B4%9B%E5%9F%8E%E5%B1%A0%E6%89%8Bbt%E7%A7%8D%E5%AD%90%E4%B8%8B%E8%BD%BD.720p%E9%AB%98%E6%B8%85.torrent&temp=yes&id=27808&uhash=b57db4fed7d35c8d0924033f" title="【720p高清】洛城屠手 /L.A. Slasher .2015.1.02GBBT種子下載" target="_blank"><p class="torrent"><img border="0" src="/style/torrent.gif" style="vertical-align:middle" alt="">【720p高清】洛城屠手<i>/L.A. Slasher</i>.2015.<em>1.02GB</em>.torrent</p></a>
3 <ul class="btTree treeview"><li><span class="file"><font color="#999">本torrent文件由BT天堂(www.BTtiantang.com)提供!</font></span></li><li><span class="video">L.A.Slasher.2015.720p.BluRay.H264.AAC-RARBG.mp4<small>1.02GB</small></span></li><li><span class="file">L.A.Slasher.2015.720p.BluRay.H264.AAC-RARBG.nfo<small>3.97KB</small></span></li><li><span class="video">RARBG.mp4<small>992.93KB</small></span></li></ul>
4 </div>
5 <div class="tinfo">
6 <a href="/download.php?n=%E6%B4%9B%E5%9F%8E%E5%B1%A0%E6%89%8Bbt%E7%A7%8D%E5%AD%90%E4%B8%8B%E8%BD%BD.1080p%E9%AB%98%E6%B8%85.torrent&temp=yes&id=27808&uhash=04645321cb7afdbdee192d1d" title="【1080p高清】洛城屠手 /L.A. Slasher .2015.1.61GBBT種子下載" target="_blank"><p class="torrent"><img border="0" src="/style/torrent.gif" style="vertical-align:middle" alt="">【1080p高清】洛城屠手<i>/L.A. Slasher</i>.2015.<em>1.61GB</em>.torrent</p></a>
7 <ul class="btTree treeview"><li><span class="file"><font color="#999">本torrent文件由BT天堂(www.BTtiantang.com)提供!</font></span></li><li><span class="video">L.A.Slasher.2015.1080p.BluRay.H264.AAC-RARBG.mp4<small>1.61GB</small></span></li><li><span class="file">L.A.Slasher.2015.1080p.BluRay.H264.AAC-RARBG.nfo<small>3.97KB</small></span></li><li><span class="video">RARBG.mp4<small>992.93KB</small></span></li></ul>
8 </div>

這裡僅僅提供了720p和1080p兩種清晰度的種子, 在多種清晰度的情況下, 爬子發現1080p的就會直接跳出對種子列表的遍歷, 否則就去下載最高清的那個

1 for (Element eachBtFileLink : btFileLinks) {
2                     Element info = eachBtFileLink.select("span[class=\"video\"]").first();
3                     if (info.text().contains(configLoader.getDefinition())) {
4                         targetBtFileLinkSuffix = eachBtFileLink.select("a").first().attr("href");
5                         break;
6                     }
7                     targetBtFileLinkSuffix = eachBtFileLink.select("a").first().attr("href");
8                 }

每發現一個目標種子就緊接著去訪問它的下載頁面

通過攔截POST請求和查看頁面源代碼, 我們發現每個種子的下載頁面都有arcid和uhash兩個屬性, POST的時候必須寫入這兩個東西才行

首先收集頁面中這兩個屬性的值

 1 String arcid = null;
 2                 String uhash = null;
 3                 boolean hasArcid = false;
 4                 boolean hasUhash = false;
 5                 while ((temp = bufferedReader.readLine()) != null) {
 6                     if (temp.contains("var _arcid")) {
 7                         arcid = temp.substring(temp.indexOf("\"")+1, temp.lastIndexOf("\""));
 8                         hasArcid = true;
 9                     }
10                     if (temp.contains("var _uhash")) {
11                         uhash = temp.substring(temp.indexOf("\"")+1, temp.lastIndexOf("\""));
12                         hasUhash = true;
13                     }
14                 }

如果兩個屬性值都拿到了就可以提交POST請求去下載種子文件啦

 1 if (hasArcid && hasUhash) {
 2                     URL requestUrl = new URL(REQUEST_URL);
 3                     HttpURLConnection httpUrlConnection = (HttpURLConnection) requestUrl.openConnection();
 4                     httpUrlConnection.setInstanceFollowRedirects(false);
 5                     httpUrlConnection.setDoOutput(true);
 6                     httpUrlConnection.setRequestMethod("POST");
 7                     String OUTPUT_DATA =
 8                             "action=download" +
 9                             "&id="            +
10                             arcid             +
11                             "&uhash="         +
12                             uhash;
13                     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
14                             httpUrlConnection.getOutputStream());
15                     outputStreamWriter.write(OUTPUT_DATA);
16                     outputStreamWriter.flush();
17                     outputStreamWriter.close();
18                     System.out.print("\r");
19                     System.out.print("[o] Downloading torrent files...("+i+"/"+validFilmSubURLs.size()+")");
20                     File file = new File(uhash+".torrent");
21                     InputStream inputStream = httpUrlConnection.getInputStream();
22                     FileOutputStream fileOutputStream = new FileOutputStream(file);
23                     byte[] buffer = new byte[1024];
24                     int length;
25                     while ((length = inputStream.read(buffer)) != -1) {
26                         fileOutputStream.write(buffer, 0, length);
27                         fileOutputStream.flush();
28                     }
29                     fileOutputStream.close();
30                     httpUrlConnection.disconnect();
31                 }

到此為止種子收集的模塊就初具雛形


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Django 遵從 MVC 模型,並將其特色化為 MTV 模型。模型的核心是通過用戶訪問的 url 來指向處理的函數,而函數處理後返回相應的結果。所以url決定了用戶訪問的入口,另外表單處理的提交地址也需要指定的url。url是所有功能的入口,所以url的編寫就變得非常重要。 Django 的 ur ...
  • <?php //php操作memcache的使用測試總結--學習 //1 Memcache::connect; //$memcache = new Memcache; //$memcache->connect('127.0.0.1',11211) or die("鏈接失敗!"); //2 Memca ...
  • 某個子站是php寫的,訪問的時候nginx時不時會冒出現502錯誤,高峰時更頻繁,檢查php-fpm的日誌發現大量的 child exited on signal 7 (SIGBUS),並且和accesslog里的502時間完全吻合,排除了php進程過載的可能,然後又排除了apc的嫌疑。 既然php ...
  • ...
  • 第2天 棧和寄存器 多文件編程 筆者在私下和很多C語言的愛好者和初學者交流的過程中發現,大家已經能夠使用C語言做出來很出色的程式了。但是這些出色的程式中的一部分竟然只有一個源文件。所以,筆者決定要介紹一下如何使用多個源文件進行編程。不得不說,多文件編程有非常多的優勢。比如在維護上非常方便,同時也給多 ...
  • 上一篇文章主要是講了mybatis-generator-core-1.3.2.jar的使用,這一篇我要介紹的是,修改jar包代碼,實現生成自定義模板。 1.我們從這裡可以下載mybatis-generator-core-1.3.2.jar項目源碼 http://maven.outofmemory.c ...
  • 看了網上一些文章,做了點總結,順便再加點自己的東西,簡單的說下。 1.利用瀏覽器的304緩存,但是304叫協商緩存,還是需要與伺服器通信一次 2.強制使用瀏覽器使用本地緩存(cache-control/expires),但是問題來了,不讓瀏覽器發資源請求,資源怎麼更新。 3.使用版本號,類似於a.c ...
  • 一、 java的基本程式設計結構 (一) java共有8種基本類型:4種整型,2種浮點類型,1種char,1種boolean。 1) 4種整型:byte(1)、short(2)、int(4)、long(8)。 2) int最常用,byte和short常用在底層的文件處理或者需要控制占用存儲空間量的大 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...