Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Ordenar tablas

20/24
Recursos

Aportes 44

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

En la línea 24 hice el siguiente cambio en parametro de la función para xpath

//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]

Cambié j por i. Solo eso.

Lo realize de la siguiente manera:

import unittest
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

class Tables(unittest.TestCase):
    def setUp(self):
        """Start web driver"""
        options = webdriver.ChromeOptions()
        options.add_argument('--no-sandbox')
        options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        self.driver = webdriver.Chrome(options=options)

    def tearDow(self):
        """Stop web driver"""
        self.driver.quit()

    def test_sort_tables(self):
        driver = self.driver
        table_data = []
        try:
            """Find and click link with text Sortable Data Tables"""
            driver.get('https://the-internet.herokuapp.com/')
            driver.find_element_by_link_text('Sortable Data Tables').click()

            get_rows_table = driver.find_element_by_id('table1').get_property('rows')
            get_head_table = get_rows_table[0].get_property('cells')


            for i in range(1, len(get_rows_table)):
                data = {}
                for j in range(len(get_rows_table)):
                    get_head_cells = get_head_table[j].text
                    get_body_cells = get_rows_table[i].get_property('cells')[j].text

                    data.update({get_head_cells:get_body_cells})
                table_data.append(data)

            print(table_data)


        except NoSuchElementException as ex:
            self.fail(ex.msg)

if __name__ == "__main__":
    unittest.main(verbosity = 2)```


Obteniendo el siguiente resultado:

[{'Last Name': 'Smith', 'First Name': 'John', 'Email': '[email protected]', 'Due': '$50.00', 'Web Site': 'http://www.jsmith.com'}, {'Last Name': 'Bach', 'First Name': 'Frank', 'Email': '[email protected]', 'Due': '$51.00', 'Web Site': 'http://www.frank.com'}, {'Last Name': 'Doe', 'First Name': 'Jason', 'Email': '[email protected]', 'Due': '$100.00', 'Web Site': 'http://www.jdoe.com'}, {'Last Name': 'Conway', 'First Name': 'Tim', 'Email': '[email protected]', 'Due': '$50.00', 'Web Site': 'http://www.timconway.com'}]

El objetivo de la clase era aprender a obtener la información de una tabla y se cumplió. Sin embargo, la información almacenada en la colección, quedó mal estructurada a mi parecer.

Este es el resultado del ejercicio del docente:

>>> [['Last Name', 'Smith', 'Frank', '[email protected]', '$50.00'], ['First Name', 'Smith', 'Frank', '[email protected]', '$50.00'], ['Email', 'Smith', 'Frank', '[email protected]', '$50.00'], ['Due', 'Smith', 'Frank', '[email protected]', '$50.00'], ['Web Site', 'Smith', 'Frank', '[email protected]', '$50.00']]

Por cada columna, está guardando la información de toda la fila.

Esta debería ser el resultado correcto, a mi parecer:

>>> [['Last Name', 'Smith', 'Bach', 'Doe', 'Conway'], ['First Name', 'John', 'Frank', 'Jason', 'Tim'], ['Email', '[email protected]', '[email protected]', '[email protected]', '[email protected]'], ['Due', '$50.00', '$51.00', '$100.00', '$50.00']]

NOTA: Modifiqué las url del código anterior, porque no me dejaba publica este comentario con dichas urls.

Ahora, el inicio de cada sublista tiene el nombre de la columna y el resto de posiciones está la información de solo esa columna.

El problema es como obtiene el valor de row_data. Dejo el código corregido para que puedan ver las diferencias:


import unittest
from selenium import webdriver


class Typos(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome(executable_path=r'./chromedriver.exe')
        driver = self.driver
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element_by_link_text('Sortable Data Tables').click()

    def test_sort_tables(self):
        driver = self.driver

        table_data = [[] for i in range(5)]
        print(table_data)

        for i in range(5):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')
            table_data[i].append(header.text)

            for j in range(4):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]')
                table_data[i].append(row_data.text)

        print(table_data)

    def tearDown(self):
        self.driver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)


El resultado del video no es correcto. min 6.10. No se vuelcan correctamente los datos de la lista

Reto resuelto:

    def test_sort_tables(self):
        driver = self.driver
        # Total elementos del header a capturar
        header_data_size = len(driver.find_elements_by_css_selector('#table1 > thead > tr > th')) -1

        # total elementos del body a capturar.
        body_data_size = len(driver.find_elements_by_css_selector('#table1 > tbody > tr'))

        # Se crea una lista de sublistas
        table_data = [[] for i in range(body_data_size)]

        # Se capturan los datos
        for i in range(body_data_size):
            for j in range(header_data_size):
                header_data = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{j+1}]/span').text
                cell_data = driver.find_element_by_xpath(f'//*[@id="table2"]/tbody/tr[{i+1}]/td[{j+1}]').text
                table_data[i].append({header_data : cell_data})

        print(table_data)

Resultado:

[[{'Last Name': 'Smith'}, {'First Name': 'John'}, {'Email': '[email protected]'}, {'Due': '$50.00'}, {'Web Site': 
'https://www.jsmith.com'}], [{'Last Name': 'Bach'}, {'First Name': 'Frank'}, {'Email': '[email protected]'}, {'Due': '$51.00'}, {'Web Site': 'https://www.frank.com'}], [{'Last Name': 'Doe'}, {'First Name': 'Jason'}, {'Email': '[email protected]'}, {'Due': '$100.00'}, {'Web Site': 'https://www.jdoe.com'}], [{'Last Name': 'Conway'}, {'First Name': 'Tim'}, {'Email': '[email protected]'}, {'Due': '$50.00'}, {'Web Site': 'https://www.timconway.com'}]]```

