吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4980|回复: 40
收起左侧

[Python 转载] Python基于Selenium控制Chrome捕获Ajax请求(省去解析网页的繁琐)

   关闭 [复制链接]
YuanFang0w0 发表于 2022-5-15 22:46

主题

Python基于Selenium控制Chrome捕获Ajax请求(不使用代{过}{滤}理服务器,不解析网页元素,直接捕获)
你还觉得Selenium爬取效率低下吗?

一、前言

1.需求以及问题

有需求需要爬取目标网站(国外网站)的用户信息,但是这个网站反爬机制还是比较强的,直接去用requests请求ajax接口直接会失败,请求到的页面将是一个用于人机校验的谷歌验证。

因此通过我不断地试验(伪装请求头,除了必要的cookies等参数,我其他的一个个试验),最终还是没有突破它的限制,我又去GitHub上找了找相关的项目,同样的问题并没有得到解决。最终,我将目光投向了Selenium。

2.Selenium分析

使用Selenium的时候,依然不能使用无头模式(即使加上了规避检测的内容依然如此),但是对于我这个项目需求来说,无头与否影响不大,因此我只做了浏览器的最小化即可。

使用Selenium时,我们面临的最大问题就是需要解析大量的网页元素,对于数据请求效率太低。特别是对于ajax请求的接口数据而言,本质上只需要拿到ajax返回的json数据直接解析json就可以,但是Selenium却要去大量解析网页内容,等待网页渲染,实在是一键很麻烦的事情。

3.查找解决办法

因此我决定上网查找资料,查找Selenium可以直接获取ajax请求结果的办法。经过搜索查找资料,网上的办法大多都是用代{过}{滤}理服务去截获请求,这个方法就需要写一个服务端的内容,无疑加大了工作量。我并不是很想用这种方法。

4.自己分析解决

苦苦思考下,突然想到了一个方法,调用Selenium在控制台执行js脚本的方法不知道行不行得通呢,于是我打算去试一下。

二、浏览器实操测试

那么Selenium执行Js脚本,肯定得编写出需要的脚本,这里我们不用编写的过于复杂,仅仅执行请求指定的ajax接口即可。

这里我随便找一个有ajax请求的网站。(示例网站:https://book.qidian.com/info/1033601341/

1.打开网站,打开浏览器控制台,切换到网络选项卡,点击Fetch/XHR,指定捕获ajax请求,刷新网页,可以看到已经有了一些ajax请求被捕获到了。
img1

2.我们随便选一个请求,鼠标右击,复制,复制为Fetch即可。
img2
复制的结果如下:

fetch("https://book.qidian.com/ajax/book/getFansHall?_csrfToken=YZNRZe0oR9VKe4QBccx6qIbURkXtcOCvg27LQNni&bookId=1033601341", {
  "headers": {
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Microsoft Edge\";v=\"101\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-requested-with": "XMLHttpRequest"
  },
  "referrer": "https://book.qidian.com/info/1033601341/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "include"
});

3.来到控制台,粘贴我们复制的Fetch,然后回车执行。
img3

4.切换到网络选项卡,可以看到已经有一个新的请求被发起过了。
img4

5.不难发现,控制台执行完Fetch后并没有得到我们想要的json数据,而是一个对象。这个时候就要重新改写一下第二步中复制的Fetch。让他的结果成为我们需要的Json数据,方便我们进行解析。

对复制的Fetch进行改写如下:

fetch("https://book.qidian.com/ajax/book/getFansHall?_csrfToken=YZNRZe0oR9VKe4QBccx6qIbURkXtcOCvg27LQNni&bookId=1033601341", {
  "headers": {
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Microsoft Edge\";v=\"101\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-requested-with": "XMLHttpRequest"
  },
  "referrer": "https://book.qidian.com/info/1033601341/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "include"
}).then((res)=>{return res.json()}).then((res)=>{console.log(res)});

这里去除了最后的分号,加上了一句代码.then((res)=>{return res.json()}).then((res)=>{console.log(res)});

改过之后再去浏览器中测试一下,可以看的到已经能正常获取到json数据了。
img5

至此,在浏览器中的测试就完全通过了,下面我们要用代码进行实现上面的操作。

三、代码实现操作

1.使用Python+Selenium加载一个浏览器对象,打开指定的网页(这里依然使上面用测试时候的网址进行演示)。
2.对【二】中第5步中的代码再进行一次改写,目的是使代码可以通过Selenium中的execute_script()方法执行并且可以获取到返回的结果,改写如下:

var resp = fetch("https://book.qidian.com/ajax/book/getFansHall?_csrfToken=YZNRZe0oR9VKe4QBccx6qIbURkXtcOCvg27LQNni&bookId=1033601341", {
  "headers": {
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Microsoft Edge\";v=\"101\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-requested-with": "XMLHttpRequest"
  },
  "referrer": "https://book.qidian.com/info/1033601341/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "include"
}).then((res)=>{return res.json()});
return resp

这里一共做了三处改动:

1.在fetch前加上了var resp =,将fetch返回的值保存为一个变量;

2.去掉了代码最后的.then((res)=>{console.log(res)}),原因是这一行是让结果在控制台中以json输出,其实获取json数据只需要.then((res)=>{return res.json()})这一行代码就足以了。

3.最后加了一行return resp,返回这个变量的值(这个值就是需要json数据)。

需要注意的是:这个网站的这个ajax请求后面会带上一个csrfToken,因此在Python中运行时要对其进行改变,不然请求肯定是得不到需要的正确数据的。

改写完之后就是把代码放在Pyhon中执行了,下面是一点示例代码(仅演示执行ajax,不说明获取token):

    print('1.加载浏览器')
    browser = selenium(url='https://book.qidian.com/info/1033601341/',
                       driver_path='chromedriver.exe')
    print('2.执行JS代码')
    token = input('请输入token')
    fetch = r'''var resp = fetch("https://book.qidian.com/ajax/book/getFansHall?_csrfToken=''' + token + r'''&bookId=1033601341", {
  "headers": {
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Microsoft Edge\";v=\"101\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-requested-with": "XMLHttpRequest"
  },
  "referrer": "https://book.qidian.com/info/1033601341/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "include"
}).then((res)=>{return res.json()});
return resp'''
    resp = browser.execute_script(fetch)
    print(resp)
    input('回车退出')
    browser.quit()

值得注意的一点是:复制的fetch放在Python中有很多”\“,这些”\“不能被删除,要注意在python的字符串前加r,让字符串变为原生字符串。

下面是执行的结果展示:

1.Python执行结果:
img6

2.浏览器控制台Network选项卡中的情况:
img7

这里Python执行JavaScript代码在控制台是看不到记录的,直接从Network看即可。

四、声明与总结

1.首先,我在互联网找资料并没有找到此类方法,但是不排除有人已经用过,我只是想分享出来我自己思考的这个思路,如欲转载,请声明出处!

2.这个方法面对一些request请求不到的情况还是挺好用的,也不用考虑cookies的问题,且直接在浏览器中发起请求,单论ajax请求的效率还是与直接requests相差不多的。效率上也并没有特别低下,免去了直接解析网页的繁琐。

3.纯属个人见解,如有错误或者任何问题请指正!

免费评分

参与人数 4吾爱币 +9 热心值 +4 收起 理由
Wyiyun777 + 1 我很赞同!
JUNGLE_CN + 1 + 1 我很赞同!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
brightwill + 1 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

  • · 好帖|主题: 549, 订阅: 87

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

话痨司机啊 发表于 2022-5-17 12:41
YuanFang0w0 发表于 2022-5-17 11:03
不是,我是因为他这个ajax用request请求直接就反爬了。不是参数拿不到。是所有参数齐全的情况下,我用req ...

你可以了解一下 ws技术

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
YuanFang0w0 + 1 + 1 用心讨论,共获提升!

查看全部评分

话痨司机啊 发表于 2022-5-17 09:41
本帖最后由 话痨司机啊 于 2022-5-17 09:56 编辑

问一下,写个JS ajax用execjs直接调用请求应该跟这个一样吧~,逆向JS直接把csftoken也搞下来,就可以本地运行了,不需要再Copy token去了吧~感觉你绕了个弯子,不知道我说的对不对,不过也是种办法,在扣不出来js的时候


[Python] 纯文本查看 复制代码
LBF.define("qd/js/component/common.4ba51.js", function(require, exports, module) {
    var e = require("ui.Nodes.Node")
      , d = (require("qd/js/component/browserSupport.1ad6c.js"),
    require("qd/js/component/login.29081.js"),
    require("qidian.report"))
      , t = require("util.Cookie")
      , r = require("qd/js/component/third.a7feb.js");
    module.exports = e.inherit({
        el: "body",
        events: {},
        elements: {},
        render: function() {
            return this.setElement(this.el),
            this.init(),
            this
        },
        init: function() {
            var e = r.getThirdData()
              , t = e.ua
              , a = (e.isThird && (r.hideAdvertising(),
            r.isIframe() && r.hideLoginBox(),
            document.getElementById("reg-btn") && (document.getElementById("reg-btn").href = "//passport.qidian.com/reg.html?appid=10&areaid=" + e.areaid + "&target=iframe&ticket=1&auto=1&autotime=30&returnUrl=https%3A%2F%2Fwww.qidian.com"),
            document.getElementById("fixed-reg") && (document.getElementById("fixed-reg").href = "//passport.qidian.com/reg.html?appid=10&areaid=" + e.areaid + "&target=iframe&ticket=1&auto=1&autotime=30&returnUrl=https%3A%2F%2Fwww.qidian.com")),
            "https://pay.yuewen.com/pc/index?appId=10&areaId=" + e.areaid + "&&returnUrl=" + location.href)
              , e = ($("body").on("click", ".qqlivepay", function(e) {
                "QQLive" == t && (external && external.OpenQQLive && external.OpenQQLive(a),
                e.preventDefault())
            }),
            g_data.bookInfo && g_data.bookInfo.bookId || g_data.pageJson && g_data.pageJson.bookId)
              , n = g_data.chapter && g_data.chapter.id
              , i = g_data.chapter && g_data.chapter.vipStatus
              , o = g_data.readSetting && g_data.readSetting.rt;
            "undefined" == typeof g_data.isWebSiteType || 1 === g_data.isWebSiteType ? d.init({
                isQD: !0,
                cname: "QDpclog",
                bid: e,
                cid: n,
                ua: t,
                bookstatus: i,
                rt: o
            }) : 0 === g_data.isWebSiteType && d.init({
                isQD: !0,
                cname: "QDmmlog",
                bid: e,
                cid: n,
                ua: t,
                bookstatus: i,
                rt: o
            }),
            this.checkLang(),
            this.pageLoaded(),
            $.ajax({
                url: "/ajax/Help/getCode"
            }).done(function(e) {
                0 === e.code && $(".footer .advice").prop("href", "http://yw.95ib.com/online/?cid=0&uid=10&code=" + e.data)
            })
        },
        pageLoaded: function() {
            $("html").addClass("loaded")
        },
        createSender: function(e) {
            var t = new Image;
            t.onload = t.onerror = function() {
                t = null
            }
            ,
            t.src = e
        },
        checkLang: function() {
            "zht" == t.get("lang") && (require["async"]("qd/css/tradition_font.d9168.css"),
            require["async"]("qd/js/component/chinese.d5874.js", function(e) {
                $("#switchEl").html("简体版"),
                $(".lang").css("fontFamily", "T_FZZCYSK"),
                e.trans2Tradition("html")
            }))
        }
    })
});
狄人3 发表于 2022-5-15 22:59
Chromedriver嘛?这个其实还有一些更好的方法,但效率还是说不上很高吧
Soc 发表于 2022-5-15 23:18
学习一下
 楼主| YuanFang0w0 发表于 2022-5-15 23:18
狄人3 发表于 2022-5-15 22:59
Chromedriver嘛?这个其实还有一些更好的方法,但效率还是说不上很高吧

我觉得只是ajax的话感觉跟requests差的不多
吾爱modi 发表于 2022-5-15 23:25
谢楼主分享,刚好用上。
Venda 发表于 2022-5-15 23:40
乍一看好像还行,仔细一看貌似没啥用
atmo 发表于 2022-5-15 23:48
不错不错,学习一下!
头像被屏蔽
FreeOvO 发表于 2022-5-16 00:09
提示: 作者被禁止或删除 内容自动屏蔽
 楼主| YuanFang0w0 发表于 2022-5-16 00:27
Venda 发表于 2022-5-15 23:40
乍一看好像还行,仔细一看貌似没啥用

至少对我来说我这个项目是用上了。
AdmiralHipper 发表于 2022-5-16 01:25
感谢,多了一种思路
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 23:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表