一、封不了的爬虫
做过爬虫的童鞋可能大多数是用python去模拟请求爬数据的,有些封IP的网站数据,可能是去页面上爬的demo,这种效率太低,今天让我们借用devtools,去高效的爬数据,本篇技术需要你熟悉谷歌插件的开发,这里我就不赘述了,不了解的可以参考这个:www.cnblogs.com/liuxianan/p…
二、起步配置谷歌插件
我的目录结构如下:
分别介绍下功能吧!
manifest.js 插件核心配置
background.html 背景页面,运行在浏览器上
popup.html 点击浏览器上的插件图标弹出的页面
devtools.html devtools页面,我们使用的vue-devtools也是这个
他们分别对应自己的js,这里同样可以引入jquery.js,下面我贴上manifest.js的配置,可参考修改
{
"manifest_version": 2,
"name": "lykj",
"version": "1.0.0",
"description": "补充浏览器功能",
"icons":
{
"16": "img/w6.png",
"48": "img/w6.png",
"128": "img/w6.png"
},
"background":
{
"page": "background.html"
},
"browser_action":
{
"default_icon": "img/w6.png",
"default_title": "谷歌插件",
"default_popup": "popup.html"
},
"content_scripts":
[
{
"matches": ["<all_urls>"],
"js": ["js/jquery.js","js/content_script.js"],
"run_at": "document_start"
}
],
"permissions":
[
"cookies",
"contextMenus",
"tabs",
"<all_urls>",
"notifications",
"webRequest",
"webRequestBlocking",
"storage",
"http://*/*",
"https://*/*"
],
"omnibox": { "keyword" : "go" },
"default_locale": "zh_CN",
"devtools_page": "devtools.html"
}
三、开始搞数据
配置好我们的插件后,首先我们需要明确下功能怎么开发,我这里画了个简单的图
大体流程是我们需要在content_script.js里面先与服务器交互获取我们想要打开的页面并且植入cookie信息,模拟登录,然后通信给background.js中,去操作打开页面,关闭页面,同时我们需要在content_script.js中配置一些信息,因为可以我们需要模拟点击实现接口调用,再有devtools拦截,上报数据。
核心代码: content_script.js
// 请求接口的url
const url = 'xxxxx'
let number = 0
let timer = null
let getClose = false;
window.onload=function(){
// 获取url上的信息
let history=window.location.href.split('?')[1];
let nowRouter =window.location.href.split('?')[0];
let query={
awemeId: '',
roomId: ''
};
if(history&&history.length>0){
history.split('&').forEach(function(item){
let obj=item.split('=')
query[obj[0]]=obj[1]
})
}
// 获取#号后面挂载的参数
let therePage = history.split('#')[1];
// 在登录页面统一操作打开逻辑
if(nowRouter==='xxx'){
// 发起ajax请求获取cookie信息并且将cookie信息注入到当前域名实现登录
$.ajax ({
url:url+'xxxxxxx',
type: 'get',
data: {},
dataType:'json',
success: function(result){
// 向background通信
chrome.runtime.sendMessage({
action:"cookie",
content:result.data,
awemeId:query.awemeId,
roomId:query.roomId,
})
},
error:function(error){
console.log(error)
}
})
}
// 挂载页面Js
if(nowRouter==='https://baidu.com'){
if(therePage){
switch (therePage) {
case 'data1':
setTimeout(()=>{
handleDemo.f()
},2000)
break;
case 'data2':
setTimeout(()=>{
handleDemo.g()
},2000)
break;
default:
break;
}
}
}
}
bakground.js
const url = 'xxxxx'
let createTabOpthon={};
let httplist=[];
let awemeId='';
let roomId='';
$("#open").click(function(){
chrome.tabs.create({url : 'https://www.baidu.com'})
})
// 提交数据到服务器
function pushData(api,data,id){
$.ajax ({
url:url+'xxxx',
type: 'post',
dataType:'json',
data:JSON.stringify({
}),
contentType: "application/json; charset=utf-8",
success: function(result){
if(result.code === 'Success'){
console.log('发送成功',api,id)
}else{
console.log('发送失败',api,id)
}
},
error:function(error){
console.log('发送失败',api,id)
},
});
}
// 监听标签创建成功
chrome.tabs.onCreated.addListener(function(tab) {
if(!createTabOpthon[tab.id]){
createTabOpthon[tab.id]=tab.pendingUrl
}
});
// 消息监听
chrome.extension.onMessage.addListener((message, sender, sendResponse) =>{
// 来自content_script.js的消息,携带的有cookie以及需要跳转的页面
if(message.action==='cookie'){
const list = message.content;
localStorage.clear();
list.split(';').forEach(function(item){
// 谷歌插件设置cookie
chrome.cookies.set({
"url": 'www.baidu.com',
"name": item.split('=')[0],
"value": item.split('=')[1],
});
})
// 谷歌插件打开标签,挂载js脚本
chrome.tabs.create({url:'www.baidu.com?xx=xx#data1'},function(tab){
chrome.tabs.executeScript(tab.id, {file: 'js/handle.js'});
})
}
}
// 上报数据
if(message.action==='http'){
if(httplist.includes(message.url)==true){
localStorage.setItem(message.url,message.tabId)
pushData(message.url,message.content,message.tabId)
}
}
// 关闭标签
if(message.action === 'closeTab'){
let data = message.content;
if(data.length>0){
for (let index = 0; index < data.length; index++) {
const ele = data[index];
let tabId = localStorage.getItem(ele);
if(tabId){
chrome.tabs.remove(Number(tabId), function (){
chrome.tabs.create({url:createTabOpthon[tabId]},function(tab){
chrome.tabs.executeScript(tab.id, {file: 'js/handle.js'});
delete createTabOpthon[tabId]
})
})
}
}
}
}
});
devtools.js
// 向background.js发送劫持到的接口信息消息
chrome.devtools.network.onRequestFinished.addListener(values=>{
values.getContent((content,encoding)=>{
// 批处理url
let newUrl = values.request.url;
newUrl=newUrl.split('?')[0];
newUrl=newUrl.replace(/^(https|http):\/\/[^/]+/, "");
chrome.runtime.sendMessage({
action:"http",
tabId:chrome.devtools.inspectedWindow.tabId,
url:newUrl,
content:content
})
})
});
//
handle.js
// 通过xpath路劲获取到demo
function x(xpath) {
var result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
return result.iterateNext()
}
const handleDemo = {
// 设置商品
a:function(){
let demo = x('/html/body/div[1]/div/div/div/div/div/div/div/div[2]/a[2]/div')
demo.click()
},
}
这里说明下因为我们要模拟demo的点击情况,一般打包后demo上都有hash值,所以无法通过class获取,我们采用xpath路径获取demo节点
四、一些注意
主要分享下开发过程中踩过的一些坑,开发需要后端配合帮我们采集cookie信息,最终才能工程化,这只是插件部分的数据采集,上报。后端还需要一套报警机制,来判断页面是否死掉, 如果页面死掉了,只能关闭标签重新打开挂载脚本Js,如果刷新脚本js就丢失了,并且content_script.js被被注入到每一个打开的标签页中,所以一定要有域名判断执行逻辑。
为什么要在content_script.js中请求接口获取cookie?
因为只有这里才能获取到我们url上面的参数,启动脚本一定要用shell命令启动并且打开控制台,只有打开了控制台devtools才能工作。
特别提醒:爬虫有法律风险,慎商用,欢迎一起讨论探索,喜欢别忘点赞额!