Lo más lindo de Python, además de ser Open Source, son sus librerías. Con esta van a poder visualizar de manera mucho más estética los valores obtenidos de la tabla:
https://pypi.org/project/prettytable/
Además de jugar un poco más con el código 😃 Espero que les guste!

Acá lo hice con diccionario, listo para convertirlo en DataFrame si se desea:

def test_sort_tables(self):
    driver = self.driver

    table_data = {}
    print(table_data)

    for i in range(5):
        header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')
        table_data[header.text] = []

        for j in range(4):
            row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]')
            table_data[header.text].append(row_data.text)

    print(table_data)

Deberían regrabar esta clase, ese ciclo for está otro nivel de mal 😦

¿Qué les parece esta presentación?

Aquí les dejo como lo hice con PrettyTable:

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
from prettytable import PrettyTable


class Tables(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome(r'chromedriver')
        driver = self.driver
	#Aquí pongan el enlace, como no es https no me permite añadir mi comentario.
        driver.get()

    def test_table(self):
        driver = self.driver
        rows = []
        ptable = PrettyTable()
        for i in range(5):
            header = driver.find_element(
                By.XPATH, f'//*[@id="table1"]/thead/tr/th[{i+1}]/span')
            for j in range(4):
                row = driver.find_element(
                    By.XPATH, f'//*[@id="table1"]/tbody/tr[{j+1}]/td[{i+1}]')
                rows.append(row.text)
            ptable.add_column(header.text, rows)
            rows.clear()

        print(ptable)

    def tearDown(self):
        self.driver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)

En este enlace encontrarán la info para usar PrettyTable.

Si se fijan el codigo de la clase retorna la misma fila de la tabla varias veces, dejo mi codigo con las pequeñas correcciones

import unittest
from selenium import webdriver
from time import sleep

ROWS = 5

class Tables(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome('../chromedriver')
        driver = self.driver
        driver.get('pagina de prueba') #no la pongo porque la IA de comentarios de platzi me la borra
        driver.find_element_by_link_text('Sortable Data Tables').click()

    def test_sort_tables(self):
        driver = self.driver

        table_data = [[] for _ in range(ROWS)]
        
        for i in range(ROWS):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')
            table_data[i].append(header.text)

            for j in range(ROWS - 1):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]')
                table_data[i].append(row_data.text)

        print(table_data)

    def tearDown(self):
        self.driver.close()

