背景
最近公司在做cms系统(内容管理),东西搞好了内容填充却是一大问题,本着节省成本的思想便觉得一部分文章通过爬虫爬取然后进行填充(爬取的内容都是经过授权的,做爬虫只是想节省人力而已)。
其实正常的文章可以直接就后端请求文章进行处理返回,奈何有些文章(例如公众号推文)要打开之后才动态生成文章内容,图片还要懒加载。得知谷歌推出了无界面的chrome, 附送了Puppeteer用于驱动没头的Chome。号称能进行几乎所有能在有界面浏览器的操作,好的,那就简单了,我用在node里直接就用Puppeteer打开页面获取内容进行操作不就完事了吗~~~
附上puppeteer地址:pptr.dev/
安装
windows环境下安装
npm i puppeteerLINUX环境下安装
linux环境下执行上面的安装命令并都不会把所有依赖都装好,需要你手动装,详情看这里:github.com/puppeteer/p…
CentOS需要安装这些:
pango.x86_64
libXcomposite.x86_64
libXcursor.x86_64
libXdamage.x86_64
libXext.x86_64
libXi.x86_64
libXtst.x86_64
cups-libs.x86_64
libXScrnSaver.x86_64
libXrandr.x86_64
GConf2.x86_64
alsa-lib.x86_64
atk.x86_64
gtk3.x86_64
ipa-gothic-fonts
xorg-x11-fonts-100dpi
xorg-x11-fonts-75dpi
xorg-x11-utils
xorg-x11-fonts-cyrillic
xorg-x11-fonts-Type1
xorg-x11-fonts-miscgconf-service
libasound2
libatk1.0-0
libatk-bridge2.0-0
libc6
libcairo2
libcups2
libdbus-1-3
libexpat1
libfontconfig1
libgcc1
libgconf-2-4
libgdk-pixbuf2.0-0
libglib2.0-0
libgtk-3-0
libnspr4
libpango-1.0-0
libpangocairo-1.0-0
libstdc++6
libx11-6
libx11-xcb1
libxcb1
libxcomposite1
libxcursor1
libxdamage1
libxext6
libxfixes3
libxi6
libxrandr2
libxrender1
libxss1
libxtst6
ca-certificates
fonts-liberation
libappindicator1
libnss3
lsb-release
xdg-utils
wget我部署的服务器是CentOS的,所以在执行完 npm i puppeteer 后,安装依赖:
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y启动:
引入
const puppeteer = require("puppeteer");启动浏览器:
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });headless为表示是否无头(即无界面),true表示无界面,false表示有界面,有界面的话启动的时候就像正常浏览器一样。
args为参数选择,亲测在window下不配置该项是没问题的,运行正常,而在centOS上缺会报错,解决办法为sandbox去除沙箱运行,所有还是得配置下该选项:
args: ['--no-sandbox', '--disable-setuid-sandbox']
有了以上得操作,浏览器是能正常调起打开的了。接下来的大家就可以随意发挥了,相关功能API大家需到文档查询,例如怎么模拟点击,输入,怎么查询节点等,应有尽有。
文章爬取
我得需求是爬取前端传过来的文章链接内容,获取文章的标题,摘要,作者,来源,meta等,然后匹配文章主体的图片,上传到自家的cdn,再把地址替换回来,最后整理这些信息返回给前端使用。
例如有一文章链接是www.abc.com,我要实现上述操作:
const page = await browser.newPage;设置一些浏览器选项:
const UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/63.0.3239.84 Chrome/63.0.3239.84 Safari/537.36";
await Promise.all([
page.setUserAgent(UA), // 设置客户端信息
page.setJavaScriptEnabled(true), //允许运行js(有些页面需要运行js才能正常打开)
page.setViewport({ width: 800, height: 600 }) ]); 设置视图大小(无界面的话这项可有可无)
打开文章页:
await page.goto('www.abc.com'); //url根据实际地址定现在page变量代表这个文章页面
获取文章某部分内容:
例如获取头部内容:
let head = await page.$eval("head", el => el.innerHTML);head表示头部标签,el表示获取到的dom节点,利用js可以正常对它操作。
现在是获取它的内部内容,用innerHTML
获取主体内容:
let mainBody = await page.$eval("#js_content", el => el.innerHTML);同理,找到主体内容的id或class或标签,轻松获取内容
内容处理:
内容拿到了,这下可以随意操作了
如匹配某个name为description的content
const meta = head.match(/(?<=<meta.*?name="description".*?content=").*?(?=".*?>)/g)[0]匹配特定格式图片,获取链接爬取图片上传cdn等,具体操作视需求而定。
最后返回处理后的数据格式,完美~~~