最近看到一篇关于爬虫的文章,而自己又正好在爬虫,于是就想写一篇分享下, 让我们一步一步来,
第一步:安装核心爬虫依赖puppeteer, 如果你打开googole.com是404,运行npm i puppeteer
前,先运行set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
; ok,如果没有问题,我们可以在项目根目录(下文简称根目录)下新建index.js
;
//index.js
const puppeteer=require('puppeteer');
复制代码
第二步:选择一个你需要爬取资源的站点, 作为一个b站用户,拿b站作为举例吧,我经常会看排行榜,于是今天我们就爬爬排行榜,地址(www.bilibili.com/ranking/all…
第三步:分析如何爬取站点内容, 打开chrome浏览器,按f12
,按ctrl+shift+c
,你首先就会看到排行榜每个条目对应的一些信息,如果你有过简单的爬虫经验,你大概可能会直接获取页面内容再做提取,当然不是不行,但这种方法一般最后才采取。更优雅的方法是去爬api,要爬api,我们需要将刚刚打开的调试工具切换到network界面,在页面点一点链接跳转,你会发现一些请求记录,自己点开看看,那些是加载这个网页所需要的资源,其中可能就有我们需要的资源,一般来说,xhr选项卡会对应数据内容,但比较神奇,经过调试发现,数据再js选项卡中找到。
然后刷新一下页面,嗯,你会发现,没有了刚刚的发现请求,那数据去哪了呢,api没数据,那数据只能在页面了,估计是为了更好的渲染,数据放在服务端渲染了,api数据只有在切换页面的时候才会有,那么这次就直接用最后手段在页面直接爬取吧。
第四步:写爬取代码, 回到我们的index.js
;
//index.js
const puppeteer=require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPad = devices['iPad landscape'];//https://github.com/GoogleChrome/puppeteer/blob/master/DeviceDescriptors.js
const program = require('commander');
//定义一些命令
program
.version('0.0.1')
.option('-t, --top_10','show top 10')
.parse(process.argv);
//记录结果,如果想写到数据库,自行对接即可
const log4js = require('log4js');
log4js.configure({
appenders: { log: { type: 'file', filename: './log/log.log' } },
categories: { default: { appenders: ['log'], level: 'info' } }
});
const logger = log4js.getLogger('log');
const ifOpenBrowser=false;
const lanchConf={
headless:!ifOpenBrowser,
// executablePath: 'C:/Users/xxx/Downloads/chromium/chrome-win32/chrome.exe',//mac用户自行查看文档更改
};
const sleep=(time)=>{
return new Promise(resolve=>setTimeout(resolve,time))
};
async function repeat(time,fn,gapTime=500){
if(time>0){
// console.log('do fn',time);
await fn();
await sleep(gapTime);
return repeat(--time,fn,gapTime)
}
// console.log('final');
return {msg:'done'}
}
const banList=['.png','.jpg'];
puppeteer.launch(lanchConf).then(async browser => {
//打开一个新的页面
const page = await browser.newPage();
//更改浏览器外观,宽高等
await page.emulate(iPad);
//启用请求拦截
await page.setRequestInterception(true);
//拦截无用请求
page.on('request', interceptedRequest => {
//屏蔽后缀为.png或.jpg的请求;减少资源消耗
if (banList.some(val=>interceptedRequest.url().match(val))){
interceptedRequest.continue();
//本来是要屏蔽的,但图片地址在屏蔽的情况下不能获取,故开启
} else{
interceptedRequest.continue();
}
});
//跳转到我们的目标页面
await page.goto('https://www.bilibili.com/ranking/all/0/0/3',{
waitUntil:'networkidle0'//页面完全加载
});
// 图片实现了懒加载,页面需要滚动到底部,连续点击翻页键一定次数,否则图片地址可能不能拿到
await repeat(20,async ()=>{
await page.keyboard.press('PageDown',200);
},200);
//通过选择器找到目标,如果觉得api难懂,建议使用cheerio辅助
const listHandle = await page.$('.rank-list');
//处理子节点内容,难点在选择器的处理,部分反爬虫的页面不会提供一直不变的选择器
const titles=await listHandle.$$eval('.info .title', nodes => nodes.map(n => n.innerText));
const authors=await listHandle.$$eval('.detail>a>.data-box', nodes => nodes.map(n => n.innerText));
const pts=await listHandle.$$eval('.pts div', nodes => nodes.map(n => n.innerText));
const links=await listHandle.$$eval('.title', nodes => nodes.map(n => n.getAttribute('href')));
const views=await listHandle.$$eval('.detail>.data-box', nodes => nodes.map(n => n.innerText));
const images=await listHandle.$$eval('img', nodes => nodes.map(n => n.getAttribute('src')));
//序列化结果
const res=[];
for(let i=0;i<100;i++){
res[i]={
rank:i+1,
title:titles[i],
author:authors[i],
pts:pts[i],
link:links[i],
view:views[i],
image:images[i]
}
}
//根据命令行输出数据
if (program.top_10) console.log(res.slice(0,10));
//写入数据
logger.info(res);
//关闭浏览器
await browser.close();
});
复制代码
写入上面的内容,认真浏览阅读相关配置选项,然后补全相关依赖npm i commander log4js
; 打开当前项目所在位置的命令行界面,输入node .
程序运行的结果就会输出到根目录下的log目录中,如果想在命令行查看前10条数据,可以运行node . -t
或node . -top_10
即可,
第五步,上传代码到github,
ps:如果运行了set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
,需设置本地chromium路径。另外,如果使用cnpm安装依赖,chromium似乎能正常下载下来,不妨试试