Node.js 种子下载器

news/2024/11/7 21:12:08/

Node.js 种子下载器

庆祝 2018 国庆,制作了一个 Node.js 的种子下载器。爬取页面,根据页面的链接,破解另外一个网站,下载种子文件。项目比较简单,爬取页面没有使用任何爬虫框架。项目源码

Node.js 的安装请看我的另外一篇文章,Node.js 的多版本安装。

项目初始化

新建一个文件夹 FBIWarning,在该文件夹下打开命令行 CMD 或者 git bash。运行 npm init -y,该文件夹会生成一个 package.json 文件。

安装依赖包

安装依赖包 cnpm install --save cheerio iconv-lite request socks5-http-client。每个依赖包的功能如下:

  • cheerio // 解析 DOM
  • iconv-lite // 解决中文乱码的问题
  • request // http 请求,图片和种子的下载
  • socks5-http-client // socks 代理

爬取网页策略

网页之间,是靠链接联系在一起的,符合数据结构里面的图状结构。所以,对应有如下两种爬取策略。

  1. 爬取所有列表页面的链接后,再去爬取所有详情页面,对应图算法的广度优先遍历。
  2. 爬取一部分列表页面,就去爬取详情页面。然后再去爬取列表页面,爬取详情页面,循环进行,对应图算法的深度优先遍历。

因为是国外网站,网络可能随时断开,所以采用第二种策略比较好。同时,也能很快得到种子文件。为了防止重复爬取页面,可以将爬取页面的链接作为索引。

请求代理

网站是国外网站,需要使用梯子,否则不能爬取。代理传送门。socks5-http-client 配合 reqeust 使用,可以解决代理的问题。但是,该代理只支持 socks 代理, http(s) 代理暂不支持。

解决中文乱码的问题

目标网站的页面编码是 gbk ,而 request 依赖包的默认编码是 UTF-8,使用默认编码解码方式,会导致页面的中文变成乱码。所以得到返回数据前,去掉默认编码,就是设置编码为 encoding: null,然后使用 iconv-lite 使用 gbk 方式解码,这样就可以解决中文编码为乱码的问题,代码如下:

