Skip to content

Instantly share code, notes, and snippets.

@kphanipavan
Last active March 30, 2023 09:33
Show Gist options
  • Save kphanipavan/6b2d52b0e8c115f6834817a32049ba24 to your computer and use it in GitHub Desktop.
Save kphanipavan/6b2d52b0e8c115f6834817a32049ba24 to your computer and use it in GitHub Desktop.
Script to scrape Nvidia's website to get the latest Nvidia driver available for a given GPU.
"""
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
"""
from typing import List
import subprocess as sp
import re
import requests as req
from bs4 import BeautifulSoup as bs
from random_user_agent.user_agent import UserAgent
from random_user_agent.params import SoftwareName, OperatingSystem
"""
This program requires the following python packages
typing : Builtin: Used for type annotations for some functions.
subprocess: Builtin: Required to run bash commands for listing devices.
re : Builtin: Required for slicing text at various locations.
requests : Addon : Used for downloading web pages.
bs4 : Addon : Package used for traversing the downloaded webpage.
random_user_agent: Addon: Randomising user agent for requests pakage.
"""
lspciRegex = r'(.*?)\s(VGA|3D).*\[\S{4}:(\S{4})\]'
latestTxt = "https://download.nvidia.com/XFree86/Linux-x86_64/latest.txt"
latestVerRegex = r"([\d.]*) "
supportedGPUList = "https://download.nvidia.com/XFree86/Linux-x86_64/{}/README/supportedchips.html"
class NvidiaGPU:
"""
Custom class made for defining an Nvidia GPU.
Attributes:
pciBus: str: required: PCI bus location where the said GPU is located, ex: "01:00.0"
cont : str: required: States whether the GPU has a VGA or a 3D controller
devID : str: required: GPU device ID used for searching from the downloaded GPU list.
driverVer: str : Driver version assigned after searching the GPU driver list.
searchedName: str : Name as defined by the downloaded GPU list.
Custom Methods:
__str__: Overridden menthod, useful when printing an NvidiaGPU object.
"""
def __init__(self, pciBus: str, controller: str, deviceID: str):
self.pciBus = pciBus
self.cont = controller
self.devID = deviceID.upper()
self.driverVer = ''
self.searchedName = ''
def __str__(self) -> str:
if self.driverVer == '':
return 'Nvidia GPU with {} controller located at {} with device ID {}'\
.format(self.cont, self.pciBus, self.devID)
else:
return 'GPU labeled {} with device ID {} found as a {} controller at {} has a latest driver version {}'\
.format(self.searchedName, self.devID, self.cont, self.pciBus, self.driverVer)
def getGPUList() -> List[NvidiaGPU]:
"""
Function to get a list of NvidiaGPU objects, each containing info about the Nvidia GPUs found on the device this script is run on.
"""
lspciOutput = list(filter(None, sp.run(['lspci -nn | grep -E \'(3[Dd] | [Vv][Gg][Aa])\' | grep -i nvidia'],
shell=True, text=True, capture_output=True, check=True)
.stdout.split('\n')))
devs = []
for dev in lspciOutput:
devData = re.search(lspciRegex, dev).groups()
if len(devData) == 2:
devData.insert(1, 'Unknown')
devs.append(NvidiaGPU(*devData))
return devs
def getRandomUA() -> str:
"""
Get a randomized User Agent as a string.
"""
return UserAgent(software_names=[SoftwareName.CHROME.value, SoftwareName.FIREFOX.value,
SoftwareName.OPERA.value, SoftwareName.BRAVE.value, SoftwareName.EPIPHANY.value, SoftwareName.CHROMIUM.value],
operating_systems=[OperatingSystem.LINUX.value, OperatingSystem.UNIX.value]).get_random_user_agent()
def getDriverVersion(useGPUOnDevice: bool = True, customGPUs: List[NvidiaGPU] = None):
"""
Function to return a list of NvidiaGPU objects containing the device info
as well as the latest driver available for the device.
The objects also contain the Device Name as stated on the Nvidia Database.
"""
devices = []
if useGPUOnDevice:
devices = getGPUList()
if customGPUs:
devices.extend(customGPUs)
ua = getRandomUA()
latestData = req.get(latestTxt, headers={'User-Agent': ua}).text
latestVer = re.search(latestVerRegex, latestData).group(0).strip()
supportedGPUPage = req.get(supportedGPUList.format(
latestVer), headers={'User-Agent': getRandomUA()}).text
bsData = bs(supportedGPUPage, 'html.parser')
searchedDevices = []
tableTag = bsData.find_all('h3')[0]
currentVersion = latestVer
while True:
tableTag = tableTag.findNext('tbody')
for i in tableTag.children:
if i == '\n':
continue
else:
tempdev = []
for j in i.children:
if j == '\n':
pass
else:
tempdev.append(j.text)
for j in devices:
if j.devID == tempdev[1][:4]:
tempj = j
tempj.driverVer = currentVersion
tempj.searchedName = tempdev[0]
searchedDevices.append(tempj)
devices.remove(j)
else:
pass
while True:
try:
if 'driver supports the following set of' in tableTag.findNext('p').text:
tableTag = tableTag.findNext('p')
break
else:
tableTag = tableTag.findNext('p')
except AttributeError as noMorePeesFound:
break
if len(devices) == 0:
break
currentVersion = re.search(
r'The[\n\s](.*)[\n\s]driver', tableTag.text).group(1)
tableTag = tableTag.findNext('table')
return searchedDevices
if __name__ == '__main__':
for device in getDriverVersion():
print(device)
"""
This program follows the API restrictions found at https://download.nvidia.com/robots.txt
This program is originally written by Phani Pavan K <[email protected]>. In case of a fork, please add your name and email below, which can help track the evolution of said forks.
"""
@kphanipavan
Copy link
Author

Update 1, 30th March 2023:

Change in line 56, capitalizes device ID, fails if it isn't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment