0


基于nodejs与Selenium&Puppeteer实现爬虫

一、什么是爬虫

爬虫是一种自动化程序,用于在互联网上收集信息。它可以模拟人类用户的行为,访问网页并提取其中的数据,这些数据可以用于分析、展示或其他应用。可以把互联网比做成一张“大网”,爬虫就是在这张大网上不断爬取信息的程序。

二、手动爬虫

手动爬虫需要人工编写代码来请求网页、解析HTML并提取信息。

自动化爬虫则使用程序自动执行这些任务,通常会使用工具如Selenium或Puppeteer来模拟浏览器行为,从而实现自动化的数据收集。

在爬虫中,cheerio是一个类似于jQuery的库,用于在服务器端解析HTML文档。它提供了一种简单而强大的方式来对HTML文档进行操作和提取数据,可以通过选择器来定位和提取所需的信息,非常适合用于网页数据的抓取和处理。

案例一:爬取图片

const http =require("https");const cheerio =require("cheerio");const download =require("download");let req = http.request("https://www.csdn.net/",(res)=>{let chunks =[];
  res.on("data",(chunk)=>{
    chunks.push(chunk);});
  res.on("end",()=>{let html = Buffer.concat(chunks).toString("utf-8");let $ = cheerio.load(html);let imgArr =Array.prototype.map.call($(".ContentBlock .ContentBlockItem img"),(item)=>encodeURI($(item).attr("src")));
    console.log("imgArr", imgArr);
    Promise.all(imgArr.map((x)=>download(x,"dist"))).then(()=>{
      console.log("files downloaded!");});});});
req.end();

案例二:爬取文案 - 从接口获取

const https =require("https");let url ="https://img-home.csdnimg.cn/data_json/toolbar/toolbar1105.json";let req = https.request(url,{method:"get"},(res)=>{let chunks =[];
  res.on("data",(chunk)=> chunks.push(chunk));

  res.on("end",()=>{let result = Buffer.concat(chunks).toString("utf-8");
    console.log(JSON.parse(result));});});
req.end();

如果遇到请求限制,可以模拟真实浏览器的请求头:

const https =require("https");let url ="https://img-home.csdnimg.cn/data_json/toolbar/toolbar1105.json";let req = https.request(url,{method:"get",headers:{"Host":"www.csdn.net","Connection":"keep-alive","Content-Length":"0","Accept":"*/*","Origin":"https://www.csdn.net/","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36","DNT":"1","Referer":"https://www.csdn.net/?spm=1001.2101.3001.4476","Accept-Encoding":"gzip, deflate","Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8","Cookie":"UM_distinctid=16b8a0c1ea534c-0c311b256ffee7-e343166-240000-16b8a0c1ea689c; bad_idb2f10070-624e-11e8-917f-9fb8db4dc43c=8e1dcca1-9692-11e9-97fb-e5908bcaecf8; parent_qimo_sid_b2f10070-624e-11e8-917f-9fb8db4dc43c=921b3900-9692-11e9-9a47-855e632e21e7........ "}},(res)=>{let chunks =[];
  res.on("data",(chunk)=> chunks.push(chunk));

  res.on("end",()=>{let result = Buffer.concat(chunks).toString("utf-8");
    console.log(JSON.parse(result));});});
req.end();

案例三:爬取图片+文本

const http =require("https");const fs =require("fs");const cheerio =require("cheerio");const download =require("download");const path =require("path");let req = http.request("https://www.csdn.net/",(res)=>{let chunks =[];
  res.on("data",(chunk)=>{
    chunks.push(chunk);});
  res.on("end",()=>{let html = Buffer.concat(chunks).toString("utf-8");let $ = cheerio.load(html);let itemArr =Array.prototype.map.call($(".ContentBlock .ContentBlockItem"),(item)=>{return{title:$(item).find(".title").text(),img:$(item).find("img").attr("src"),};});let jsonData =JSON.stringify(itemArr,null,2);const distDir = path.join(__dirname,"dist");if(!fs.existsSync(distDir)){
      fs.mkdirSync(distDir);}

    fs.writeFile("dist/data.json", jsonData,(err)=>{if(err)throw err;});
    Promise.all(itemArr.map((x)=>download(x.img,"dist"))).then(()=>{
      console.log("Files downloaded!");});});});
