轻松截获 Selenium 中的 Ajax 数据
这篇文章同样的还是转载崔大的,因为都是一个系列的,所以我就转载出来了,我觉得很实用。原文链接:点我
以下内容为原文。
之前我们介绍了 ajax-hook 来实现爬虫的过程中截获 Ajax 请求,可以看这篇文章如何用 Hook 实时处理和保存 Ajax 数据,在这里再另外介绍一个工具 BrowserMob Proxy,利用它我们同样可以实现 Selenium 爬虫过程中 Ajax 请求的获取。
下面我们来简单介绍一下。
BrowserMob Proxy
BrowserMob Proxy,简称 BMP,它是一个 HTTP 代理服务,利用它我们可以截获 HTTP 请求和响应内容,另外还可以把 Performance data 输出成一个 HAR 文件。
其 GitHub 链接为:https://github.com/lightbody/browsermob-proxy/。
大家可以点击进去看看详情介绍。
实际上其原理就是开了一个代理服务器,然后抓包,同时对接了 Java、Python API,以方便我们可以直接通过代码来获取到内容。
案例
官方的一些介绍比较复杂,而且大多数都是 Java 的对接,在这里我们使用 Python 来实验一下。
这里我们就直接通过一个案例来测试下吧,废话不多说。
还是拿我自己的一个测试网站为案例,链接为:https://dynamic2.scrape.center/。
页面如图所示:
其数据都是通过 Ajax 加载的,同时带着一些加密参数:
这个网站通过 Selenium 爬的话一点问题也没有,但是由于数据本身就是从 Ajax 加载的,所以如果能直接截获 Ajax 请求的话,连页面解析都省了。
所以这里我们要利用 BrowserMob Proxy 来截获一下试试。
代码实现
要用 Python 实现,我们需要先安装一个 BrowserMob Proxy 的包,命令如下:
1 | pip3 install browsermob - proxy |
另外我们还需要下载 browsermob-proxy 的二进制文件,以便于启动 BrowserMob Proxy。
下载的地址见:https://github.com/lightbody/browsermob-proxy/releases
直接下载 build 过的版本即可:
比如这里我就下载 browsermob-proxy-2.1.4.zip 文件,直接放到我的项目目录下。
好,接着呢,我们就可以实现如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | from browsermobproxy import Server import time from selenium import webdriver from selenium.webdriver.chrome.options import Options # 启动代理 server = Server( './browsermob-proxy-2.1.4/bin/browsermob-proxy' ) server.start() proxy = server.create_proxy() print ( 'proxy' , proxy.proxy) # 启动浏览器 chrome_options = Options() chrome_options.add_argument( '--ignore-certificate-errors' ) chrome_options.add_argument( '--proxy-server={0}' . format (proxy.proxy)) driver = webdriver.Chrome(options = chrome_options) # 监听结果 base_url = 'https://dynamic2.scrape.center/' proxy.new_har(options = { 'captureContent' : True , 'captureHeaders' : True }) driver.get(base_url) time.sleep( 3 ) # 读取结果 result = proxy.har for entry in result[ 'log' ][ 'entries' ]: print (entry[ 'request' ][ 'url' ]) print (entry[ 'response' ][ 'content' ]) |
在这里呢,一共分了四步:
•第一步便是启动 BrowserMob Proxy,它会在本地启动一个代理服务,这里注意 Server 的第一个参数需要指定 BrowserMob Proxy 的可执行文件路径,这里我就指定了下载下来的 BrowserMob Proxy 的 bin 目录的 browsermob-proxy 的路径。
•第二步便是启动 Selenium 了,它可以设置 Proxy Server 为 BrowserMob Proxy 的地址。
•第三步便是访问页面同时监听结果,这里我们需要调用 new_har 方法,同时指定捕获 Resopnse Body 和 Headers 信息,紧接着调用 Selenium 的 get 方法访问一个页面,这时候浏览器便会加载这个页面,同时所有的请求和响应信息都会被记录到 HAR 中。
•第四步便是读取 HAR 到内容了,我们调用 log 到 entries 字段,里面便包含了请求和响应的具体结果,这样所有的请求和响应信息我们便能获取到了,Ajax 的内容也不在话下。
运行结果类似如下:
这里可以看到所有的数据都能获取到了,包括 Ajax 结果、JavaScript、CSS 文件内容等等。
这里 har 的内容其实是一个 JSON 对象,里面记录了在访问页面的过程中发生的所有请求和响应内容,一般内容都会记录在 logs 的 entries 字段里面,还有其他的信息如有需要也可以读取。
所以,这样我们就能从 Selenium 中获取 Ajax 请求内容了。
优化
不过像上面这种代码还是不方便啊,不好复用,不好扩展,我们来稍微改写下,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | from selenium import webdriver from selenium.webdriver.chrome.options import Options from browsermobproxy import Server import time import json class BaseFramework( object ): def __init__( self ): self .server = Server( './browsermob-proxy-2.1.4/bin/browsermob-proxy' ) self .server.start() self .proxy = self .server.create_proxy() chrome_options = Options() chrome_options.add_argument( '--ignore-certificate-errors' ) chrome_options.add_argument( '--proxy-server={0}' . format ( self .proxy.proxy)) self .browser = webdriver.Chrome(options = chrome_options) def process_request( self , request, response): pass def process_response( self , response, request): pass def run( self , func, * args): self .proxy.new_har(options = { 'captureContent' : True , 'captureHeaders' : True }) func( * args) result = self .proxy.har for entry in result[ 'log' ][ 'entries' ]: request = entry[ 'request' ] response = entry[ 'response' ] self .process_request(request, response) self .process_response(response, request) def __del__( self ): self .proxy.close() self .browser.close() class MovieFramework(BaseFramework): def process_request( self , request, response): print (request) def process_response( self , response, request): if '/api/movie/' in request[ 'url' ]: print (response[ 'content' ]) text = response[ 'content' ][ 'text' ] results = json.loads(text)[ 'results' ] for item in results: name = item.get( 'name' ) with open (f '{name}.json' , 'w' , encoding = 'utf-8' ) as f: json.dump(item, f, ensure_ascii = False , indent = 2 ) def load( self , url): self .browser.get(url) time.sleep( 3 ) if __name__ = = '__main__' : f = MovieFramework() for page in range ( 1 , 5 ): url = f 'https://dynamic2.scrape.center/page/{page}' f.run(f.load, url) |
这里框架写的很基础,还有很多需要完善的地方,就只借着这雏形说说大体思路:
•这里我先定义了一个 BaseFramework,就是基础框架,然后里面定义了几个关键方法,__init__
方法不多说了,就是把一些初始化的工作放进去。然后定义了 run 方法,把 HAR 的声明、访问、读取的操作封装了一下。然后定义了 process_response 和 process_request 的回调,这里就没实现任何操作,可以在子类实现。•如果我们要写一个爬虫的话,可以新建一个子类来继承刚才定义的 BaseFramework,然后可以自己实现 process_request 和 process_response 来处理请求和响应的结果,比如这里我就实现了一个 MovieFramework,然后实现了 process_response 处理响应信息,里面判断了 Ajax 请求的 URL,然后进行了提取和保存处理。里面 load 方法就是自行定义的,里面正常定义逻辑即可。•最后运行的时候使用 run 方法运行自定义的 load 方法即可,传入 load 方法的参数,即可完成页面的加载。同时加载的过程中 process_response 方法就会被回调,对结果进行处理。这里我们就提取了 Ajax 数据,然后保存下来了。
最终运行下,我们就可以看到一条条的电影数据就被保存下来了,如图所示:
是不是方便多了?有了它我们连页面解析的那一步都直接省略了,直接拿到了原始 Ajax 数据,舒服。
当然上面的框架还有很多很多需要优化的地方,大家可以参考思路自己实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】