使用node.js+puppeteer 实现一次稍微复杂的爬虫
1.一些方法的说明均在代码中有注释
const puppeteer = require('puppeteer');
const fs = require('fs');
const userAgents = require('./userAgents.js');
(async ()=>{
//代理地址
let proxy = '127.0.0.1:8888';
//创建 操作浏览器的 对象(木偶人)
let browser = await puppeteer.launch({
headless:false,
args: [
`--proxy-server=${proxy}`, // Charles proxy 设置代理
]
});
try{
// 打开浏览器
const page = await browser.newPage();
//将拦截请求的开关 开启 设置为true 之后 就可以 监听 request 和 response 事件 可以拦截 请求 和请求响应
await page.setRequestInterception(true);
//设置 request 监听事件 (只要有请求发送就会触发)
page.on('request', interceptedRequest => {
//判断如果是 图片请求 就直接拦截
if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))
interceptedRequest.abort(); //终止请求
else
interceptedRequest.continue();//弹出
});
// 数据爬取 主要逻辑
//数据最终存储 的数组
const array=[];
//进入 亚马逊 搜索页面
await page.goto('https://www.amazon.cn/?nocache=1551401627147',{timeout:300000});
//选择出 搜索框
const search = await page.$('#twotabsearchtextbox');
//输入 iphonex
await search.type('ipad');
//选择出 查询按钮
const searchSubmit = await page.$('#nav-search > form > div.nav-right > div > input');
//点击查询
await searchSubmit.click();
//等待2.5s
await page.waitFor(2500);
//获取当前 搜索结果页 路径
let searchResult = await page.evaluate(()=>{return document.URL;});
console.log('结果页面',searchResult);
//商品页面 下一页按钮
let searchResultNext = '';
//下一页按钮路径
let searchResultNextUrl= '';
//获取商品搜索结果 页面 下一页按钮
searchResultNext = await page.$('#pagnNextLink');
while(1){
//获取当前页的所有商品 链接
console.log('运行');
//等待2s执行
await page.waitFor(2000);
//获取当前页面的所有商品
const firstGoods = await page.$$('li > div > div.a-row.a-spacing-base > div > div > a');
console.log('商品数量',firstGoods.length);
for(let i=0;i<firstGoods.length;i++){
//每次获取完一个店铺 之后 回到 搜索结果页面 重新获取 所有 商品链接
let goods = await page.$$('li > div > div.a-row.a-spacing-base > div > div > a');
//将goods 转化为 数组 (之前是类数组)
goods = Array.from(goods);
let arr=[];
let item = goods[i];
//获取商品链接
let goodsUrl = await page.evaluate((item)=>{return item.href;},item);
//跳转到 该商品地址
await page.goto(goodsUrl,{timeout:300000});
//等待 1 s
await page.waitFor(1000);
//获取店铺名称标签
const store = await page.$('#ddmMerchantMessage > a');
//获取店铺名称
const storeName = await page.evaluate((store)=>{return store ? store.innerHTML:'';},store)
console.log('获取店铺名store');
//获取包含 商品评分信息的元素
const grade = await page.$('#acrPopover > span.a-declarative > a > i.a-icon.a-icon-star.a-star-4-5 > span');
// 获取 商品评分
const gradeNumber = await page.evaluate((grade)=>{return grade? grade.innerHTML:''},grade);
console.log('获取评分gradeNumber');
//等待 2.5s
await page.waitFor(2500);
//获取店铺 中 查看所有评论的按钮
const goodsAllMessagePage = await page.$('#reviews-medley-footer > div.a-row.a-spacing-large > a');
console.log('goodsAllMessagePage');
//判断当前 店铺 该商品 是否有评论
if(goodsAllMessagePage){
//获取 查看所有评论 元素
const address = await page.evaluate((goodsAllMessagePage)=>{return goodsAllMessagePage.href;},goodsAllMessagePage);
//等待1s
await page.waitFor(1000);
//跳转到 所有评论页面
await page.goto(address,{timeout:300000});
while(1){
//所有评论 页面的 下一页按钮
const messageNextPage = await page.$('li.a-last > a');
//调用 getData 获取当前页的数据
await getData(arr);
//模拟 点击跳转
if(messageNextPage){
await messageNextPage.click();
console.log('点击商品评价下一页');
}else{
break;
}
console.log('还在获取数据');
await page.waitFor(5000);//休息5s 防止淘宝发现我们是机器人
}
}
//生成每个店铺的信息
let storeObj={
storeName:storeName,
grade:gradeNumber,
message:JSON.stringify(arr)
}
//放入数组
array.push(storeObj);
console.log('所有数据数量:',array.length);
await page.goto(searchResult,{timeout:300000});
}
console.log('本商品评论获取完毕');
if(searchResultNext){
//获取 搜索结果 下一页 元素
searchResultNext = await page.$('#pagnNextLink');
//获取搜索结果 下一页 的 路径
searchResultNextUrl = await page.evaluate((searchResultNext)=>{return searchResultNext.href;},searchResultNext);
//跳转到 搜索结果 页面
await page.goto(searchResultNextUrl,{timeout:300000});
//等待 2s
await page.waitFor(2000);
//更新 搜索结果页面的路径
searchResult = await page.evaluate(()=>{return document.URL;});
console.log('更新翻页的搜索结果页面',searchResult);
//随机更换 user-agent
await page.setUserAgent(userAgents[Math.floor(Math.random()*userAgents.length)]);
}else{
break;
}
}
await fs.writeFile('index.json',JSON.stringify(array),()=>{
console.log('写入成功!');
})
//关闭浏览器
await browser.close();
//找到当前页的所有商品链接
async function getData(arr){
//在当前页面执行脚本
const list = await page.evaluate(()=>{
let alist = []
//获取所有的商品
let pageGoods = document.querySelectorAll('#cm_cr-review_list>div.review');
for(let items of pageGoods){
let data={
message:'',//商品评价
}
//获取商品评价
const message = items.querySelector('div span[data-hook="review-body"]');
data.message = message.innerHTML;
alist.push(data);
}
return alist;
})
list.forEach(item=>{
arr.push(item);
})
console.log('数组长度:',list.length);
}
}catch(e){
console.log('出错了!');
console.log(e);
}
})()