req.end();

三、自动化爬虫

Web应用的自动化测试框架,可以创建回归测试来检验软件功能和用户需求,通过框架可以编写代码来启动浏览器进行自动化测试,换言之,用于做爬虫就可以使用代码启动浏览器,让真正的浏览器去打开网页,然后去网页中获取想要的信息,无惧反爬虫手段。

1、Selenium与puppeteer的对比

Selenium和Puppeteer都是用于Web自动化测试和爬虫的工具,它们之间有一些区别:

  1. 语言支持: - Selenium:支持多种编程语言,如Java、Python、JavaScript等。- Puppeteer:主要支持JavaScript,因为是基于Node.js开发的。
  2. 浏览器支持: - Selenium:支持多种浏览器,如Chrome、Firefox、Safari等。- Puppeteer:主要支持Chrome浏览器,因为是由Chrome团队开发的。
  3. 性能: - Puppeteer通常比Selenium更快,因为Puppeteer是直接操作Chrome浏览器而不是通过WebDriver协议来控制浏览器。
  4. 功能: - Puppeteer提供了更多高级功能,如截图、PDF生成、网络请求拦截等,而Selenium相对来说功能较为基础。
  5. 部署: - Puppeteer相对来说更容易部署,因为它是一个Node.js库,安装和配置相对简单。- Selenium需要下载相应的浏览器驱动,并配置环境变量,相对复杂一些。

总的来说,如果需要更高级的功能和更好的性能,可以选择Puppeteer;如果需要跨浏览器支持或使用其他编程语言,可以选择Selenium。

2、Selenium的使用

  1. 根据平台下载需要的webdriver,下载后放入项目根目录,注意: 下载的webdriver要和当前浏览器的版本一致
    浏览器webdriverChromehttps://getwebdriver.com/chromedriver#stableInternetExplorerhttp://selenium-release.storage.googleapis.com/index.htmlEdgehttp://go.microsoft.com/fwlink/?LinkId=619687Firefoxhttps://github.com/mozilla/geckodriver/releases/Safarihttps://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
    2、安装selenium-webdriver的包
npm i selenium-webdriver

3、自动化采集文本及图片

const download =require('download')const fs =require('fs')const path =require("path");const{ Builder, By, Key, until }=require('selenium-webdriver');functionchangeExtension(filename, newExtension){// 使用正则表达式找到文件名的扩展部分,并将其替换为新的扩展名return filename.replace(/\.[^/.]+$/,'.'+ newExtension);}(asyncfunctionstart(){let driver =awaitnewBuilder().forBrowser('chrome').build();await driver.get('https://www.juejin.cn/');await driver.findElement(By.css('.search-input')).sendKeys('前端', Key.ENTER);let elements =await driver.wait(until.elementsLocated(By.className('content-box')),10000);const infos =await Promise.all(elements.map(asyncitem=>{const userName =await item.findElement(By.css('.meta-list .user-popover')).getText()const time =await item.findElement(By.css('.meta-list li:nth-child(2)')).getText()const type =await item.findElement(By.css('.meta-list li:nth-child(3)')).getText()const title =await item.findElement(By.css('.title')).getText()let img ='';const imgDom =await item.findElements(By.css('.thumb'));if(imgDom.length >0){const src =await imgDom[0].getAttribute('src')||'';if(src){
        img =changeExtension(src,'png');}}return{userName: userName,time: time,type: type,title: title,img: img
    };}));let jsonData =JSON.stringify(infos,null,2);const distDir = path.join(__dirname,"dist");if(!fs.existsSync(distDir)){
    fs.mkdirSync(distDir);}
  fs.writeFile("dist/data.json", jsonData,(err)=>{if(err)throw err;});
  Promise.all(infos.map(info=>{if(info.img){download(info.img).then(data=>{
            fs.writeFileSync(`dist/${info.userName}.png`,data)})}})).then(()=>{
    console.log('files downloaded!');});
  console.log('info9', infos)})();

