nodeJS搭建免费代理IP池爬取贴吧图片实战

news2024/11/24 8:31:24

之前用python写过爬虫,这次想试试nodeJS爬虫爬取贴吧图片,话不多说代码如下,爬取制定吧的前十页所有帖子里的图片

 爬取贴吧图片脚本

你得提前创建一个images文件夹

const axios = require("axios");
const cheerio = require("cheerio");
const sanitize = require("sanitize-filename");
const fs = require("fs");
const path = require("path");

// 定义要爬取的贴吧URL
const baseUrl = "https://tieba.baidu.com/f?kw=%CB%EF%D0%A6%B4%A8&fr=ala0&tpl=5&dyTabStr=MCwxLDMsMiw2LDQsNSw4LDcsOQ%3D%3D";

// 发送HTTP请求获取页面内容
async function getTitlesByPage(pageNum) {
  const url = baseUrl + pageNum * 50;
  try {
    const response = await axios.get(url);
    if (response.status === 200) {
      // 使用cheerio解析页面
      const $ = cheerio.load(response.data);

      $(".threadlist_title a.j_th_tit").each((index, element) => {
        // 定义要下载的帖子URL
        const url = "https://jump2.bdimg.com" + $(element).attr("href");

        // 发送HTTP请求获取页面内容
        axios
          .get(url)
          .then((response) => {
            if (response.status === 200) {
              // 使用cheerio解析页面
              const $ = cheerio.load(response.data);

              // 获取帖子中的所有图片链接
              const imgUrls = [];
              $("img.BDE_Image").each((index, element) => {
                imgUrls.push($(element).attr("src"));
              });

              // 下载所有图片
              imgUrls.forEach((imgUrl, index) => {
                axios({
                  method: "get",
                  url: imgUrl,
                  responseType: "stream",
                  headers: {
                    Referer: url,
                  },
                })
                  .then((response) => {
                    const filename = sanitize(path.basename(imgUrl));
                    const filePath = path.resolve(
                      __dirname,
                      `./images/${filename}.jpg`
                    );
                    response.data.pipe(fs.createWriteStream(filePath));
                    console.log(`第 ${index + 1} 张图片下载完成`);
                  })
                  .catch((error) => {
                    console.log(`第 ${index + 1} 张图片下载失败`, error);
                  });
              });
            } else {
              console.log("请求失败");
            }
          })
          .catch((error) => {
            console.log("请求出错", error);
          });
      });

    } else {
      console.log(`请求第 ${pageNum + 1} 页失败`);
    }
  } catch (error) {
    console.log(`请求第 ${pageNum + 1} 页出错`, error);
  }
}

async function getTitles() {
  for (let i = 0; i < 10; i++) {
    await getTitlesByPage(i);
  }
}

getTitles();

这里有个弊端,IP会被马上封掉,那么通过爬取免费代理IP网站的IP去创建本地代理IP池txt文件

找了一个勉强可用的免费代理IP网站免费代理IP_免费HTTP代理IP_SOCKS5代理服务器_优质IP代理_89免费代理IP

里面的有效IP很少,那么得自己去大量爬取筛选可用IP

 这个是

爬取建立免费代理IP池的脚本

你得提前创建一个proxy.txt文件

const fs = require('fs');
const axios = require('axios');
const cheerio = require('cheerio');

const headers = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
};