if __name__ == "__main__":
    unittest.main(verbosity = 2)

RESULTADO:

test_sort_tables (__main__.Tables) ...
DevTools listening on ws://127.0.0.1:50420/devtools/browser/1ba7a561-8b24-40af-9638-e8594d558ec8
[['Last Name', 'Smith', 'Bach', 'Doe', 'Conway'], ['First Name', 'John', 'Frank', 'Jason', 'Tim'], ['Email', 'jsmith@gmail.com', 'fbach@yahoo.com', 'jdoe@hotmail.com', 'tconway@earthlink.net'], ['Due', '$50.00', '$51.00', '$100.00', '$50.00'], ['Web Site', 'https://www.jsmith.com', 'https://www.frank.com', 'https://www.jdoe.com', 'https://www.timconway.com']]

Para no tener que hacer maromas para tener los datos de cada columna con su table_header, use un dictionary con listas y no solo listas con más listas.

#!/usr/bin/python

import unittest
from selenium import webdriver

class Tests (unittest.TestCase):
    def setUp (self):
        self.options = webdriver.ChromeOptions()
        self.options.binary_location = '/usr/bin/brave'
        self.driver = webdriver.Chrome(executable_path='/home/bl4ky113/bin/chromedriver', options=self.options)
        self.driver.maximize_window()

        self.driver.get("https://the-internet.herokuapp.com/tables")

    def test_get_table_info (self):
        categories = len(self.driver.find_elements_by_css_selector('#table1 > thead > tr > th')) - 1
        size_table = len(self.driver.find_elements_by_css_selector('#table1 > tbody > tr'))
        table = {}

        for i in range(categories):
            category = self.driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span').text
            table[category] = []

            for j in range(size_table):
                table[category].append(self.driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]').text)

        print(table)

    def tearDown (self):
        self.driver.quit()

if __name__ == "__main__":
    unittest.main(verbosity=2)

Dejo mi version para que en cada sub-lista aparezca la data de cada usuario, si hay algo que pueda mejorar o corregir son bien aceptadas:

import unittest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

class Tables(unittest.TestCase):
    
    def setUp(self):
        self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
        driver = self.driver
        driver.maximize_window()
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element(By.LINK_TEXT, 'Sortable Data Tables').click()

    def test_sort_tables(self):
        driver = self.driver

        iter_rows = range(1, 5)
        iter_colums = range(1, 6)

        table_data = [[] for i in range(4)]

        for row in iter_rows:
            for colum in iter_colums:
                data = driver.find_element(By.XPATH, f'//*[@id="table1"]/tbody/tr[{row}]/td[{colum}]')
                table_data[row - 1].append(data.text)

        print(table_data)

    def tearDown(self):
        self.driver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)

La parte del scraping está bastante desordenada. Les dejo mi versión. Cree una lista de diccionarios, así la información queda mucho más ordenada y más fácil de visualizar.

  • instale tabulate con “pip install tabulate” luego lo importe con"from tabulate import tabulate", con el objetivo de visualizar mejor la tabla, dividiendo las cabeceras y el contenido.
  • Adicional modifique la linea 26 utilizando la variable “i” en el “td” del xpath
  • y modifique la linea 27 cambiando “i” por “j”
  • por ultimo modifique la linea 29 del print utilizando tabulate

Creo que en cada array dentro de la variable table_data deben ir las columnas como tal. Dejo mi código que ordena según ese criterio.

import unittest

from selenium import webdriver
from selenium.webdriver.common.by import By

class TablesTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.driver = webdriver.Chrome(executable_path=r'./chromedriver.exe')
        cls.driver.get('https://the-internet.herokuapp.com/tables')
        cls.driver.maximize_window()
    
    def test_tables(self):
        table_data = [[] for i in range(5)]
        headers = self.driver.find_elements(By.CLASS_NAME, 'header')
        print(len(headers))
        for i in range(5):
            table_data[i].append(headers[i].find_element(By.TAG_NAME, 'span').text)
        datos = self.driver.find_element(By.TAG_NAME, 'tbody').find_elements(By.TAG_NAME, 'tr')

        for i in range(5):
            for j in range(4):
                table_data[i].append(datos[j].find_elements(By.TAG_NAME,'td')[i].text)

        print(table_data)


    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

if __name__ == '__main__':
    unittest.main(verbosity=2)
import unittest
from selenium import webdriver

class Tables(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome(executable_path = './chromedriver')
        driver = self.driver
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element_by_link_text('Sortable Data Tables').click()


    def test_sort_tables(self):
        driver = self.driver

        table_data = [[] for i in range(5)]
        print(table_data)

        for i in range (5):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]')
            table_data[i].append(header.text)
            

            for j in range(4):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]')
                table_data[i].append(row_data.text)


        print(table_data)

    def tearDown(self):
        self.driver.close()


if __name__ == "__main__":
    unittest.main(verbosity = 2)

Aquí mi aproximación al ejercicio, la cual creo que es la solución a la que quería llegar el profesor.

import unittest
from selenium import webdriver


class Tables(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome(executable_path= r'/mnt/c/Users/luism/OneDrive/Desktop/chromedriver.exe')
        driver = self.driver
        driver.get("link") # ingresa el link
        driver.find_element_by_link_text('Sortable Data Tables').click()
        driver.maximize_window()

    def test_sort_tables(self):
        driver = self.driver

        table_data = {}

        for i in range(5):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')
            table_data[header.text] = []

            for j in range(4):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i+1}]')
                table_data[header.text].append(row_data.text)
        
        print(table_data)

    def tearDown(self):
        self.driver.quit()


if __name__ == "__main__":
    unittest.main(verbosity = 2)

el tr[ {j + 1} ] es un error de logica , necesitas iterar sobre el tr[1] para sacar todos los nombres , en cambio te estas llendo a otros campos antes de tiempo, antes de sacar todos los nombres , cuidado ahi

deberian regrabar la clase , los datos de salida no tienen ningun sentido , el codigo tiene errores de logica

code

import unittest
from pyunitreport import HTMLTestRunner
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class CompareProducts(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome(executable_path='./chromedriver')
        driver = self.driver
        driver.implicitly_wait(30)
        driver.maximize_window()
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element_by_link_text('Sortable Data Tables').click()


    def test_sort_tables(self):
        driver = self.driver

        table_data = [[] for i in range(5)]
        print(table_data)

        for i in range(5):
            header =driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i+1}]/span')
            table_data[i].append(header.text)

            for j in range(4):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j+1}]/td[{j+1}]')
                table_data[j].append((row_data.text))
       
        print(table_data)


    def tearDown(self):
        self.driver.close()

if __name__ == '__main__':
    unittest.main(verbosity=2, testRunner= HTMLTestRunner(output= 'reports', report_name= 'hello-world-report'))

🤠

import unittest
from selenium import webdriver
from time import sleep

class Tables(unittest.TestCase):

    def setUp(self) -> None:
        self.driver = webdriver.Chrome(executable_path = './chromedriver')
        driver = self.driver
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element_by_link_text('Sortable Data Tables').click()
        return super().setUp()
    
    def test_data_table(self):
        driver = self.driver
        self.assertTrue('tables'in driver.current_url)

        table_data = [[] for i in range(5)]
        print(table_data)

        for i in range(len(table_data)):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i+1}]/span')
            table_data[i].append(header.text)

            for j in range(4):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j+1}]/td[{i+1}]')
                table_data[i].append(row_data.text)
        
        print(table_data)

    def tearDown(self) -> None:
        self.driver.close()
        return super().tearDown()

if __name__=='__main__':
    unittest.main(verbosity = 2)
