我寫了個 Chrome 插件,一鍵下載 Pornhub 影片!

  • 2019 年 12 月 10 日
  • 筆記

插件獲取和使用見文末,請先欣賞技術流。

小詹有個讀者叫zgao,他之前寫過一篇部落格,爬取了 Pornhub(下面全部簡稱P站)上的影片,程式碼很簡單,關鍵還好用!當時小詹就 P 站上爬取了一些考研數學影片,雖然我用不上,但是我熱愛技術啊!

技術分析

之所以有今天這篇文章呢,是因為zgao(下文中的我都為讀者zgao)發現 P 站修改了前端的程式碼,將影片的介面資訊全部隱藏起來了,不像以前那樣能直接在網頁源碼中找到影片鏈接。於是我根據最新的 P 站情況寫了一個插件,能夠一鍵下載學習影片。

以下是從分析到寫插件的全過程。首先可以來對比下修改前後的 P 站:

這是以前的 P 站。

這是現在的 P 站。

這兩段都是 JS 程式碼,但是以前的一看就很清晰明了,而現在的有一大堆的垃圾注釋在裡面。不用說,肯定是做了 JS 混淆。

因為程式碼是被壓縮過的,所以在 chrome 開發者工具里直接程式碼格式化。

在 network 面板中將程式碼格式化之後可以看的很清楚,看上面定義的那一堆變數感覺就是我們要的 url,而下面就是將變數拼接的最後的影片鏈接。

我在想 P 站的程式猿不會真的這麼傻吧,難道就這樣拼接一堆字元串??所以我就去前面找到定義的那個變數。

然後在控制台輸出這個變數的值。

我整個人都傻了,P 站的程式猿這麼直接的嗎?那還混淆個**的程式碼?

以前寫個 P 站的爬蟲還得每個 URL 正則匹配才能提取出來。現在更省事了,都不用爬了,直接把這個變數的值取出來就什麼資訊都有了。

我嚴重懷疑P站的前端程式猿是不是寫程式碼的時候看片去了!

所以接下來要想提取出影片的url就很容易了,直接用ExecJS這個庫來執行這段被混淆過的JS程式碼就ok了。

但是想了想這樣也太簡單了吧,沒什麼意思。要不我寫一個Chrome的插件來完成這件事?因為本身影片載入之前JS肯定會執行。那麼用插件的方式也更加方便。插件開發的過程,最關鍵的問題就是如何將包含所有影片介面資訊的變數給提取出來。

一開始我是想到是直接在瀏覽器的全局變數window中拿到那個變數,這是最簡單的辦法。我發現通過注入JS程式碼用console.log(window)輸出的全局變數中還是沒有包含flashvars_*****這個變數,不清楚為什麼。我一開始認為可能是頁面onload的時候還沒有執行JS所以沒有變數資訊。後面我想了想要不執行settimeout來實現延遲執行程式碼,但是還是不行。 於是我決定用另外一個辦法,將字元串作為程式碼執行。也就是寫木馬最常用到的eval函數。在頁面載入時,通過xpath得到混淆JS程式碼的位置,將它作為一段字元串當成程式碼執行,這樣同樣拿到了介面資訊。

給大家看一下插件最核心的兩段程式碼content-script.js,也就是注入頁面的JS程式碼。(程式碼可左右拖動)

function Func() {     return new Promise((resolve, reject) => {        var a = document.querySelector("#player >script:nth-child(1)").innerHTML        a = a.split('loadScriptUniqueId')[0]        var c = a.match("flashvars_[0-9]{1,}")[0]        eval(a)        var d = eval(c)        resolve(d)     })  }  window.onload = () => {     Func().then(res => {        var videoType = []        Object.keys(res).forEach((item) => {           if (item.startsWith('quality_')) {              var obj={                 key:item,                 val:res[item]              }              videoType.push(obj)           }        })     for(var i = 0, len = videoType.length; i < len; i++){     console.log(videoType[i].key,videoType[i].val)  }     chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){        if(request.cmd == 'test')        sendResponse(videoType);        });     })  }    popup.js    function sendMessageToContentScript(message, callback)  {      chrome.tabs.query({active: true, currentWindow: true}, function(tabs)      {          chrome.tabs.sendMessage(tabs[0].id, message, function(response)          {              if(callback) callback(response);          });      });  }  sendMessageToContentScript({cmd:'test', value:'test'}, function(videoType)  {      console.log(videoType);     for(var i = 0, len = videoType.length; i < len; i++){        console.log(videoType[i].key,videoType[i].val)  }      var boxEl = document.getElementsByTagName('ul')[0]      //var videoType = [{ key: 'qeqw', val: 'adasda' }, { key: 'qeqw', val: 'adasda' }, { key: 'qeqw', val: 'adasda' }]      var videoStr = ''      videoType.forEach(item => {          videoStr += "<li>" + "<label>清晰度:" + "<span>" + item.key + "</span>" + "</label>" + "<a href=" + item.val + " target='_blank'>下載</a>" + "</li>"      });      boxEl.innerHTML = videoStr  });

popup.js的作用主要就是和content-script.js通訊,相互傳值。然後在插件中渲染生成頁面,JS間傳值主要用到了Chrome的API。插件開發的過程中還涉及到很多細節問題,比如ico的製作,這些就不提了。

關於插件

看到這的小夥伴,其實真正吸引你的是這最後的插件吧?

插件寫完,我順便上傳到了Google商店裡,畢竟是自己寫的第一個Chrome插件,還是蠻激動的。感興趣的小夥伴可以使用試試。

插件使用方式:

  • 安裝插件(下文有插件壓縮包)
  • 打開 P 站,打開你要下載的影片網頁
  • 點擊菜單欄的 PH 插件即可選擇清晰度進行下載(如下圖文)

插件壓縮包:

Pornhub影片下載插件已經打包好(因為某些原因,如果不能從Google商店安裝的話,可以直接解壓導入。)

Tags: