Skip to main content

3 posts tagged with "puppeteer"

View All Tags

puppeteer爬虫400,412错误

· 4 min read

需求场景

最近发现一直爬取的网站升级了,无法通过request模块简单获取了。考虑到其实自己爬取的数据访问量并不大,实际也就我一个人偶尔访问一下,不会是因为我的爬取而去更新反爬虫机制,猜测是其他原因导致了网站升级了反爬虫机制。

解决过程

通过postman测试,可以发现一直返回412错误,后来在postman中携带了cookie,发现可以正常访问,所以通过在request请求中注入了我自己的cookie解决了问题。后来发现还是too young啊,cookie是有时效性的,一段时间后就会消失。于是想到可以通过puppeteer爬取而不是request,可奇怪的问题出现了,puppeteer访问居然显示400,搜了一大圈可以很确定是网站的反爬虫机制才会出现这个情况,参见puppeteer get 400 Error,这个机制有点666啊,一直以为puppeteer是万能的大招,后来通过搜索反爬虫机制,了解到识别puppeteer和普通chrome的大致原理,参见浏览器如何识别selenium及爬虫如何绕过反爬,到这里就很清楚了,我们需要改变puppeteer的默认参数来模拟真实的浏览器,搜索到puppeteer相关的代码,参见puppeteer无头模式下反反爬配置集合,后来在实际测试中,我需要爬取的网站只要删除默认的webdriver字段即可,并没有那么高级。

核心代码

const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.evaluateOnNewDocument(() => { //在每个新页面打开前执行以下脚本
const newProto = navigator.__proto__;
delete newProto.webdriver; //删除navigator.webdriver字段
navigator.__proto__ = newProto;
window.chrome = {}; //添加window.chrome字段,为增加真实性还需向内部填充一些值
window.chrome.app = {"InstallState":"hehe", "RunningState":"haha", "getDetails":"xixi", "getIsInstalled":"ohno"};
window.chrome.csi = function(){};
window.chrome.loadTimes = function(){};
window.chrome.runtime = function(){};
Object.defineProperty(navigator, 'userAgent', { //userAgent在无头模式下有headless字样,所以需覆写
get: () => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36",
});
Object.defineProperty(navigator, 'plugins', { //伪装真实的插件信息
get: () => [{"description": "Portable Document Format",
"filename": "internal-pdf-viewer",
"length": 1,
"name": "Chrome PDF Plugin"}]
});
Object.defineProperty(navigator, 'languages', { //添加语言
get: () => ["zh-CN", "zh", "en"],
});
const originalQuery = window.navigator.permissions.query; //notification伪装
window.navigator.permissions.query = (parameters) => (
parameters.name === 'notifications' ?
Promise.resolve({ state: Notification.permission }) :
originalQuery(parameters)
);
})
await page.goto('')

vue单页应用seo解决方案rendertron

· 4 min read

需求场景

现在越来越多的前端项目是基于vue,react,angular做的单页应用,可是单页应用的一大痛点就是seo不友好,虽然百度统计支持单页应用,但是seo不行呀。

可行解决方案

目前有三种解决方案。方案一预渲染,方案二服务端渲染,方案三针对seo的渲染。方案一和方案二都会原有项目进行改动,其中方案二改动方案最大。所以目前来说,改动成本最小的就是方案三了。本文采用的方式就是针对seo渲染的方案三。

rendora和rendertron

目前有rendora和rendertron两种主流开源方案,rendora是基于go语言的,支持docker部署。rendertron是由google团队基于node开发的,背靠大树。从目前的star数以及更新状况来看,rendertron要更好些。rendora已经一年多没有更新,并且安装过程中有些bug还无法解决。

rendertron原理

rendertron会启动一个node项目,对请求进行拦截,如果是搜索引擎发来的请求,就会被拦截,通过puppeteer渲染想要请求的地址,并将渲染好的html返回给搜索引擎,而普通用户的请求还是转发到对应的后端服务器上

环境技术栈

操作系统ubuntu18.04
前端vue
后端接口nestjs
服务器nginx
seo预渲染rendertron

步骤一:rendertron安装启动

git clone https://github.com/GoogleChrome/rendertron.git
cd rendertron
npm install
npm run build
npm run start

rendertron在启动过程中可能会报错,原因是puppeteer的环境没有安装完,需要参考puppeteer官方文档安装对应的包chrome-headless-doesnt-launch-on-unix
如果启动没有问题了,之后可以通过pm2加入后台进程

步骤二:配置nginx拦截搜索引擎

  location / {
if ($http_user_agent ~* "bot|bing|yandex|duckduckgo|baiduspider|googlebot|360spider|sogou spider") {
rewrite ^/(.*) /render/https://sz.urcloud.co/$1 break;
proxy_pass http://127.0.0.1:3001; #rendertron端口
}
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000; #后端api接口
}

步骤三:测试搜索引擎访问和普通用户访问

通过postman在请求header中添加User-Agent:baiduspider来测试蜘蛛访问和普通用户访问,如果正常的话,可以看到搜索引擎访问返回的是渲染好的html页面。

后续说明

可以看到这样改只是修改了nginx配置以及新增了一个rendertron项目,对原有项目没有任何改动,rendertron官方是有node,python等后端语言的插件的,通过nginx转发到后端,在后端项目里面判断是否搜索引擎来决定是否转发url到rendertron项目下。这样会更好,不过对原有项目做了一些小的调整,感兴趣的同学可以去研究一下。

puppeteer安装太慢,更换为国内镜像

· One min read

需求场景

由于墙的原因,采用npm i puppeteer -S安装puppeteer非常的慢。博主就深受折磨,搜遍了全网,在没有梯子的情况下采用淘宝镜像安装是最快的方式,快的飞起的那种。

解决方法

PUPPETEER_DOWNLOAD_HOST=https://npm.taobao.org/mirrors npm i puppeteer -S 

使用此方法安装快速而又无副作用,可以说是最优解了。