const request = require("request")
// 解析 dom
const cheerio = require("cheerio")
// 中文编码
const iconv = require("iconv-lite")
// 代理
const Agent = require("socks5-http-client/lib/Agent")
const COMMON_CONFIG = require("./config")
/*** 请求页面* @param {String} requestUrl 请求页面*/
function requestPage(requestUrl) {try {return new Promise((resolve, reject) => {if (!requestUrl) {resolve(false)}request.get({url: requestUrl,agentClass: Agent,agentOptions: {socksPort: 13838, // 代理端口socksHost: "127.0.0.1" // 代理 Host},headers: {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"},// 去掉默认 utf-8 解码,否则解码会乱码encoding: null},function(err, response, body) {// 防止解析报错try {// 统一解决中文乱码的问题let content = iconv.decode(body, "gbk")let $ = cheerio.load(content)resolve($, err, response, body, content)} catch (error) {resolve(null)}})})} catch (error) {//如果连续发出多个请求,即使某个请求失败,也不影响后面的其他请求Promise.resolve(null)}
}

并发请求

分页请求有很多个,可以使用递归来一个一个请求,但是写法不太好看。所以,可以使用 ES7+ 里面的 async 函数,将同步过程变为异步过程。async 要配合 await 使用,就可以将同步过程变为异步过程。详细了解 async 请看阮一峰 ES async

async function innerRecursion() {for (let i = 1; i <= 100; i++) {let requestUrl = "http://www.baidu.com?page=" + i // 事例网站,非爬取网站let result = await this.requestPage(url)}
}

一个一个请求比较慢,可以使用 Promise.all 实现并发请求。当然,也可以使用 async 模块 提高下载的并发量,有需要的可以自己去了解。这个 async 模块并非上面的 async 函数。

function innerRecursion() {let requestUrls = []for (let i = 1; i <= 100; i++) {let requestUrl = "http://www.baidu.com?page=" + i // 事例网站,非爬取网站requestUrls.push(requestUrl)}let promises = requestUrls.map(url => this.requestPage(url))Promise.all(promises).then(results => {// results 是一个数组,对应上面每个请求的结果}).catch(error => {// 捕获请求中可能发生的错误console.log(error)})
}

图片下载

图片的下载非常简单,代码如下:

/*** 下载文件* @param {String} url 请求链接* @param {String} filePath 文件路径*/
function downloadFile(url, filePath) {// try...catch 防止一个请求出错,导致程序终止 种子的下载相同try {if (!url || !filePath) {return false}request.get({url,agentClass: Agent,agentOptions: {socksPort: 13838, // 代理端口socksHost: "127.0.0.1" // 代理 Host},headers: {headers: {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}}}).pipe(fs.createWriteStream(filePath))} catch (error) {console.log(error)}
}

破解网站种子下载

解析详情页面,只能得到类似 http://www.jandown.com?ref=VENU794 的链接,需要破解该网站的种子下载。查看网站的种子下载方式,就是一个 post 请求,后端就会返回种子文件。刚开始的时候,不熟悉服务端的表单提交方式,导致文件一直得不到,后来详细查看了 request 的官文文档,发现是自己写错了。结合上面的图片下载,种子的下载方式自然就有了,代码如下:

/*** 下载种子链接* @param {String} childDir // 子目录* @param {String} downloadUrl  // 下载种子地址*/
function downloadTorrent(childDir, downloadUrl) {try {// 解析出链接的 code 值let code = querystring.parse(downloadUrl.split("?").pop()).refif (!code || !childDir) {return false}// 发出 post 请求,然后接受文件即可request.post({url: "http://www.jandown.com/fetch.php",agentClass: Agent,agentOptions: {socksPort: 13838, // 代理端口socksHost: "127.0.0.1" // 代理 Host},headers: {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"},formData: {code}}).pipe(fs.createWriteStream(childDir + "/" + code + ".torrent"))} catch (error) {console.log(error)}
}

面向对象

刚开始是使用面向过程的方式写的,后来发现代码太重复了,所以采用 OOP 改写了整个代码。详细了解 javaScript Class 请看阮一峰 ES class

总结

  1. 学习中文编码为乱码的解决方法
  2. 学习了 request 的代理以及文件下载功能
  3. 破解种子网站的种子下载功能
  4. js 面向对象开发
  5. 爬虫并发量解决

感谢阅读!

转载于:https://www.cnblogs.com/stevexu/p/9755337.html


http://www.ppmy.cn/news/345482.html

相关文章

自制的 .net framework 强命名工具(支持.net4)

我们知道&#xff0c;以前在Windows下开发程序时常会遭遇著名的“DLL Hell”问题&#xff0c;即动态链接库的向后兼容问题。微软在.Net产生前曾尝试使用COM组件的方式来解决DLL Hell问题&#xff0c;即使用Guid来唯一的标识每一个COM组件。但是&#xff0c;实际上使用COM组件&a…

ref

在C的算法和函数之间&#xff0c;以类对象作为参数的情况很非常常见的问题&#xff0c;一般情况下传值语义是可行的&#xff0c;但也有很多特殊情况&#xff0c;作为参数的函数对象拷贝代价过高(具有复杂的内部状态)&#xff0c;或者不希望拷贝对象(内部状态不应该被改变)&…

华为OD机试 JavaScript 实现【密码强度等级】【牛客练习题】,附详细解题思路

一、题目描述 密码按如下规则进行计分&#xff0c;并根据不同的得分为密码进行安全等级划分。 1、密码长度&#xff1a; 5 分: 小于等于4 个字符 10 分: 5 到7 字符 25 分: 大于等于8 个字符 2、字母&#xff1a; 0 分: 没有字母 10 分: 密码里的字母全都是小&#xff08;…

浅析Struts2中的OGNL和ValueStack

要了解Struts2与OGNL表达式的关系&#xff0c;我们必须先搞清楚以下三个概念: 1、 ActionContext它是Action运行的上下文环境&#xff0c;Action的多项设置都存放在次&#xff0c;我们每一次Action调用都会创建一个ActionContext。通常情况下我们可以通过静态方法getContext(…

C#制作KTV点歌系统

这是一个KTV点歌系统&#xff0c;分为前台与后台&#xff0c;在后台是对操作人员、歌曲分类、歌曲上传等&#xff1b;前台就是一个KTV点歌功能的实现。 前台界面&#xff1a; 部分代码&#xff1a; //数字点歌private void bntNumber_Click(object sender, EventArgs e){Numbe…

ktv点歌系统服务器的安装,最详细的KTV点歌系统安装教程

对于喜欢唱歌的朋友来说&#xff0c;如果能在家安装一个KTV点歌台&#xff0c;那就可以在家天天唱歌了。可以享受自在的点歌唱歌真的非常棒&#xff0c;今天小编就给大家详细的介绍一下如何在家也安装一个KTV点歌系统。 最详细的KTV点歌系统安装教程图一 我们要安装的这个软件叫…

基于JAVA的KTV点歌系统,管理系统。

开发环境&#xff0c;语言&#xff0c;工具 本系统的采用的开发语言是JAVA&#xff0c;主要使用到的框架是Swing框架&#xff0c;数据库使用的是Mysql5.7.开发工具使用的是Eclipse 主要功能与身份 身份&#xff1a;管理员&#xff0c;客户 客户主要功能&#xff1a;选择对应…

KTV点歌系统数据库设计文档

KTV点歌系统数据库设计文档 前台e-r图 后台 E-R 3.数据库字典 SwanInfo(歌星表) 中文名 英文名 数据类型 大小 约束 备注 歌星编号 SwanID varchar 20 主键&#xff0c; 歌星序列号 歌星姓名 SwanName varchar 50 必填 歌星性别 Sw…