首页 > 解决方案 > 如何从 Fast.com 获取互联网速度结果

问题描述

我想定期检查我的互联网速度并在路由器下降到某个阈值时重置我的路由器,这似乎修复了我的 ISP“提供”的糟糕连接。

虽然可能有更简单的方法可以解决这个问题,但我认为我应该从 Fast.com 获取结果,它可以为我提供我需要的结果,例如下载速度、上传速度和到靠近我的服务器的 ping 时间。

寻找任何指针。

标签: pythonseleniumgoogle-chromeweb-scrapingbeautifulsoup

解决方案


编辑 2020-11-03:我已经修复了代码,并且感谢Carlos再次工作。


该页面是 JS 驱动的,没有简单的方法可以通过简单地从页面上刮掉 HTML 来获得结果,因此requests仅靠bs4它不会帮助您。
你需要运行一个完整的现代浏览器来让测试运行,并等待它需要的时间,然后得到结果。

设置

好的,我们为此使用Selenium ,它可以控制 Chrome、Firefox、Safari,基本上是任何浏览器。

通过运行安装包:

pip install selenium

我们还需要安装一个驱动程序来控制我们的浏览器,你可以在这里找到它(事实上完全阅读了关于安装的页面,它拥有运行它所需的一切)。然后我们需要将该可执行文件放在我们的PATH. 最简单的方法是把它c:\Windows放在 Windows 或/usr/binLinux 上。但是在 SO 和互联网上有大量的文档,所以学习正确的方法来正确地做到这一点,你会需要它。

除此之外,我们还需要Beautiful Soup,它是事实上的 HTML 解析器(注意,我们可以继续使用 selenium 和浏览器,但我习惯于使用 bs4 来完成这项工作)。使用安装它

pip install bs4

运行测试

现在要获得结果,我们需要运行浏览器,访问https://fast.com,让测试完成,然后获取结果的 HTML,然后提取我们需要的信息。

我们如何知道测试何时结束?

好吧,我们可以等待,比如 30 秒,直到一切都完成。但是如果提前结束呢?还是根本没有完成?那么我们就会无缘无故地等待。有更好的方法。

测试完成后,微调器变为绿色。如果我们从开发者控制台观察 DOM,我们会看到它有一个succeeded类。

下载结果在

但是如果我们展开结果,我们会看到上传结果还没有出现,当这种情况发生时,页面会再次更新,我们会到达这个状态:

上传结果在

等待

Selenium 有一个显式的等待功能,可以让您等到页面上发生某些事情。我们将使用它来检查并等待.succeeded页面上出现一些具有类的元素。如果您只需要下载速度,只需等待 spinner.succeeded上课,如果您还需要上传结果,则需要等待。对于这项工作,我们可以使用这个辅助函数:

from selenium.common.exceptions import TimeoutException
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


def wait_until_present(driver: Chrome, selector: str, timeout: int = 5):
    condition = EC.presence_of_element_located((By.CSS_SELECTOR, selector))
    try:
        WebDriverWait(driver, timeout).until(condition)
    except TimeoutException as e:
        raise LookupError(f'{selector} is not present after {timeout}s') from e

提取结果

输入结果后,我们将获得包含上传和下载结果的父元素的 HTML。

# this is the parent element that contains both download and upload results
results_selector = '.speed-controls-container'
results_el = driver.find_element_by_css_selector(results_selector)
results_html = results_el.get_attribute('outerHTML')

然后我们将 HTML 提供给 BeautifulSoup 并提取值。

代码

这是完整的代码:

from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
from contextlib import contextmanager


@contextmanager
def get_chrome() -> Chrome:
    # https://docs.python.org/3.7/library/contextlib.html#contextlib.contextmanager
    opts = ChromeOptions()
    # opts.headless = True
    driver = Chrome(options=opts)
    yield driver
    driver.close()


def wait_until_present(driver: Chrome, selector: str, timeout: int = 5):
    condition = EC.presence_of_element_located((By.CSS_SELECTOR, selector))
    try:
        WebDriverWait(driver, timeout).until(condition)
    except TimeoutException as e:
        raise LookupError(f'{selector} is not present after {timeout}s') from e


def extract_speed_info(soup: BeautifulSoup) -> dict:
    dl_speed = soup.select_one('#speed-value').text
    dl_unit = soup.select_one('#speed-units').text
    upload_speed = soup.select_one('#upload-value').text
    upload_unit = soup.select_one('#upload-units').text

    return {
        'upload': f'{upload_speed} {upload_unit}',
        'download': f'{dl_speed} {dl_unit}'
    }


def run_speed_test() -> dict:
    with get_chrome() as driver:
        driver.get('https://fast.com')

        # wait at most 60s until upload results come in
        download_done_selector = '#speed-value.succeeded'
        upload_done_selector = '#upload-value.succeeded'
        wait_until_present(driver, upload_done_selector, timeout=60)

        # this is the parent element that contains both download and upload results
        results_selector = '.speed-container'
        results_el = driver.find_element_by_css_selector(results_selector)
        results_html = results_el.get_attribute('outerHTML')

    # we're finished with chrome, let it close (by exiting with block)

    soup = BeautifulSoup(results_html, 'html.parser')
    info = extract_speed_info(soup)
    return info


if __name__ == '__main__':
    try:
        results = run_speed_test()
        print('Speed results:', results)
    except LookupError as e:
        print('Cannot get speed results')
        print(e)
        exit(1)

输出:

{'upload': '320 Kbps', 'download': '3.4 Mbps'}

推荐阅读