PornHub的免费视频登录后是可以直接下载的,且链接就直接放在源代码里,我们只需要在请求中带上Cookies即可下载。但收费视频只支持在线观看,如果免费用户要下载到本地,就需要找到在线播放对应的文件链接。
打开Chrome调试模式并播放视频,Network选项卡里显示视频是从https://cv.phncdn.com/videos/xxxxxxx的链接取到的,在调试工具的Elements窗口里查找到此链接位于一个<video>标签内,但右键源代码里并没有此标签,显然是异步渲染的。
查找"cv."关键词,发现它只在id="player"标签下的一大段script里出现了,这段script的前十几行,定义了一个变量,可以读出描述了一些此视频的元信息;而后就是超长且无分行的大段定义,这种人为的可读性障碍一般就意味着里面有干货。搜索关键字(比如720p, 1080p这种分辨率),发现定位到的文本是这样的:
var quality_1080p=/* + ravbmd0slra75ravbmd0slra75 + */rahttpscra100rahttpscra100 + /* + ra8a47lxizkra10ra8a47lxizkra10 + */ravphncdnra87ravphncdnra87 + /* + ravbmd0slra75ravbmd0slra75 + */racomvideora70racomvideora70 + /* + ra6zdjixveara14ra6zdjixveara14 + */ras202001ra48ras202001ra48 + /* + rarwyc9jryra34rarwyc9jryra34 + */ra29280164ra17ra29280164ra17 + /* + raxxaaxrr2bra23raxxaaxrr2bra23 + ...中间略 +rajizy4aof8ra97rajizy4aof8ra97 + /* + rahiuxvgjbdra31rahiuxvgjbdra31 + */rait5pb7qvra34rait5pb7qvra34 + /* + ravphncdnra87ravphncdnra87 + */rafrdzfusijra70rafrdzfusijra70 + /* + ravkr6lm0dlra94ravkr6lm0dlra94 + */rapd8628sljra94rapd8628sljra94 + /* + ra8a47lxizkra10ra8a47lxizkra10 + */raqbf2mfoy5ra88raqbf2mfoy5ra88 + /* + rahttpscra100rahttpscra100 + */rac5daiara85rac5daiara85;
是一个字符串拼接,只是写了大量注释来混淆视听,而涉及到的拼接变量,经查找正好在此段文本之前的内容做了定义,至此有一些js基本功的就可以写一个下载脚本了。
但如果要用Python写爬虫怎么办?execjs和py2js这样的库是一个可考虑的选项,但此类库往往有执行速度慢和环境配置坑多的问题,并且笔者在实际使用过程中,还发现js语句中某些符号会导致此类库解析抛出异常。事实上Python与JavaScript在某些基础语法上是很相近的,我们完全可以把js语句转化为Python语句执行。
def getpornaddr(content: bs4.BeautifulSoup): script = content.select_one('#player').script if not script: return 'error' else: script_text = script.text.strip() # 根据变量命名特性划定有用的script范围 begin = script_text.rindex(" var ra") end = script_text.index("var quality_") valid_zone = script_text[begin:end].replace('var ','').strip() #Python支持末尾分号,所以不需处理,只把var 字样去掉即可,现在已经是Python的变量定义式了 exec(valid_zone) # exec方法能把字符串当做语句执行,无论在js还是Python里,exec都是一种挺危险的行为,要谨慎使用 generate_addr = re.search(r'var quality_[dw]+=(.+?);', script_text).group(1) # 用正则表达式定位到下载链接的运算式 generate_addr = re.sub(r'/*.+?*/', '', generate_addr) #去混淆用注释语句 return eval(generate_addr) #eval只能计算表达式的值
至此便得到了视频真正的下载地址。
更新:目前网站增加了一步验证,即需在最后一步用真实地址请求时带上一个Referer头部,这个referer的值即为地址X的分段base64编码拼接,地址X在视频页的源代码里,名为linkProxyUrl,具体分析方法同上文,不再赘述。