selenium、browsermobproxy拦截请求时,字节型/非文本型/二进制型响应报文无法正确获取问题解决(python)

最近用selenium和browsermobproxy弄了个爬虫,专门去某个网站爬取pdf文件。虽然该网站没有提供下载文件的功能,但用户在浏览器上预览pdf内容时,浏览器事实上已经下载了pdf了,所以我试着用browsermobproxy在拦截请求阶段把文件给保存下来。

import browsermobproxy
from selenium import webdriver
from selenium.webdriver import chrome

server = browsermobproxy.Server(r'D:\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat', {'port': 9723})
server.start()
browsermob_proxy = server.create_proxy({'trustAllServers': True})  # 设置trustAllServers为true,否则访问https网站报错

chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument(f'--proxy-server={browsermob_proxy.proxy}')
chrome_options.add_argument('--ignore-certificate-errors')  # 设置--ignore-certificate-errors,否则设置代理后的https网站无法正常访问
driver = webdriver.Chrome(options=chrome_options, service=chrome.service.Service(r'D:\chromedriver.exe'))

以上就是初始化操作,接下来便是操控浏览器,在需要拦截的请求之前,每次都创建一个新的har,如下:

browsermob_proxy.new_har(options={'captureContent': True, 'captureBinaryContent': True})

浏览器加载完pdf后,就可以通过遍历来得到自己想要的东西了,如下:

for entry in browsermob_proxy.har['log']['entries']:
    # 具体的逻辑代码

万万没想到,browsermob_proxy.har里的确有拦截到了pdf的报文,但却是字符串文本的乱码报文,写到文件里也不是一个正常pdf文件,根本打不开。

寻找解决办法的过程很曲折,去browsermob-proxy的github页面看介绍,去看browsermob-proxy的源码,终于有了结果。

先说一下我对于browsermob-proxy的理解:

browsermob-proxy是一个java项目,启动后会作为一个代理服务器,浏览器设置它为代理后,我们就可以通过该项目对外开放的一系列REST API来操作了。事实上python里browsermobproxy模块的很多方法源码里都是requests.get和requests.put。

那么问题来了,请看下边的client.py源码:

    @property
    def har(self):
        """
        Gets the HAR that has been recorded
        """
        r = requests.get('%s/proxy/%s/har' % (self.host, self.port))

        return r.json()

得到的这个har它就是个json字符串,那我想要的pdf文件,也就是非文本型的文件怎么放进去一个json里呢?

browsermob-proxy也有考虑这方面的问题,就是非文本型报文会转为base64字符串,然后才放到json里,这样就没问题了。

那browsermob-proxy怎么判断响应报文是否为非文本型的呢?请看java代码:

    /**
     * Returns true if the content type string indicates textual content. Currently these are any Content-Types that start with one of the
     * following:
     * <pre>
     *     text/
     *     application/x-javascript
     *     application/javascript
     *     application/json
     *     application/xml
     *     application/xhtml+xml
     * </pre>
     *
     * @param contentType contentType string to parse
     * @return true if the content type is textual
     */
    public static boolean hasTextualContent(String contentType) {
        return contentType != null &&
                (contentType.startsWith("text/") ||
                contentType.startsWith("application/x-javascript") ||
                contentType.startsWith("application/javascript")  ||
                contentType.startsWith("application/json")  ||
                contentType.startsWith("application/xml")  ||
                contentType.startsWith("application/xhtml+xml")
                );
    }

居然是靠报文头的Content-Type字段来判断的。众所周知,Content-Type是最不可信的东西,这东西有时候甚至可以想怎么设置就怎么设置,对报文实体毫无影响。恰好,我想要爬取的pdf就设置为application/javascript,判断为文本型,然后按照默认编码变为字符串乱码了。

上面扯了些没用的,以下进入正题,解决办法:

在拦截时修改pdf的Content-Type头

server = browsermobproxy.Server(r'D:\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat', {'port': 9723})
server.start()
browsermob_proxy = server.create_proxy({'trustAllServers': True})  # 设置trustAllServers为true,否则访问https网站报错
browsermob_proxy.response_interceptor('''
    if (contents.isText() && contents.getTextContents().startsWith("%PDF")) {
        response.headers().set("Content-Type", "application/pdf");
    }
''')

-----------------------------分割线------------------------------

最近发现python里seleniumwire也能实现我这里拦截pdf的功能,不用browsermob-proxy。当然啦,browsermob-proxy可不只是用来跟selenium配合使用的,它应该跟fiddler这些代理服务一样,也可以用来监控其它的东西。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值