>  기사  >  웹 프론트엔드  >  노드는 Puppeteer를 크롤러로 사용합니다.

노드는 Puppeteer를 크롤러로 사용합니다.

php中世界最好的语言
php中世界最好的语言원래의
2018-06-07 14:06:002577검색

이번에는 Node의 Puppeteer를 크롤러로 사용하는 방법을 알려드리겠습니다. Node의 Puppeteer를 크롤러로 사용할 때 주의할 점은 무엇인가요? 다음은 실제 사례입니다.

아키텍처 다이어그램

Puppeteer 아키텍처 다이어그램

  1. Puppeteer는 devTools

  2. Browser 여러 페이지를 가질 수 있는 브라우저(chroium) 인스턴스

  3. 페이지에 최소한 프레임이 포함되어 있습니다. 페이지

  4. Frame에는 javascript 실행을 위한 실행 환경이 하나 이상 있고, 여러 실행 환경을 확장할 수도 있습니다.

Foreword

최근에 데스크탑 컴퓨터를 사고 싶은데 노트북에 있는 i5가 웹 페이지를 엽니다. 그리고 vsc를 사용하면 확실히 렉이 있어서 i7 + GTX1070TI나 GTX1080TI 컴퓨터를 장착하려고 합니다. 타오바오에서 직접 검색하려면 페이지도 너무 많고, 사진도 너무 많아서 제 두뇌 능력이 감당할 수 없을 정도입니다. 일부 데이터를 크롤링하고 최근 가격 추세를 그래픽으로 분석합니다. 그래서 관련 데이터를 크롤링하기 위해 Puppeteer를 사용하여 크롤러를 작성했습니다.

퍼피티어란 무엇인가요?

Puppeteer는 DevTools 프로토콜을 통해 헤드리스 Chrome 또는 Chromium을 제어하기 위한 고급 API를 제공하는 노드 라이브러리입니다. 또한 전체(헤드리스가 아닌) Chrome 또는 Chromium을 사용하도록 구성할 수도 있습니다.

간단히 말하면 이 제품은 devtool을 통해 헤드리스 모드에서 크롬이나 크롬을 제어할 수 있는 고급 API를 제공하는 노드 라이브러리입니다.

와cherio

cherrico의 차이점은 본질적으로 jquery와 유사한 구문을 사용하여 HTML 문서를 조작하는 라이브러리입니다. ajax를 사용하면 해당 데이터를 크롤링할 수 없습니다. Puppeteer는 브라우저의 운영 환경을 시뮬레이션하고, 웹사이트 정보를 요청하고, 웹사이트의 내부 로직을 실행할 수 있습니다. 그런 다음 WS 프로토콜을 통해 페이지 내부의 데이터를 동적으로 가져오고 시뮬레이션된 작업(클릭, 슬라이드, 호버 등)을 수행할 수 있으며 페이지 점프 및 다중 페이지 관리를 지원합니다. 노드의 스크립트를 브라우저의 내부 환경에 삽입하여 실행할 수도 있습니다. 즉, 웹 페이지에서 할 수 있는 모든 작업을 수행할 수 있고 수행할 수 없는 작업도 수행할 수 있습니다.

시작

이 글은 단계별 튜토리얼이 아니므로 Puppeteer API에 대한 기본 지식이 필요합니다. 이해가 안 되시면 공식 소개를 먼저 읽어주세요
Puppeteer 공식 사이트
PuppeteerAPI

먼저 크롤링할 웹사이트 정보를 관찰합니다. GTX1080

크롤링하려는 타오바오 웹페이지입니다. 가운데에 있는 제품 항목만 크롤링해야 할 콘텐츠입니다. 그 구조를 자세히 분석한 결과 전면- end에는 그러한 기능이 있습니다.

내가 사용하는 Typescript는 Puppetter 및 관련 라이브러리에 대한 완전한 API 팁을 얻을 수 있습니다. TS를 모른다면 관련 코드를 ES 구문으로 변경하기만 하면 됩니다.

// 引入一些需要用到的库以及一些声明
import * as puppeteer from 'puppeteer' // 引入Puppeteer
import mongo from '../lib/mongoDb' // 需要用到的 mongodb库,用来存取爬取的数据
import chalk from 'chalk' // 一个美化 console 输出的库
const log = console.log // 缩写 console.log
const TOTAL_PAGE = 50 // 定义需要爬取的网页数量,对应页面下部的跳转链接
// 定义要爬去的数据结构
interface IWriteData { 
 link: string // 爬取到的商品详情链接
 picture: string // 爬取到的图片链接
 price: number // 价格,number类型,需要从爬取下来的数据进行转型
 title: string // 爬取到的商品标题
}
// 格式化的进度输出 用来显示当前爬取的进度
function formatProgress (current: number): string { 
 let percent = (current / TOTAL_PAGE) * 100
 let done = ~~(current / TOTAL_PAGE * 40)
 let left = 40 - done
 let str = `当前进度:[${''.padStart(done, '=')}${''.padStart(left, '-')}]  ${percent}%`
 return str
}