async function get89IP(filePath) {
  for (let i = 1; i <= 10; i++) { // 循环采集前10页的数据
    const url = `https://www.89ip.cn/index_${i}.html`;
    try {
      const response = await axios.get(url, { headers });
      const $ = cheerio.load(response.data);
      const trs = $('table tbody tr');
      trs.each((index, element) => {
        const ip = $(element).find('td:nth-child(1)').text().trim();
        const port = $(element).find('td:nth-child(2)').text().trim();
        const proxyIP = `${ip}:${port}`;
        fs.appendFileSync(filePath, proxyIP + '\n');
      });
      console.log(`第${i}页采集完成`);
    } catch (error) {
      console.error('出错了:', error);
    }
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
}

async function main() {
  const filePath = './proxy.txt';
  while (true) {
    try {
      await get89IP(filePath);
      console.log('采集完成');
    } catch (error) {
      console.error('出错了:', error);
    }
    await new Promise((resolve) => setTimeout(resolve, 60000));
  }
}

main();

 采集完成后的筛选IP代码

 一个一个筛选太慢,这里使用到了Promise.all

你得提前创建一个KyProxy.txt文件

const fs = require('fs');
const axios = require('axios');

const proxyList = fs.readFileSync('proxy.txt', 'utf-8').split('\n').filter(Boolean);

async function testProxy(ip) {
  try {
    const response = await axios.get('https://tieba.baidu.com/', {
      proxy: {
        host: ip.split(':')[0],
        port: ip.split(':')[1]
      },
      timeout: 5000
    });
    if (response.status === 200 || response.status === 302) {
      return true;
    }
  } catch (error) {
    console.error(error);
  }
  return false;
}

async function main() {
  const promiseArr = [];
  for (const proxy of proxyList) {
    promiseArr.push(testProxy(proxy));
  }
  const resultArr = await Promise.all(promiseArr);
  const validProxies = resultArr.reduce((acc, curr, index) => {
    if (curr) {
      acc.push(proxyList[index]);
      console.log(`代理IP ${proxyList[index]} 可用`);
    } else {
      console.log(`代理IP ${proxyList[index]} 不可用`);
    }
    return acc;
  }, []);

  fs.writeFileSync('kyProxy.txt', validProxies.join('\n'));
  console.log('可用代理IP已写入 kyProxy.txt');
}

main().catch((error) => console.error(error));

 到这一步kyProxy.txt里面的IP基本是稳定可用的了,最后一步就是使用kyProxy.txt里的代理I去爬取图片

 通过代理IP爬取贴吧图片

const axios = require("axios");
const cheerio = require("cheerio");
const sanitize = require("sanitize-filename");
const fs = require("fs");
const path = require("path");

// 定义要爬取的贴吧URL
const baseUrl =
  "https://tieba.baidu.com/f?kw=%CB%EF%D0%A6%B4%A8&fr=ala0&tpl=5&dyTabStr=MCwxLDMsMiw2LDQsNSw4LDcsOQ%3D%3D";

// 获取代理IP池
async function getProxyList() {
  const fileContent = await fs.promises.readFile(
    path.resolve(__dirname, "./kyProxy.txt"),
    "utf8"
  );
  return fileContent.trim().split("\n");
}

// 发送HTTP请求获取页面内容
async function getTitlesByPage(pageNum, proxyList) {
  const url = baseUrl + pageNum * 50;
  try {
    let success = false;
    for (let i = 0; i < proxyList.length; i++) {
      const proxy = `${proxyList[i]}`;
      console.log(`使用代理IP:${proxy}`);
      try {
        const response = await axios.get(url, {
          proxy: {
            host: proxyList[i].split(":")[0],
            port: proxyList[i].split(":")[1],
          },
        });
        if (response.status === 200) {
          // 使用cheerio解析页面
          const $ = cheerio.load(response.data);

          $(".threadlist_title a.j_th_tit").each(async (index, element) => {
            // 定义要下载的帖子URL
            const url = "https://jump2.bdimg.com" + $(element).attr("href");

            // 发送HTTP请求获取页面内容
            const imgUrls = await getImgUrls(url, proxy);

            // 下载所有图片
            for (let j = 0; j < imgUrls.length; j++) {
              await downloadImg(imgUrls[j], j, url, proxy);
            }
          });
          success = true;
          break;
        } else {
          console.log(`代理IP ${proxy} 请求失败`);
        }
      } catch (error) {
        console.log(`代理IP ${proxy} 请求出错`, error);
      }
    }
    if (!success) {
      console.log(`请求第 ${pageNum + 1} 页失败,跳过`);
    }
  } catch (error) {
    console.log(`请求第 ${pageNum + 1} 页出错`, error);
  }
}

// 获取帖子中的所有图片链接
async function getImgUrls(url, proxy) {
  try {
    const response = await axios.get(url, {
      proxy: {
        host: proxy.split(":")[0],
        port: proxy.split(":")[1],
      },
      headers: {
        Referer: url,
      },
    });
    if (response.status === 200) {
      const $ = cheerio.load(response.data);
      const imgUrls = [];
      $("img.BDE_Image").each((index, element) => {
        imgUrls.push($(element).attr("src"));
      });
      return imgUrls;
    } else {
      console.log(`请求 ${url} 失败`);
      return [];
    }
  } catch (error) {
    console.log(`请求 ${url} 出错`, error);
    return [];
  }
}

// 下载单张图片
async function downloadImg(imgUrl, index, url, proxy) {
  try {
    const response = await axios({
      method: "get",
      url: imgUrl,
      responseType: "stream",
      proxy: {
        host: proxy.split(":")[0],
        port: proxy.split(":")[1],
      },
      headers: {
        Referer: url,
      },
    });
    if (response.status === 200) {
      const filename = sanitize(path.basename(imgUrl));
      const filePath = path.resolve(__dirname, `./images/${filename}.jpg`);
      response.data.pipe(fs.createWriteStream(filePath));
      console.log(`第 ${index + 1} 张图片下载完成`);
    } else {
      console.log(`第 ${index + 1} 张图片下载失败`);
    }
  } catch (error) {
    console.log(`第 ${index + 1} 张图片下载出错`, error);
  }
}

async function getTitles() {
  const proxyList = await getProxyList();
  for (let i = 0; i < 10; i++) {
    await getTitlesByPage(i, proxyList);
  }
}

getTitles();

爬取效果

效果还可以

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1343576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4. 云原生之kubesphere基础服务搭建

文章目录 安装kubesphere插件服务暴露NodePort方式LoadBalancer方式安装 OpenELB部署eip资源配置网关启动网关创建路由测试网关路由ingress高级功能在服务中配置LoadBalancer 基础设施部署服务部署建议helm仓库添加helm仓库 运维相关部署gitlab部署nexus3部署harbor 研发相关 安…

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V2模型算法详解

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V2模型算法详解 文章目录 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V2模型算法详解前言ShuffleNet_V2讲解四条实用指导思想G1:相等的通道宽度可以降低存储访问成本G2:大量的分组卷积…

在IntelliJ IDEA中精通Git配置与使用:全面指南

目录 1 前言2 idea中使用git的准备2.1 在 IntelliJ IDEA 中配置 Git2.2 配置 Git 忽略文件 3 在IntelliJ IDEA中使用Git的基本步骤3.1 项目导入到 Git3.2 查看与切换版本信息 4 在 IntelliJ IDEA 中使用分支4.1 创建分支4.2 无冲突合并4.3 冲突合并 5 结语 1 前言 版本控制是现…

JavaScript:正则表达式

JavaScript&#xff1a;正则表达式 什么是正则表达式正则表达式语法定义正则表达式判断是否有匹配的字符串查找匹配的字符串 正则表达式匹配法则元字符边界符量词字符类 什么是正则表达式 正则表达式用于匹配字符串中字符的组合模式。 正则表达式会依据其自身语法&#xff0c;…

电脑怎么检测手机配置信息

目录 摘要 引言 用户登录工具和连接设备 查看设备信息&#xff0c;电池信息 查看硬盘信息 硬件信息 查看 基带信息 销售信息 摘要 本文介绍了如何使用克魔助手工具在电脑上检测手机的配置信息。通过该工具&#xff0c;用户可以全面了解手机的硬件和操作系统信息&#xff…

大语言模型发展史

前言 2023年可谓是生成式AI元年&#xff0c;大语言模型从崭露头角到锋芒毕露&#xff0c;已然成为人工智能领域的关键推动力。这一创新性的技术不仅在自然语言处理领域崭露头角&#xff0c;更深刻地改变了我们对人机交互、智能助手和信息处理的认知。那么大语言模型的发展历程…

STM32CubeMX教程10 RTC 实时时钟 - 周期唤醒、闹钟A/B事件和备份寄存器

目录 1、准备材料 2、实验目标 3、实验流程 3.0、前提知识 3.1、CubeMX相关配置 3.1.1 、时钟树配置 3.1.2、外设参数配置 3.1.3 、外设中断配置 3.2、生成代码 3.2.1、外设初始化函数调用流程 3.2.2、外设中断函数调用流程 3.2.3、添加其他必要代码 4、常用函数 …

听GPT 讲Rust源代码--src/tools(34)

File: rust/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs 文件"collection_is_never_read.rs"位于Rust源代码中的clippy_lints工具中&#xff0c;其作用是检查在集合类型&#xff08;如Vec、HashMap等&#xff09;的实例上执行的操作是否被忽略了…

[Angular] 笔记 19:路由参数

油管视频 Route Parameters 路由参数是跟在 url 后面的数字&#xff0c;字符串&#xff0c;或者 数字字符串&#xff0c;例如如下 url 中的 123&#xff0c;此类参数会传给后端&#xff1a; www.facebook.com/profile/123 首先将 pokemon-template-form 组件移到 pokeman-ba…

【开源】基于Vue+SpringBoot的房屋出售出租系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 房屋销售模块2.2 房屋出租模块2.3 预定意向模块2.4 交易订单模块 三、系统展示四、核心代码4.1 查询房屋求租单4.2 查询卖家的房屋求购单4.3 出租意向预定4.4 出租单支付4.5 查询买家房屋销售交易单 五、免责说明 一、摘…

Matlab:BP神经网络算法,二叉决策树

1、BP神经网络算法 (1)步骤 1.准备训练数据和目标值 2.创建并配置BP神经网络模型 3.训练BP神经网络模型 4.用BP神经网络模型预测数据 例&#xff1a;某企业第一年度营业额为132468&#xff0c;第二年度为158948&#xff0c;第三年度为183737&#xff0c;预测第四年度的营…

相位相关匹配法的opencv C++实现

前言&#xff1a;一个图像拼接的小项目&#xff0c;用途场景&#xff0c;显微图像的拼接&#xff0c;或者只包含x&#xff0c;y平移的图像拼接。本来是显微镜拼接工具&#xff0c;MIST的核心拼接代码&#xff0c;matlab版的&#xff0c;已经开源。下面是地址&#xff0c; GitH…

AI面板识别 - 华为OD统一考试

OD统一考试 (B卷) 分值: 100分 题解: Java / Python / C++ 题目描述 AI识别到面板上有N(1 ≤ N ≤ 100)个指示灯,灯大小一样,任意两个之间无重叠。 由于AI识别误差,每次别到的指示灯位置可能有差异,以4个坐标值描述AI识别的指示灯的大小和位置(左上角x1,y1,右下角x2…

Android NDK打包armeabi平台架包

NDK打包armeabi 1.降低NDK版本和Cmake版本 sdk.dirE\:\\Android\\sdk //指定ndk版本&#xff0c;不指定默认使用最新的NDK ndk.dirE\:\\Android\\sdk\\ndk\\16.1.4479499修改builde.gradle(app) android{defaultConfig{...//配置 AS 工程的 C/C 源文件编译参数externalNativ…

帆软FineBi V6版本经验总结

帆软FineBi V6版本经验总结 BI分析出现背景 ​ 现在是一个大数据的时代&#xff0c;每时每刻都有海量的明细数据出现。这时大数据时代用户思维是&#xff1a;1、数据的爆炸式增长&#xff0c;人们比起明细数据&#xff0c;更在意样本的整体特征、相互关系。2、基于明细的“小…

香橙派 ubuntu实现打通内网,外网双网络,有线和无线双网卡

当香橙派 ubuntu 连了有线&#xff0c;和无线时&#xff0c;默认请求外网时&#xff0c;只走一个网卡&#xff0c;如走了内网网卡&#xff0c;就只能访问内访问&#xff0c;访问不了外网&#xff1b;走了外网网卡就只能访问外网&#xff0c;访问不了内网&#xff1b; 实现双网…

如何使用Docker将.Net6项目部署到Linux服务器(三)

目录 四 安装nginx 4.1 官网下载nginx 4.2 下载解压安装nginx 4.3 进行configure 4.4 执行make 4.5 查看nginx是否安装成功 4.6 nginx的一些常用命令 4.6.1 启动nginx 4.6.2 通过命令查看nginx是否启动成功 4.6.3 关闭Nginx 4.6.5 重启Nginx 4.6.6 杀掉所有Nginx进程 4.…

嵌入式SOC之通用图像处理之OSD文字信息叠加的相关实践记录

机缘巧合 机缘巧合下, 在爱芯元智的xx开发板下进行sdk的开发.由于开发板目前我拿到是当前最新的一版(估计是样品)&#xff0c;暂不公开开发板具体型号信息.以下简称板子 .很多优秀的芯片厂商,都会提供与开发板配套的完善的软件以及完善的技术支持(FAE)&#xff0c;突然觉得爱芯…

C/C++转WebAssembly及微信小程序调用

上一篇文章讲了C/C如何转WebAssembly&#xff0c;并测试了在Web端调用。本篇内容和上篇一样&#xff0c;介绍C/C包转的.wasm包如何在小程序中调用。 说明 本篇是在上一篇步骤1-4的基础上&#xff0c;再做修改&#xff0c;供微信小程序端调用的方法和步骤。 本篇操作手册可以…

burpsuite模块介绍之repeater

导语 repeater是一个用于手动操作和发送个别HTTP请求的简单工具&#xff0c;它可以帮助您分析应用程序的响应。您可以使用repeater从Burp Suite的任何位置发送内部请求&#xff0c;然后修改请求并发送。通过这种方式&#xff0c;您可以测试和调试应用程序&#xff0c;并对请求…