Requests-html 我可以获取所有请求的状态代码吗(或 selenium 替代品)

Requests-html can I get status codes of all requests (or selenium alternative)

我有以下代码:

from requests_html import HTMLSession

ses = HTMLSession()
r = ses.get(MYURL)  # start a headless chrome browser and load MYURL
r.render(keep_page=True)  # This will now 'render' the html page
                          # which means like in a real browser trigger
                          # all requests for dependent links. 
                          # like .css, .js, .jpg, .gif

调用 render() 会触发对 Javascript、位图等的大量请求。 有什么办法可以跟踪每个请求的状态代码。 我最感兴趣的是 404,但 4035xx 错误也可能很有趣。

例如,一个用例是:

• 转到一页或一系列页面

• 然后报告有多少请求失败以及访问了哪些 url。

如果 requests-html 无法做到这一点,但使用 selenium 相当简单,我可以切换到 selenium

附录:丑陋的解决方法 1:

我可以设置日志记录以登录到文件并将日志级别设置为调试。 然后我可以尝试解析 websockets.protocol: 的日志,其中包含类似的字符串 {\"url\":\"https://my.server/example.gif\",\"status\":404,\"statusText\":\"...

问题:

将日志级别 DEBUG 激活到一个文件中似乎激活了其他东西,因为突然加载的调试信息也被记录到 stdout

例如:

[I:pyppeteer.launcher] Browser listening on: ws://127.0.0.1:45945/devtools/browser/bc5ce097-e67d-455e-8a59-9a4c213263c1
[D:pyppeteer.connection.Connection] SEND: {"id": 1, "method": "Target.setDiscoverTargets", "params": {"discover": true}}

此外,实时解析它并将其与我在代码中使用的 url 相关联也不是很有趣。

附录:丑陋的解决方案 2:

更糟糕的是关联,但更好的解析和识别 404s 并且只要控制 http 服务器就可以工作。

用 nginx 解析日志 http 服务器的日志我什至可以用我感兴趣的数据设置一个 csv 格式的自定义记录器。

附录:丑陋的解决方案 3:

使用 python 日志记录(pyppeteer 的专用处理程序和过滤器)我可以拦截描述 pyppeteer.connection.CDPSession 记录器响应的 json 字符串,而无需 stderr 被污染。

过滤器允许我实时检索数据。

这仍然很老套。所以寻找更好的解决方案。

尝试以下内容,看看它是否是您想要的。它严格来说是一个 pyppeteer 版本(而不是 requests_html)并且依赖于未公开的私有变量,因此很容易受到版本更新的破坏。

import asyncio
from pyppeteer import launch
from pyppeteer.network_manager import NetworkManager

def logit(event):
    req = event._request
    print("{0} - {1}".format(req.url, event._status))

async def main():
    browser = await launch({"headless": False})
    page = await browser.newPage()
    page._networkManager.on(NetworkManager.Events.Response, logit)
    await page.goto('https://www.google.com')
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

检查 requests_html 浏览器页面对象的来源似乎被埋得很深——所以获取 NetworkManager 并不是那么简单。如果您真的希望它在 requests_html 内工作,那么 monkeypatch 可能是最简单的方法。这是一个例子:

import asyncio
from requests_html import HTMLSession, TimeoutError, HTML
from pyppeteer.network_manager import NetworkManager
from typing import Optional, Union

def logit(event):
    req = event._request
    print("{0} - {1}".format(req.url, event._status))

async def _async_render(self, *, url: str, script: str = None, scrolldown, sleep: int, wait: float, reload, content: Optional[str], timeout: Union[float, int], keep_page: bool, cookies: list = [{}]):
    """ Handle page creation and js rendering. Internal use for render/arender methods. """
    try:
        page = await self.browser.newPage()
        page._networkManager.on(NetworkManager.Events.Response, logit)

        # Wait before rendering the page, to prevent timeouts.
        await asyncio.sleep(wait)

        if cookies:
            for cookie in cookies:
                if cookie:
                    await page.setCookie(cookie)

        # Load the given page (GET request, obviously.)
        if reload:
            await page.goto(url, options={'timeout': int(timeout * 1000)})
        else:
            await page.goto(f'data:text/html,{self.html}', options={'timeout': int(timeout * 1000)})

        result = None
        if script:
            result = await page.evaluate(script)

        if scrolldown:
            for _ in range(scrolldown):
                await page._keyboard.down('PageDown')
                await asyncio.sleep(sleep)
        else:
            await asyncio.sleep(sleep)

        if scrolldown:
            await page._keyboard.up('PageDown')

        # Return the content of the page, JavaScript evaluated.
        content = await page.content()
        if not keep_page:
            await page.close()
            page = None
        return content, result, page
    except TimeoutError:
        await page.close()
        page = None
        return None


ses = HTMLSession()
r = ses.get('https://www.google.com')  # start a headless chrome browser and load MYURL
html =  r.html
html._async_render = _async_render.__get__(html, HTML)
html.render()