4、滚动加载更多数据并采集

const download =require('download');const fs =require('fs');const path =require("path");const{ Builder, By, Key, until }=require('selenium-webdriver');functionchangeExtension(filename, newExtension){// 使用正则表达式找到文件名的扩展部分,并将其替换为新的扩展名return filename.replace(/\.[^/.]+$/,'.'+ newExtension);}(asyncfunctionstart(){let driver =awaitnewBuilder().forBrowser('chrome').build();await driver.get('https://www.juejin.cn/');await driver.findElement(By.css('.search-input')).sendKeys('前端', Key.ENTER);constcollectData=async()=>{let elements =await driver.wait(until.elementsLocated(By.className('content-box')),10000);const infos =await Promise.all(elements.map(asyncitem=>{const userName =await item.findElement(By.css('.meta-list .user-popover')).getText();const time =await item.findElement(By.css('.meta-list li:nth-child(2)')).getText();const type =await item.findElement(By.css('.meta-list li:nth-child(3)')).getText();const title =await item.findElement(By.css('.title')).getText();let img ='';const imgDom =await item.findElements(By.css('.thumb'));if(imgDom.length >0){const src =await imgDom[0].getAttribute('src')||'';if(src){
              img =changeExtension(src,'png');}}return{userName: userName,time: time,type: type,title: title,img: img
      };}));return infos;};let allInfos =[];const maxScrolls =6;// 最大滚动次数let scrolls =0;let lastInfosLength =0;while(scrolls < maxScrolls){let infos =awaitcollectData();
    allInfos = allInfos.concat(infos);// 滚动到页面底部await driver.executeScript('window.scrollTo(0, document.body.scrollHeight)');await driver.sleep(2000);// 等待页面加载// 检查是否有更多内容加载,如果没有则退出循环if(infos.length === lastInfosLength)break;
    lastInfosLength = infos.length;
    
    scrolls++;}let jsonData =JSON.stringify(allInfos,null,2);const distDir = path.join(__dirname,"dist");if(!fs.existsSync(distDir)){
    fs.mkdirSync(distDir);}
  fs.writeFile("dist/data.json", jsonData,(err)=>{if(err)throw err;});

  Promise.all(allInfos.map(info=>{if(info.img){download(info.img).then(data=>{
            fs.writeFileSync(`dist/${info.userName}.png`,data)})}})).then(()=>{
    console.log('files downloaded!');});
  console.log('info', allInfos);})();

3、selenium常用api的列举

API说明示例

driver.get(url)

打开指定的 URL

await driver.get("https://www.example.com");
driver.findElement(By)

查找并返回匹配的单个 Web 元素

let element = await driver.findElement(By.id("element_id"));
driver.findElements(By)

查找并返回匹配的所有 Web 元素,返回一个列表

let elements = await driver.findElements(By.className("element_class"));
driver.wait(condition, timeout)

等待某个条件成立,最多等待指定的时间

let element = await driver.wait(until.elementLocated(By.id("element_id")), 10000);
driver.executeScript(script, *args)

执行 JavaScript 脚本

let title = await driver.executeScript("return document.title;");
driver.sleep(seconds)

使当前线程休眠指定的秒数

await driver.sleep(5000);
driver.getTitle()

获取当前页面的标题

let title = await driver.getTitle();
driver.getCurrentUrl()

获取当前页面的 URL

let currentUrl = await driver.getCurrentUrl();
driver.close()

关闭当前窗口

await driver.close();
driver.quit()

关闭所有窗口并退出 WebDriver 会话

await driver.quit();
element.click()

点击元素

await element.click();
element.sendKeys(keys)

向元素发送键盘输入