from selenium import webdriver
import unittest

class SortedTables(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome('..\chromedriver.exe')
        driver = self.driver
        driver.maximize_window()
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element_by_link_text('Sortable Data Tables').click()

    def test_tables(self):
        driver = self.driver
        table_data = [[] for i in range(5)]
        print(table_data)

        for i in range(5):
            header = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/thead/tr/th[{i+1}]/span')
            table_data[i].append(header.text)
            for j in range(4):
                row_data = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/tbody/tr[{j+1}]/td[{i+1}]')
                table_data[i].append(row_data.text)
        
        print(table_data)

    def tearDown(self):
        self.driver.close()

if _name_ == "_main_":
    unittest.main(verbosity=2)

Este es mi aporte con selectores de css, eso si yo quería visualizar los datos distintos, ni como lo ve el profe, ni como un json (es decir, { “name” : [“a”, “b”, …] , …}, o algo de ese estilo), sino la misma tabla pero en una array bidimensional, donde las rows serían efectivamente las rows por instancia, y las columnas serían los vectores, o atributos, que definen a la instancia.

def test_sort_tables(self):
        driver = self.driver
        n, m = 5, 6
        myTable = [[0 for j in range(m)] for i in range(n)]
        # header
        headers = driver.find_elements_by_css_selector('#table1 > thead > tr > th > span')
        for i in range(len(headers)):
            myTable[0][i] = headers[i].text
        # data
        for i in range(1, n):
            for j in range(m):
                data_i = driver.find_element_by_css_selector(f"#table1 > tbody > tr:nth-child({i}) > td:nth-child({j+1})")
                myTable[i][j] = data_i.text
        print(myTable)
        sleep(5)

El resultado sería algo del estilo:

Aqui mi versión antes de ver la solución

import unittest
from pyunitreport import HTMLTestRunner
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class ExtractData(unittest.TestCase):

    @classmethod
    def setUp(cls):
        options = Options()
        options.binary_location = '/snap/brave/119/opt/brave.com/brave/brave'
        cls.driver = webdriver.Chrome(options = options, executable_path="./chromedriver")
        driver = cls.driver
        driver.get("https://the-internet.herokuapp.com/tables")
        driver.maximize_window()
    
    def test_get_data(self):
        driver = self.driver

        header = driver.find_elements_by_css_selector("#table1 thead th")
        column_names = [c.text for c in header]
        rows = driver.find_elements_by_css_selector("#table1 tbody tr")
        data = [{"id":i} for i,row in enumerate(rows)]
        for i, row in enumerate(rows):
            vls = row.find_elements_by_css_selector("td")
            for k, v in zip(column_names,vls):
                data[i][k] = v.text

        print(data)

    @classmethod
    def tearDown(cls):
        cls.driver.quit()

if __name__ == "__main__":
    unittest.main(verbosity=2, testRunner=HTMLTestRunner(output="reportes", report_name="automatic_search"))

Lista ordenada (posicion 0 es el titulo de la columna):

        for i in range(5):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i+1}]/span')
            table_data[i].append(header.text)
        for i in range(5):
            for j in range(4):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j +1}]/td[{i+1}]')
                table_data[i].append(row_data.text)
                print(table_data)

Adjunto el código donde el resultado corresponderá a cada fila de la tabla.

def test_sort_tables(self):
    driver = self.driver

    table_data = [[] for i in range(ROWS)]

    print()
    for fila in range(ROWS):
        for columna in range(COLUMNS):
            if fila == 0:
                header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{columna + 1}]/span')
                table_data[fila].append(header.text)
            else:
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{fila}]/td[{columna + 1}]')
                table_data[fila].append(row_data.text)
        print(table_data[fila])

El resultado por consola fue el siguiente:

En el video muestra siempre la misma primera línea en la impresión en la consola, debe ser algo con los for y la variable que se coloca en el XPath.

Este es mi código:

"""
    Este scrip obtiene el header de una tabla y su contenido
"""

