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

之前用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();

爬取效果

效果还可以

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spark应用程序的结构与驱动程序

Apache Spark是一个强大的分布式计算框架&#xff0c;用于处理大规模数据。了解Spark应用程序的结构和驱动程序是构建高效应用的关键。本文将深入探讨Spark应用程序的组成部分&#xff0c;以及如何编写一个Spark驱动程序来处理数据和执行计算。 Spark应用程序的结构 Spark应用…

vue3项目使用pako库解压后端返回zip数据

文章目录 前言一、pako 介绍一些特点和功能&#xff1a;简单示例 二、vue3 实战示例1.安装后引入库安装:引用用自定义hooks 抽取共用逻辑部署小插曲 前言 外部接口返回一个图片数据是经过zip压缩的&#xff0c;前端需要把这个数据处理成可以显示的图片。大概思路&#xff1a;z…

LTPI协议的理解——2、LTPI实现的底层架构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 LTPI协议的理解——2、LTPI实现的底层架构 前言一、体系结构三、实现细节四、物理接口信号传输方法总结 前言 前面讲了LTPI的定义和大概结构&#xff0c;接下来继续理解LTPI…

封装uniapp签字板

新开发的业务涉及到签字功能&#xff0c;由于是动态的表单&#xff0c;无法确定它会出现在哪里&#xff0c;不得已封装模块。 其中涉及到一个难点就是this的指向性问题&#xff0c; 第二个是微信小程序写法&#xff0c; 我这个写法里用了u-view的写法&#xff0c;可以自己修改组…

PiflowX组件-ReadFromKafka

ReadFromKafka组件 组件说明 从kafka中读取数据。 计算引擎 flink 有界性 Unbounded 组件分组 kafka 端口 Inport&#xff1a;默认端口 outport&#xff1a;默认端口 组件属性 名称展示名称默认值允许值是否必填描述例子kafka_hostKAFKA_HOST“”无是逗号分隔的Ka…

Zookeeper之手写一个分布式锁

前言 我之前写了一篇快速上手ZK的文章&#xff1a;https://blog.csdn.net/qq_38974073/article/details/135293106 本篇最要是进一步加深学习ZK&#xff0c;算是一次简单的实践&#xff0c;巩固学习成果。 设计一个分布式锁 对锁的基本要求 可重入&#xff1a;允许同一个应…

QT上位机开发(掌握一点c++基础)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 c是c语言的补充和扩展&#xff0c;本身的语法构成也是在一直迭代中。相信很多同学上大学读书的时候&#xff0c;或多或少对c语言有所了解&#xff…

python+django网上银行业务综合管理系统vue_bvj8b

本课题主要研究如何用信息化技术改善传统网上银行综合管理行业的经营和管理模式&#xff0c;简化网上银行综合管理的难度&#xff0c;根据管理实际业务需求&#xff0c;调研、分析和编写系统需求文档&#xff0c;设计编写符合银行需要的系统说明书&#xff0c;绘制数据库结构模…

尽量避免删改List

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

2024收入最高的编程语言

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 1.Python Python 是最流行、用途最广泛的语言之一。它通常用于网络开发、数据科学、机器学习等。 以下是 Python 编程语言的一些主要用途&#xff1a; Web 开发&…

UE4运用C++和框架开发坦克大战教程笔记(十二)(第37~39集)

UE4运用C和框架开发坦克大战教程笔记&#xff08;十二&#xff09;&#xff08;第37~39集&#xff09; 37. 延时事件系统38. 协程逻辑优化更新39. 普通按键绑定 37. 延时事件系统 由于梁迪老师是写 Unity 游戏出身的&#xff0c;所以即便 UE4 有自带的 TimeManager 这样的延时…

基于JAVA的考研专业课程管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 考研高校模块2.3 高校教师管理模块2.4 考研专业模块2.5 考研政策模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 考研高校表3.2.2 高校教师表3.2.3 考研专业表3.2.4 考研政策表 四、系统展示五、核…

命令行创建Vue项目

Vue项目创建 1. 打开UI界面 在命令行中&#xff0c;执行如下指令&#xff1a; vue ui 2. 打开项目管理器 3. 创建项目 创建项目的过程&#xff0c;需要联网进行&#xff0c;这可能会耗时比较长的时间&#xff0c;请耐心等待。 windows的命令行&#xff0c;容易卡顿&#xff0c…

WPF 漂亮长方体、正文体简单实现方法 Path实现长方体 正方体方案 WPF快速实现长方体、正方体的方法源代码

这段XAML代码在WPF中实现了一个类似长方体视觉效果的图形 声明式绘制&#xff1a;通过Path、PathGeometry和PathFigure等元素组合&#xff0c;能够以声明方式精确描述长方体每个面的位置和形状&#xff0c;无需编写复杂的绘图逻辑&#xff0c;清晰直观。 层次结构与ZIndex控制…

RabbitMQ之快速入门、上手

前言 学习一样新技术、新框架&#xff0c;最重要的是学习其思想、原理。即原理性思维。 如果是因为工作原因&#xff0c;需要快速上手RabbitMQ&#xff0c;本篇或许适合你。 核心概念 Connection&#xff1a;publisher&#xff0f;consumer 和 broker 之间的 TCP 连接Channel…

Hadoop之Yarn 详细教程

1、yarn 的基本介绍和产生背景 YARN 是 Hadoop2 引入的通用的资源管理和任务调度的平台&#xff0c;可以在 YARN 上运行 MapReduce、Tez、Spark 等多种计算框架&#xff0c;只要计算框架实现了 YARN 所定义的 接口&#xff0c;都可以运行在这套通用的 Hadoop 资源管理和任务调…

nodejs+vue+微信小程序+python+PHP的冷链物流配送系统-计算机毕业设计推荐

对于冷链物流信息调度系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如管理员&#xff1b;首页、用户管理&#xff08;管理员、客户、业务员、配送员&#xff09;客户管理&#xff08;货物信息、客户运输单、车辆信息、调度安排&#xff09;这给管理者的工作带来了巨大…

【机组期末速成】指令系统|机器指令概述|操作数类型与操作类型|寻址方式|指令格式

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;计算机组成原理&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 目录 前言&#xff1a; 一、本章考点总览 二、考点分析 1、以下有关指令系统的说法中错误的是&#xff08; &#xff09;。 2…

【电商项目实战】MD5登录加密及JSR303自定义注解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《电商项目实战》。&#x1f3af;&#x1f3af; &am…

mac安装k8s环境

安装kubectl brew install kubectl 确认一下安装的版本 kubectl version --client 如果想在本地运行kubernetes 需要安装minikube brew install minikube 需要注意安装minikube需要本地的docker服务是启动的 启动 默认连接的是google的仓库 minikube start 指定阿…