다음으로 크롤러 메인 로직에 들어갑니다

// 因为我们需要用到大量的 await 语句,因此在外层包裹一个 async function
async function main() {
 // Do something
}
main()
// 进入代码的主逻辑
async function main() {
 // 首先通过Puppeteer启动一个浏览器环境
 const browser = await puppeteer.launch()
 log(chalk.green('服务正常启动'))
 // 使用 try catch 捕获异步中的错误进行统一的错误处理
 try {
  // 打开一个新的页面
  const page = await browser.newPage()
  // 监听页面内部的console消息
  page.on('console', msg => {
   if (typeof msg === 'object') {
    console.dir(msg)
   } else {
    log(chalk.blue(msg))
   }
  })
  // 打开我们刚刚看见的淘宝页面
  await page.goto('https://s.taobao.com/search?q=gtx1080&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180416&ie=utf8')
  log(chalk.yellow('页面初次加载完毕'))
  // 使用一个 for await 循环,不能一个时间打开多个网络请求,这样容易因为内存过大而挂掉
  for (let i = 1; i <= TOTAL_PAGE; i++) {
   // 找到分页的输入框以及跳转按钮
   const pageInput = await page.$(`.J_Input[type=&#39;number&#39;]`)
   const submit = await page.$(&#39;.J_Submit&#39;)
   // 模拟输入要跳转的页数
   await pageInput.type(&#39;&#39; + i)
   // 模拟点击跳转
   await submit.click()
   // 等待页面加载完毕,这里设置的是固定的时间间隔,之前使用过page.waitForNavigation(),但是因为等待的时间过久导致报错(Puppeteer默认的请求超时是30s,可以修改),因为这个页面总有一些不需要的资源要加载,而我的网络最近日了狗,会导致超时,因此我设定等待2.5s就够了
   await page.waitFor(2500)
   // 清除当前的控制台信息
   console.clear()
   // 打印当前的爬取进度
   log(chalk.yellow(formatProgress(i)))
   log(chalk.yellow(&#39;页面数据加载完毕&#39;))
   // 处理数据,这个函数的实现在下面
   await handleData()
   // 一个页面爬取完毕以后稍微歇歇,不然太快淘宝会把你当成机器人弹出验证码(虽然我们本来就是机器人)
   await page.waitFor(2500)
  }
  // 所有的数据爬取完毕后关闭浏览器
  await browser.close()
  log(chalk.green(&#39;服务正常结束&#39;))
  // 这是一个在内部声明的函数,之所以在内部声明而不是外部,是因为在内部可以获取相关的上下文信息,如果在外部声明我还要传入 page 这个对象
  async function handleData() {
   // 现在我们进入浏览器内部搞些事情,通过page.evaluate方法,该方法的参数是一个函数,这个函数将会在页面内部运行,这个函数的返回的数据将会以Promise的形式返回到外部 
   const list = await page.evaluate(() => {
    
    // 先声明一个用于存储爬取数据的数组
    const writeDataList: IWriteData[] = []
    // 获取到所有的商品元素
    let itemList = document.querySelectorAll('.item.J_MouserOnverReq')
    // 遍历每一个元素,整理需要爬取的数据
    for (let item of itemList) {
     // 首先声明一个爬取的数据结构
     let writeData: IWriteData = {
      picture: undefined,
      link: undefined,
      title: undefined,
      price: undefined
     }
     // 找到商品图片的地址
     let img = item.querySelector('img')
     writeData.picture = img.src
     // 找到商品的链接
     let link: HTMLAnchorElement = item.querySelector('.pic-link.J_ClickStat.J_ItemPicA')
     writeData.link = link.href
     // 找到商品的价格,默认是string类型 通过~~转换为整数number类型
     let price = item.querySelector('strong')
     writeData.price = ~~price.innerText
     
     // 找到商品的标题,淘宝的商品标题有高亮效果,里面有很多的span标签,不过一样可以通过innerText获取文本信息
     let title: HTMLAnchorElement = item.querySelector('.title>a')
 
     writeData.title = title.innerText
     // 将这个标签页的数据push进刚才声明的结果数组
     writeDataList.push(writeData)
    }
    // 当前页面所有的返回给外部环境
    return writeDataList
    
   })
   // 得到数据以后写入到mongodb
   const result = await mongo.insertMany('GTX1080', list)
   log(chalk.yellow('写入数据库完毕'))
  }
 } catch (error) {
  // 出现任何错误,打印错误消息并且关闭浏览器
  console.log(error)
  log(chalk.red('服务意外终止'))
  await browser.close()
 } finally {
  // 最后要退出进程
  process.exit(0)
 }
}

Thinking

1. 왜 Typescript를 사용하나요?

Typescript는 사용하기 쉽기 때문에 Puppeteer의 API를 모두 외울 수도 없고, 일일이 확인하고 싶지도 않기 때문에 TS를 사용하면 지능적인 알림을 제공하고 철자법으로 인한 낮은 수준의 오류를 피할 수 있습니다. 기본적으로 TS를 사용한 후 코드를 다시 입력하면 됩니다

puppeteer.png

2. 크롤러의 성능 문제는 무엇인가요?

Puppeteer는 브라우저를 시작하고 내부 로직을 실행하기 때문에 많은 메모리를 차지합니다. 콘솔을 보면 이 노드 프로세스는 약 300MB의 메모리를 차지합니다.

내 페이지는 하나씩 크롤링됩니다. 더 빠르게 크롤링하려면 여러 프로세스를 시작할 수 있습니다. V8은 단일 스레드이므로 하나의 프로세스 내에서 여러 페이지를 여는 것은 의미가 없습니다. 매개변수는 다른 노드 프로세스를 엽니다. 물론 노드 클러스터를 통해 구현될 수도 있습니다. 본질은 동일합니다. 한편으로는 크롤링 프로세스 중에 다른 대기 시간을 설정합니다. 반면에 웹페이지가 로딩되기를 기다리는 것입니다. 타오바오가 크롤러 폭탄 인증코드를 인식하는 것을 방지하는 것입니다

3. Puppeteer의 다른 기능

Puppeteer의 일부 기본 기능만 사용합니다. 실제로 Puppeteer에는 더 많은 기능이 있습니다. 예를 들어, 노드의 처리 기능을 도입하여 브라우저 내부에서 실행되도록 하고, 현재 페이지를 pdf나 png 이미지로 저장합니다. 또한 const browser = wait puppeteer.launch({ headless: false })를 통해 인터페이스 효과가 있는 브라우저를 시작할 수 있으며 크롤러가 어떻게 작동하는지 확인할 수 있습니다. 또한, 로그인이 필요한 일부 웹사이트의 경우, 인증코드를 제3자에게 처리를 위탁하고 싶지 않은 경우에는 헤드리스 기능을 끄신 후 프로그램에서 대기 시간을 설정하고 일부 인증을 수동으로 완료하시면 됩니다. 로그인 목적을 달성합니다.

물론 Google은 데이터 크롤링에만 사용되는 것이 아니라 일부 자동화된 성능 분석, 인터페이스 테스트, 프런트엔드 웹사이트 모니터링 등에 사용되는 멋진 라이브러리를 제작했습니다.

4. 다른 측면 생각

일반적으로 데이터를 크롤링하는 크롤러를 만드는 것은 많은 기본 기술을 테스트하는 비교적 복잡한 연습 프로젝트입니다. 이 크롤러에서는 비동기가 여러 번 사용되므로 비동기, 약속 및 기타 관련 지식에 대한 완전한 이해가 필요합니다. . 데이터를 수집하기 위해 DOM을 분석할 때 DOM 속성을 얻기 위해 기본 메서드도 여러 번 사용했습니다. (웹 사이트에 jquery가 있으면 직접 사용할 수도 있고 그렇지 않은 경우 외부 주입이 필요합니다. 보고를 피하기 위해 typescript에서 일부 구성이 필요합니다. 인식할 수 없는 $ 변수를 사용하여 jquery 구문을 통해 DOM을 조작할 수 있으며 DOM 관련 API에 대한 숙련도를 검사했습니다.

또한 이는 프로세스 지향 프로그래밍일 뿐입니다. 이를 작업용 클래스로 완전히 캡슐화할 수 있습니다. 이는 ES의 OOP 이해도 테스트합니다.

이 기사의 사례를 읽고 나면 방법을 마스터했다고 생각합니다. PHP 중국어 웹사이트의 다른 관련 기사에도 관심을 가져주세요!

추천 도서:

vuex의 상태 객체 사용 방법 요약

Angular를 사용하여 구성 요소를 시작하는 방법

위 내용은 노드는 Puppeteer를 크롤러로 사용합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.