import unittest
from selenium import webdriver

class Tables(unittest.TestCase):

    def setUp(self):
        self.webdriver = webdriver.Chrome(executable_path='../chromedriver_win32/chromedriver.exe')
        self.webdriver.get('iserte la url aca')
        self.webdriver.maximize_window()
        self.webdriver.implicitly_wait(1)
        self.webdriver.find_element_by_link_text('Sortable Data Tables').click()

    def test_sort_tables(self):
        webdriver = self.webdriver
        table_data = list()

        # Obtengo una lista de los tr en el body de la tabla
        # a dicha lista le aplico un len() y obtenermos cuantos datos hay en el body
        data_size = webdriver.find_elements_by_xpath('//*[@id="table1"]/tbody/tr')

        # Obtener una lista de los th contenido en el thead de la tabla
        # Con la lista de los th, le aplico un len a la lista y veos cuantos header hay
        header_size = webdriver.find_elements_by_xpath('//*[@id="table1"]/thead/tr/th')
        

        for i in range(1,len(data_size)+1):
            for j in range(1,len(header_size)): # Omitimos el header action
                header = webdriver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{j}]/span').text
                content = webdriver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{i}]/td[{j}]').text
                table_data.append({header:content})
        
        print(table_data)
                

            

    def tearDown(self):
        self.webdriver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)

La salida:

Pista reto revisa los xpath

El problema de los magic number lo podemos solucionar usando una variable llamada colums = 5 como ejemplo y remplazamos el 5 por dicha variable

Reto cumplido:

	def test_sort_tables(self):
		driver = self.driver
		list_of_elements = [[] for i in range(5)]
		print(list_of_elements)
		
		for i in range(5):
			element = driver.find_element_by_xpath('//*[@id="table1"]/thead/tr/th[{}]/span'.format(i+1))
			list_of_elements[0].append(element.text)

		for i in range(1,5):
			for j in range(5):
				element = driver.find_element_by_xpath('//*[@id="table1"]/tbody/tr[{}]/td[{}]'.format(i,j+1))
				list_of_elements[i].append(element.text)
		for i in range(5):		
			print(list_of_elements[i])

Un pequeño scrapping de la primera tabla:

def test_tables(self):
        driver = self.driver

        person = {}
        big_list = []
        keys = []
        i = 1; j = 1; x = 1
        y = 0; r = 0
        
        while True:
            try:
                header = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/thead/tr/th[{i}]/span')
                keys.append(header.text)
                i += 1
            except:
                break


        while True:
            try:
                driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/tbody/tr[{x}]/td[1]')
                while True:
                    try:
                        item = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/tbody/tr[{x}]/td[{j}]')
                        person[keys[y]] = item.text
                        j += 1; y += 1 
                    except:
                        break
                big_list.append(person.copy())
                print(big_list)
                j = 1; x += 1; y = 0; r += 1
            except:
                break
import unittest
from selenium import webdriver
from api_data_mock import ApiDataMock
from selenium.webdriver.support.ui import Select # Modulo para poder seleccionar del dropdown
from time import sleep
from selenium.webdriver.common.by import By #Hacer referencia a un elemento del sitio web, para interactuar
from selenium.webdriver.support.ui import WebDriverWait # Modulo para  manejo de esperas explicitas
from selenium.webdriver.support import expected_conditions as EC #esperas explicitas




