场景描述
做过爬虫或者机器人的朋友一定会遇到登陆的问题:大多数的网站需要登陆之后才能读取内容或者执行操作。要实现网站的自动化登陆和操作,大概的方法有两种:1,通过Chrome无头浏览器-selenium等工具模拟登陆,然后通过控制selenium实现和网站的交互操作;2,将包含已登陆信息的cookie设置到HTTP请求当中,直接通过HTTP request进行交互;这两种方法各自适用不同的场景,互相不能替代。但总的来说,当已经摸清服务器HTTP API的时,第二种方法是最直接有效,代码也是最简洁的。
面对的问题
对于第一种方法来说,最麻烦的是处理现在越来越繁复的验证码校验。虽然google和baidu 都有发布过一些包可以识别图片中的验证码,但准确率并不高;并且遇到需要手动拖拽的防机器人校验还得加入复杂的js代码进行动作模拟;
对于第二种方法,关键是如何获取cookie并妥善的处理cookie中登陆token过时的问题。我这篇文章提供一个简单的解决思路,虽然没有完全的自动化,但是只是部分解决了获取cookie和将cookie同步到机器人端的问题
解决思路
首先,我们需要在平时我们访问网站的时候,能够自动的将cookie保存下来,并且发送到云服务器数据库中,方便机器人获取该cookie。
其次,在每次机器人运行对应的工作时,都到云数据库中获取对应网站最新的cookie,再执行自动化操作。
这样做的好处在于:
- 我们不需要写复杂的代码去模拟登陆
- 每次我们访问常用网站的时候都会将最新的cookie更新到数据库,避免机器人使用的cookie过时
比如,我访问csdn,可以从下图看到,chrome已经自动导出了我的cookie,并且同步到了我的aws dynamoDB当中。
要让chrome能实现这样的行为,我们需要自己开发chrome插件,所幸的是,代码量并不大。
使用插件主要基于以下原因:
1、对于大部分的用户登陆信息(user token等)来说,在保存到cookie中时,都带了HTTP_ONLY的flag,这个flag作为一个安全机制,目前基本已经成为一个业内标准。即当cookie中的值被标记为HTTP_ONLY时,在js脚本中无法通过document.cookie
获取。即,如果你希望在js中获取当前页面的cookie时,document.cookie
只能获取部分内容(非HTTP_ONLY)。而chrome plugin因为有更高的操作权限,可以通过chrome.cookies
接口获取所有的cookie 2、因为我们期望的操作是获取用户登陆信息并同步到远端,数据安全尤为重要,以插件的方式进行操作,可以有效的隔离危险,防止被窥探
chrome cookie share插件开发
我这里只提一下重点,贴一下代码,关于具体的教程可以自行在网上搜索。这里给出google chrome的开发者官网。对接口有不清楚的可以到这上面去看一下。
项目结构
GetCookie2Server
├── images
│ ├── icon_48.ico
│ └── icon_48.png
├── js
│ ├── background.js
│ └── content.js
└── manifest.json
主要的只有三个文件:
manifest.json
content.js
background.js
manifest.json
类似node开发里的package.json
,里面定义了chrome插件的信息和结构。
{
"manifest_version": 2,
"name": "Cookie读取保存工具",
"version": "1.0.0",
"description": "将网站的cookie保存到dynamoDB上,方便爬虫等工具直接使用",
"icons": {
"16": "images/icon_48.png",
"48": "images/icon_48.png",
"128": "images/icon_48.png"
},
"browser_action": {
"default_icon": {
"19": "images/icon_48.png",
"38": "images/icon_48.png"
},
"default_title": "Cookie读取保存工具"
},
"background": {
"scripts": [
"js/background.js"
]
},
"permissions": [
"cookies",
"tabs",
"http://*/*",
"https://*/*",
"storage"
],
"content_scripts": [
{
"matches": [
"https://xxx.com/list"
],
"js": [
"js/content.js"
],
"run_at": "document_end",
"exclude_globs": [],
"include_globs": [
"*"
]
}
]
}
还是对应到node的开发:
manifest.json 里的background
, 相当于后端运行的JS
"background": {
"scripts": [
"js/background.js"
]
}
content_scripts
, 相当于前端。
"content_scripts": [
{
"matches": [
"https://xxx.com/list" // 对应的页面才执行该脚本
],
"js": [
"js/content.js"
],
"run_at": "document_end", // 对应的页面加载完之后执行脚本
"exclude_globs": [],
"include_globs": [
"*"
]
}
]
这里需要注意的是,cookies不是默认的chrome组件,必须手动添加
"permissions": [
"cookies", // cookies的权限必不可少
"tabs",
"http://*/*",
"https://*/*",
"storage"
]
前端:content.js
这部分的代码超简单:主要是触发后端去进行cookie的更新,这里需要注意的是,content和background是运行在两个环境当中,它们需要通过onMessage通信机制进行通信
window.addEventListener("load", myMain, false);
function getCookies(url) {
chrome.runtime.sendMessage({url: url}, async function (response) {
console.log(`chrome.runtime.sendMessage`,response);
});
}
function myMain(evt) {
console.log("Cookies helper running!");
getCookies(document.URL)
}
后端:background.js
var serverApiUrl = 'https://xxxx.com/update-cookies';
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
getCookies(request.url)
sendResponse("cookie update are trigger");
});
// 获取指定URL的cookie
function getCookies(url) {
chrome.cookies.getAll({
url: url
}, (cks) => {
let cookie = cks.map((item) => {
return item.name + "=" + item.value
}).join(";") + ";";
// 保存到服务器
saveCookieToServer(cookie);
});
}
// 保存cookie到服务器
function saveCookieToServer(currentCookie) {
var xhr = new XMLHttpRequest();
xhr.open("GET", `${serverApiUrl}?cookie=${currentCookie}`, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON解析器不会执行攻击者设计的脚本.
var resp = JSON.parse(xhr.responseText);
console.log(resp)
}
}
xhr.send();
}
文章评论(0)