await element.sendKeys("text to input");
element.clear()

清除元素中的内容

await element.clear();
element.getText()

获取元素的文本内容

let text = await element.getText();
element.isDisplayed()

检查元素是否显示

let isDisplayed = await element.isDisplayed();
element.isEnabled()

检查元素是否可用

let isEnabled = await element.isEnabled();
element.isSelected()

检查元素是否被选中

let isSelected = await element.isSelected();
driver.switchTo.frame(frame_reference)

切换到指定的 iframe

await driver.switchTo().frame("frame_name");
driver.switchTo.defaultContent()

切换回主文档

await driver.switchTo().defaultContent();

4、Puppeteer的使用

1、puppereer的安装

npm i puppeteer
# or "yarn add puppeteer"

当你安装 Puppeteer 时,它会下载最新版本的Chromium(170MB Mac,282MB Linux,~280MB Win),以保证可以使用 API。

2、puppeteer截图

const puppeteer =require('puppeteer');(async()=>{const browser =await puppeteer.launch();const page =await browser.newPage();await page.setViewport({width:1920,height:2000});//waitUntil: 'networkidle2' 会等待页面的网络连接在500毫秒内没有超过2个请求时,认为页面加载完成await page.goto('https://www.4399.com/',{waitUntil:'networkidle2'});await page.screenshot({path:'4399.png'});await browser.close();})();

3、自动化滚动并采集

const puppeteer =require('puppeteer');const fs =require('fs');const path =require('path');const download =require('download');(async()=>{const browser =await puppeteer.launch({headless:true});const page =await browser.newPage();await page.goto('https://www.juejin.cn/');// 输入搜索关键字并提交await page.type('.search-input','后端');await page.keyboard.press('Enter');await page.waitForSelector('.content-box',{timeout:10000});// 滚动并采集数据const infos =[];let previousHeight;let scrollCount =0;const maxScrolls =15;// 最大滚动次数while(scrollCount < maxScrolls){const newInfos =await page.evaluate(()=>{functionchangeExtension(filename, newExtension){return filename.replace(/\.[^/.]+$/,'.'+ newExtension);}const items = document.querySelectorAll('.content-box');return Array.from(items).map(item=>{const userName = item.querySelector('.meta-list .user-popover')?.innerText ||'';const time = item.querySelector('.meta-list li:nth-child(2)')?.innerText ||'';const type = item.querySelector('.meta-list li:nth-child(3)')?.innerText ||'';const title = item.querySelector('.title')?.innerText ||'';let img ='';const imgDom = item.querySelector('.thumb');if(imgDom){const src = imgDom.getAttribute('src');if(src){
            img =changeExtension(src,'png');}}return{ userName, time, type, title, img };});});

    infos.push(...newInfos);
    previousHeight =await page.evaluate('document.body.scrollHeight');await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');awaitnewPromise(resolve=>setTimeout(resolve,2000));// 等待加载新的内容const newHeight =await page.evaluate('document.body.scrollHeight');if(newHeight === previousHeight){break;}

    scrollCount++;
    console.log(`开始采集${scrollCount}页数据`)}// 去重处理const uniqueInfos = Array.from(newSet(infos.map(a=>JSON.stringify(a)))).map(b=>JSON.parse(b));const jsonData =JSON.stringify(uniqueInfos,null,2);const distDir = path.join(__dirname,"dist");if(!fs.existsSync(distDir)){
    fs.mkdirSync(distDir);}
  fs.writeFileSync(path.join(distDir,"data.json"), jsonData);await Promise.all(uniqueInfos.map(async(info)=>{if(info.img){const data =awaitdownload(info.img);
      fs.writeFileSync(path.join(distDir,`${info.userName}.png`), data);}}));

  console.log('采集完成');
  console.log('info:', uniqueInfos);await browser.close();})();

Puppeteer是一个由Google开发的无头浏览器库,可以在后台执行浏览器操作,不会弹出可见的浏览器窗口,因此可以更高效地进行网页自动化和爬虫操作。也可以通过设置headless 为false,来打开浏览器查看采集过程:await puppeteer.launch({ headless: false});