class Tables(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome(executable_path = r'./chromedriver.exe')
        driver = self.driver
        # driver.implicitly_wait(30)
        driver.maximize_window()
        driver.get('*************************') #colocar el sitio web
        driver.find_element_by_link_text('Sortable Data Tables').click()
        

    def test_sort_tables(self):
        driver = self.driver
        COLUMNS = 5
        FILES = 4
        table_data = [[] for i in range(COLUMNS)]
        print(table_data)

        for i in range(COLUMNS):
            # table_data[i].append(header.text)
            for j in range (FILES):
                header = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/thead/tr/th[{i + 1}]/span')
                row_data = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/tbody/tr[{j + 1}]/td[{i + 1}]')
                table_data[j].append({header.text : row_data.text})

        print(table_data)
        

    def tearDown(self):
        self.driver.implicitly_wait(5)
        self.driver.close()


   
if __name__ == '__main__':
    unittest.main(verbosity=2)

Creo que en el código del video se están recorriendo varias veces los datos (una por cada columna de la tabla), porque está el for anidado con el otro, en realidad no es necesario.

Esta es mi solución

driver = self.driver
table = [ ]

    for i in range(1,6):
        column = [ ]
	header_xpath = f'/html/body/div[2]/div/div/table[1]/thead/tr/th[{i}]/span'
	header = driver.find_element_by_xpath(header_xpath)
	column.append(header.text)

	for j in range(1, 5):
	    cell_xpath = f'/html/body/div[2]/div/div/table[1]/tbody/tr[{j}]/td[{i}]'
	    cell = driver.find_element_by_xpath(cell_xpath)
	    column.append(cell.text)

    table.append(column)

pongo al xpath como una variable para evitar lineas muy largas, porque si no el linter me jode.
y remplace el +1 en el xpath por subir 1 el inicio y el fin del rango.
fuera de eso lo unico que hice es que el segundo for muestre los datos que corresponden a nuestro interes

La tabla está incorrecta y es por este pequeño detalle:

for j in range(4):
        row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]')
        table_data[i].append(row_data.text)

En vez de usar j dos veces, primero usamos j y después i.
Así obtuve esta tabla:
[
[‘Last Name’, ‘Smith’, ‘Bach’, ‘Doe’, ‘Conway’],
[‘First Name’, ‘John’, ‘Frank’, ‘Jason’, ‘Tim’],
[‘Email’, ‘[email protected]’, ‘[email protected]’, ‘[email protected]’, ‘[email protected]’],
[‘Due’, ‘$50.00’, ‘$51.00’, ‘$100.00’, ‘$50.00’],
[‘Web Site’, ‘https://www.jsmith.com’, ‘https://www.frank.com’, ‘https://www.jdoe.com’, ‘https://www.timconway.com’]
]
Y listo, ya tuvimos todos los datos de la tabla acomodados en sus columnas y filas correspondientes.

hola a todos, el resultado obtenido por el profesor no es el correcto, si se fijan bien, por cada columna esta entregando los valores de la una sola fila de la tabla, el único valor que cambio fue el nombre del header, lo demás esta repetido en cada lista: ‘Smith’, ‘Frank’, ‘[email protected]’, ‘50’ esta secuencia se repite en todas, además los datos no son los correspondientes, puesto el nombre del seño Smith no es Frank, sino John,… el error esta en los ciclos usando los iteradores, por el momento les comparto mi solución que da el resultado esperado, sin usar ciclos anidados e imprime un datagrama con la ayuda de pandas

Yo lo hice de esta forma, me gusta un poco mas ya que los muestra en forma de tabla o similar. Nose si era el objetivo de la clase o si esta hecho de la mejor forma. Soy nuevo en esto, pero estoy satisfecho con lo que quedo. Saludos a todos!

import unittest
from selenium import webdriver

class Tables(unittest.TestCase):
    
    def setUp(self) -> None:
        self.driver = webdriver.Chrome(executable_path=r'C:\Users\ignac\OneDrive\Escritorio\Selenium con Python\chromedriver.exe')
        driver = self.driver
        driver.get('https://the-internet.herokuapp.com/')
        driver.find_element_by_link_text('Sortable Data Tables').click()

    def test_sort_tables(self):
        driver = self.driver

        table_data = [[] for i in range(5)]

        for i in range(5):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')
            table_data[0].append(header.text)

        g = 1

        while g < 5: 

            for j in range(5):
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{g}]/td[{j+1}]')
                table_data[g].append(row_data.text)
            
            g += 1
        
        for o in range(5):
            print(table_data[o])

    def tearDown(self) -> None:
        self.driver.close()
    
