本文更新中,敬请期待
本文阅读条件:
基本的Python, javascript知识,希望你了解一点点爬虫知识,大神可无视。
写在最前:
现在使用爬虫技术有较大法律风险,务必注意使用场景,本文仅供学习使用。
小弟我目前并不是程序员,也没有正式做过程序员,有错误之处欢迎指正,感谢~~
未经允许,请勿转载!
开篇:
控制浏览器影响浏览器的运行的常见方法:
1.自动化测试技术,利用浏览器提供的控制接口,常见方案如selenium,phoantomjs
2.从系统进程层面注入代码到浏览器js执行引擎,没有研究过
3.中途拦截并篡改js代码,常见方案如 mitmproxy
4.浏览器插件(本文使用的是Chrome Extension, 基本适用于chrome内核的浏览器,如360浏览器)
本文采用的是方案4,可代替方案1与方案4,又或者,相互结合。
优点:
*
简单,只需懂简单的js就可以啦
*
情怀,可以使用Jquery,想想在selenium的driver.get_element_by_id,还有美丽汤,鸡汤(jsoup),人生苦短,吾生有涯而知无涯,能不学就不学
*
反爬,现在淘宝等一些网站可以检测selenium,运行了selenium的浏览器会有一些特殊的变量产生,类似的“指纹”还是比较多的,不好对抗,同时没有很好的办法可以避开。具体检测手段可谷歌“selenium detect”学习。
*
暴力:直接访问网页的变量,cookie等;任意执行当前页面的Js函数,对破解很有用;可以说是直接强奸目标网页了。
*
易于调试,直接使用chrome dev tool
*
轻松面对动态生成的页面
缺点:
*
速度,但也比selenium好(只是在有窗口的情况下),因为用senlenium要多开个senlenium进程,耗费更多的内存和CPU。所以说此法只能适用小中规模的爬虫。
*
既是优化也是缺点,使用Rest Api的形式与后台进行交互,处理数据,所以就需要多一个“后台”,我们可以把插件看成client,后台看成server(本文实用python的flask框架)。
关于Chrome extension的学习,难度还是比较低的,只需一些基本的前端知识,如果你以前没接触过爬虫,也可以试试,因为浏览器插件非常好玩
谷歌官方文档:https://developer.chrome.com/extensions/devguide
Chrome插件开发全攻略:https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html
也可以阅读我博客,我尝试总结一些技巧,希望对你有用: https://blog.csdn.net/chifu5720/article/details/101622682
另外,油管上也有不少教程,是中文世界未曾存在过的。
如果你未玩过chrome插件,可在你的浏览器下输入以下地址访问你已安装的插件
chrome://extensions/
这是我们的插件
下面我们来撸一个搜狗公众号文章采集的插件,话说我们有时能看到一些文章,也可以这样收藏一下。
setTimeout(() => {
var URL = document.URL;
function blobToDataURL(blob, callback) {
let a = new FileReader();
a.onload = function (e) {
callback(e.target.result);
}
a.readAsDataURL(blob);
}
// *********************************************************************************************************************
// 搜狗搜索页面 作用是当前所有文章页并自动打开下一页
var sougou_search_reg = new RegExp('https://weixin\.sogou\.com/weixin');
var enter_link = new RegExp("weixin\.sogou\.com/api/share");
var sg_links = [];
if (sougou_search_reg.test(document.URL)) {
$('a').each(function (index, element) {
let link = $(this)[0].dataset.share;
// console.debug("INJECTEE>>" + link);
if (link && enter_link.test(link)) {
sg_links.push(link);
}
});
// 去重
sg_links = Array.from(new Set(sg_links));
let time = 1;
// 遍历打开所有文章,进入到文章页面后采集代码自动执行
for (let sg_link of sg_links) {
(function (time) {
setTimeout(() => {
console.log(sg_link);
window.open(sg_link);
}, time * 1 * 1001);
})(time++);
}
// 自动进入下一页,下一页又会自动进入下一页,直到最后一页
if ($("#sogou_next").length != 0){
setTimeout(() => {
window.open("https://weixin.sogou.com/weixin" + $("#sogou_next").attr("href"));
}, 60 * 1000);
}
}
// **************************************************************************************************************************
// 搜狗微信文章页面注入
// let sougou_find_group_reg = new RegExp("weixin\.sogou\.com/weixin\\?type=");
let sougou_find_group_reg = new RegExp("mp\.weixin\.qq\.com/s\\?src=");
if (sougou_find_group_reg.test(URL)) {
// alert("代码注入成功!");
$(() => {
var fetches = [];
$("img").each(function (i, e) {
var img = $(this);
let src = img.attr("data-before-oversubscription-url");
if (!src){
src = img.attr("data-src");
}
if (src && (src.indexOf("mmbiz.qpic.cn/mmbiz") != -1 || src.indexOf("mmbiz.qlogo.cn/mmbiz"))) {
fetches.push(
fetch(src).then(function (res) {
return res.blob();
}).then(function (res) {
blobToDataURL(res, function (result) {
img.attr("src", result);
// 可以类似这样移除一些没用的内容,压缩整个html
img.removeAttr("data-src");
// 实测没有下面这句的话 在手机下浏览html会变形
img.attr("style", "width: 100% !important; height: auto !important; visibility: visible !important;")
});
})
.catch(status, err => {
return console.log(status, err);
})
);
} else {
// 正常来说,公众号所有图片符合以上的匹配
console.error("INJECTEE:>>未成功处理图片>>:" + src);
}
});
// 确保所有图片加载完毕
Promise.all(fetches).then(function () {
setTimeout(() => {
data = {
"url":URL,
"title": $(".rich_media_title").eq(0).text().replace(/\s+/g,""),
"source": $("#js_name").text().replace(/(^\s*)|(\s*$)/g, ""), // 采集的公众号名字
"content": $('html').html()
};
// 额外修剪
$("#js_view_source").remove();
console.log("INJECTEE:>>文章处理完毕,开始发送文章数据!");
$.ajax({
url: "https://172.16.5.71:4499/record",
type: "post",
data: data,
});
}, 2 * 1000);
})
// 采集完毕了 10秒后自动强制关闭页面
function AUTOCLOSE() {
window.opener = null;
window.open('', '_self');
window.close();
}
setTimeout(() => {
AUTOCLOSE();
}, 10 * 1000);
});
}
}, 3 * 1000);
from flask import Flask,request
import codecs,os
app = Flask(__name__)
@app.route("/record",methods=["POST"])
def _record():
url = request.values.get("url")
title = request.values.get("title")
source = request.values.get("source")
content = request.values.get("content")
write_path = os.path.join(os.getcwd() ,"articles/{}_{}.html".format(source,title))
print("当前文章:",title)
# 以 文章标题.html 为目标写出文件名
with codecs.open(write_path, mode="w+",encoding="utf-8",errors="ignore") as f:
# 写出网页内容
f.writelines(content)
f.flush()
return "200"
if __name__ == '__main__':
app.run(host='0.0.0.0',port="4499",debug=True,threaded=True,ssl_context="adhoc")
案例地址:
https://github.com/ChenAnZong/ChromeExtensionCodeInjectSpider