5、puppeteer常用api的列举
API说明示例

puppeteer.launch(options)

启动一个新的浏览器实例

const browser = await puppeteer.launch({ headless: true });
browser.newPage()

创建一个新的页面

const page = await browser.newPage();
page.goto(url)

导航到指定的 URL

await page.goto('https://www.example.com');
page.waitForSelector(selector)

等待指定的选择器出现在页面中

await page.waitForSelector('#element_id');
page.$(selector)

查找并返回匹配的单个元素

const element = await page.$('#element_id');
page.$$(selector)

查找并返回匹配的所有元素,返回一个数组

const elements = await page.$$('.element_class');
page.evaluate(pageFunction, ...args)

在页面上下文中执行 JavaScript 代码

const title = await page.evaluate(() => document.title);
page.click(selector)

点击指定的元素

await page.click('#element_id');
page.type(selector, text)

在指定的元素中输入文本

await page.type('#input_id', 'text to input');
page.screenshot(options)

截取页面截图

await page.screenshot({ path: 'screenshot.png' });
page.pdf(options)

将页面保存为 PDF

await page.pdf({ path: 'page.pdf', format: 'A4' });
page.setViewport(viewport)

设置页面视口大小

await page.setViewport({ width: 1280, height: 800 });
page.content()

获取页面的 HTML 内容

const content = await page.content();
page.title()

获取页面的标题

const title = await page.title();
page.url()

获取页面的 URL

const url = await page.url();
page.waitForTimeout(milliseconds)

等待指定的时间

await page.waitForTimeout(5000);
page.waitForNavigation(options)

等待页面导航

await page.waitForNavigation();
page.waitForFunction(pageFunction, options, ...args)

等待指定的函数返回 true

await page.waitForFunction(() => document.querySelector('#element_id') !== null);
elementHandle.click()

点击元素

await element.click();
elementHandle.type(text)

在元素中输入文本

await element.type('text to input');
elementHandle.evaluate(pageFunction, ...args)

在元素上下文中执行 JavaScript 代码

const text = await element.evaluate(el => el.textContent);
browser.close()

关闭浏览器实例

await browser.close();

5、技术文档

selenium中文文档: https://www.selenium.dev/zh-cn/documentation/webdriver/getting_started/using_selenium/

puppeteet中文文档:https://puppeteer.bootcss.com/

四、爬虫的危害

虽然爬虫在数据收集和分析方面有许多合法和有益的应用,但不当使用爬虫也会带来一系列的危害。

  1. 服务器负载增加:- 大量的爬虫请求会对目标网站的服务器造成过大的负载,可能导致服务器性能下降,甚至崩溃。这不仅影响网站的正常运营,还可能导致用户体验变差。
  2. 数据隐私和安全问题:- 爬虫可能会获取到敏感信息或个人数据,导致数据泄露和隐私问题。这种行为可能违反数据保护法律和法规,给网站所有者和用户带来法律风险。
  3. 知识产权侵权:- 爬虫可能会未经授权地复制和使用网站上的内容,侵犯版权和其他知识产权。这种行为可能导致法律纠纷和经济损失。等。。。

为了避免上述危害,使用爬虫时应遵循以下原则:

  • 遵守网站的robots.txt文件和使用条款。
  • 避免频繁访问,设置合理的访问间隔。
  • 不获取和使用敏感信息或个人数据。
  • 遵守相关法律法规,尊重知识产权。

通过合理和合法地使用爬虫技术,可以有效地获取所需数据,同时避免对目标网站和其他相关方造成不良影响。

标签: selenium 爬虫 puppet

本文转载自: https://blog.csdn.net/weixin_44289670/article/details/139355820
版权归原作者 Moyo203 所有, 如有侵权,请联系我们删除。

“基于nodejs与Selenium&Puppeteer实现爬虫”的评论:

还没有评论