if __name__ == '__main__':
    unittest.main(verbosity=2)

lo realice de esta manera:

import unittest
from selenium import webdriver
from time import sleep

class oerdenar_tabla(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome(executable_path=r'C:/chromedriver.exe')
        driver = cls.driver
        driver.maximize_window()
        driver.get('######') 
        driver.implicitly_wait(15)
        driver.find_element_by_link_text('Sortable Data Tables').click()

    def test_prueba1(self):
        driver = self.driver

        data = [[] for i in range(5)]

        headers = driver.find_elements_by_xpath('//*[@id="table1"]/thead/tr/th/span')

        for i in range(0, 5):
            data[0].append(headers[i].text)
        print('\n')
        print(data[0])
        for i in range(1,5):
            for j in range(0,5):
                dato = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{i}]/td[{j+1}]').text
                data[i].append(dato)
            print(data[i])

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

if __name__ == '__main__':
    unittest.main(verbosity=2)

Salida:

['Last Name', 'First Name', 'Email', 'Due', 'Web Site']
['Smith', 'John', '[email protected]', '$50.00', '#######']
['Bach', 'Frank', '[email protected]', '$51.00', '#######']
['Doe', 'Jason', '[email protected]', '$100.00', '#######']
['Conway', 'Tim', '[email protected]', '$50.00', '#######']

#agregue "######" en lugar de los enlaces para que me dejara subirlo

Saludos chicos aqui les dejo el codigo de la clase con algunos comentarios:

import unittest
from selenium import webdriver
from time import sleep

class SortablaData(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox(executable_path= r'./geckodriver.exe')
        driver = self.driver
        driver.get("https://the-internet.herokuapp.com/")
        driver.find_element_by_link_text('Sortable Data Tables').click()

    def test_sortable_data(self):
        driver = self.driver
        columnas = 5
        filas = 4

        #Lista vacia que contiene almacenara los datos
        table_data = [[] for i in range(columnas)]#Cinco por la cantidad de columnass que existen
        print(table_data)#Impresion de valores

        #Iteracion por headers y informacion de las tablas
        for i in range(columnas):
            header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')#Copiamos el Xpath por que permite iterar sobre el
            table_data[i].append(header.text)#Ingresamos los datos a la tabla vacia

            for j in range(filas): #Ciclo para recorrer filas
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j + 1}]/td[{i + 1}]')
                table_data[i].append(row_data.text)

    def tearDown(self):
        self.driver.close()

if __name__ == "__main__":
    unittest.main()

Dejo mi aporte.

def test_data(self):
        driver = self.driver

        WebDriverWait(driver, 15).until(EC.title_contains('The Internet'))

        headers = []        #Obteniendo los header(Last name, First name...)
        for i in range(5):
            get_header = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/thead/tr/th[{i + 1}]')
            headers.append(get_header.text)

        cells = []          #Obteniendo los valores de los headers en sublistas([Smith,Jhon,[email protected]], [], etc)
        for i in range(4):
            cells_block = []
            for j in range(5):
                get_cells = driver.find_element_by_xpath(f'/html/body/div[2]/div/div/table[1]/tbody/tr[{i + 1}]/td[{j + 1}]')
                cells_block.append(get_cells.text)                
            cells.append(cells_block)

        data = {}       #Fusionando todo en un diccionario con diccionarios dentro que contienen la info de cada persona.
        for i in range(4):
            user_data = {}
            for j in range(5):
                user_data.update({headers[j] : cells[i][j]})

            data.update({f'Persona{i + 1}': user_data})

        print(data)```
       for i in range(6):
           header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i+1}]/span')
           table_data[i].append(header.text)
           for j in range(4):
               row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{j+1}]/td[{i+1}]')
               table_data[i].append(row_data.text)
       print(table_data)

listo reto cumplido!!

cada clase más interesante u.u

Brillante