独辟蹊径做爬虫,使用Jquery+Chrome Extension,让浏览器成为你的数据收集器 公众号文章自动离线

本文更新中,敬请期待

本文阅读条件:
基本的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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值