No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Ordenar tablas

20/24
Recursos

Aportes 55

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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.

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)


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'}]

¿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.

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!

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

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']]

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)

Hola, acá mi aporte, tiene el extra de que determina el número de filas y columnas de la tabla, esto lo hace através de los selectores css.

def test_sort_tables(self):
    driver = self.driver
    
    # Compute rows and columns
    rows = len(driver.find_elements_by_css_selector('#table1 > tbody > tr'))
    cols = len(driver.find_elements_by_css_selector('#table1 > thead > tr > th'))
    
    table = [[] for i in range(rows+1)]
    
    # Add headers
    for i in range(rows):
        header = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{i + 1}]/span')
        table[0].append(header.text)
    
    # Add table data
    for i in range(rows):
        for j in range(cols):
            row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{i + 1}]/td[{j + 1}]')
            table[i].append(row_data.text)
    
    # Let's give it a basic format
    formated_rows = [', '.join(i) for i in table]
    print('\n' + '\n'.join(formated_rows))

En mi humilde opinion con el selector CSS se puede hacer mas rapido, siplemente pones la tabla y ya, nada de for loop.

En pantalla mi respuesta:

import unittest
from selenium import webdriver
from pyunitreport import HTMLTestRunner
from selenium.webdriver.common.by import By

class HomePageTests(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome(executable_path = 'chromedriver.exe')
        cls.driver.get('https://the-internet.herokuapp.com/tables')
        cls.driver.maximize_window()
        cls.driver.implicitly_wait(15)


    def test_xfil(self):
        """Test of table by its xpath"""
        
        data_table = self.driver.find_element(By.CSS_SELECTOR, '#table1')
        print(data_table.text)
    
    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

if __name__ == '__main__':
    unittest.main(verbosity = 2, 
        testRunner = HTMLTestRunner(output = 'reportes', report_name = 'table-data-report'))

Aquí está mi resolución del reto, espero a alguien le sea de ayuda.

Con este código logré que devuelva, en cada lista creada (creadas en base a la cantidad de filas que tiene la tabla) en forma de diccionarios, cada dato del header junto al que contiene el body.

El código está todo comentado para que se entienda mejor.

import unittest
from selenium import webdriver
from time import sleep


class Tables(unittest.TestCase):
    
    def setUp(self):
        self.driver = webdriver.Chrome(executable_path=r'C:\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

        # Get the len of the head data ( Table 1 ), getting a list of elements from his css selector ( -1 for the edit/delete panel )
        columns_size = len(driver.find_elements_by_css_selector('#table1 > thead > tr > th')) - 1
        # Get the len of data size ( in rows ) from the table 1 ( Without header )
        row_size = len(driver.find_elements_by_css_selector('#table1 > tbody > tr'))

        table_data = [[] for i in range(row_size)] # Create a list with empty lists inside (the quantity of rows of the body)
        print(table_data)

        # Lets save all data
        for i in range(row_size): # Iter through each row of the data

            # Now iter through each column of the data
            for j in range(columns_size):
                # Take each head data text of the table
                header_data = driver.find_element_by_xpath(f'//*[@id="table1"]/thead/tr/th[{j + 1}]/span').text # j will iter column by column
                # Take each body data text
                row_data = driver.find_element_by_xpath(f'//*[@id="table1"]/tbody/tr[{i + 1}]/td[{j + 1}]').text # i iter row by row
                # Append to the table data, as a dict
                table_data[i].append({header_data : row_data})

        print(table_data)

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


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

Esto es lo que devuelve el código.

esta clase esta bastante improvisada, las iteraciones le salen mal y el lo sabe, y no hace nada por corregirlas

El reto queda así:

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

COLUMN_NUMBER = 5
ROW_NUMBER = 4

class Tables(unittest.TestCase):
    
    def setUp(self):
        self.driver = webdriver.Chrome(executable_path="./usr/bin/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(COLUMN_NUMBER)]
        print(table_data)

        for i in range(COLUMN_NUMBER):
            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(ROW_NUMBER):
                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)

A quién le sirva, el siguiente código extrae los datos de header y body de forma aislada

y genera un dataframe de pandas 😃

def test_sort_tables(self):
        driver = self.driver
        header = driver.find_elements(By.XPATH, '//*[@id="table1"]/thead/tr/th')
        header_text = [cell.text for cell in header[:-1]]
        body_table = driver.find_elements(By.XPATH, '//*[@id="table1"]/tbody/tr')
        table = []
        for row in body_table:
            values = row.find_elements(By.XPATH, "td")
            values = [cell.text for cell in values[:-1]]
            table.append(values)
        table_df = pd.DataFrame(data=table, columns=header_text)
        print(table_df)

el código para instalar pandas es

pip install pandas

Con el ordenamiento de la clase los datos quedan escalonados, se cambio la linea row_data = driver.find_element(By.XPATH,f’//*[@id=“table1”]/tbody/tr[{j + 1}]/td[{i + 1}]’) para que conserve los datos de la columna y se itere solo las filas y queda asi, ya sale la columna web site con los datos de sus filas:
[[‘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’, aqui sales las urls que no deja aportar en el comentario’]]

Considerando la organizacion correcta de los datos, actualizando el codigo actualizado. (deprecated) El codigo final quedaria asi.

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

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

    def test_sort_table(self):
        driver = self.driver

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

        for i in range(5):
            header = driver.find_element(by=By.XPATH, value=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=By.XPATH, value=f'//*[@id="table1"]/tbody/tr[{j+1}]/td[{i+1}]')
                table_data[i].append(row_data.text)

            ptable.add_column(header.text, table_data[i])
        #print(table_data)
        print(ptable)

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

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


Hice el reto utilizando list comprehension y dict comprehension y asi creo una lista de diccionarios como un API retorna los datos de base de datos.

    def test_sort_tables(self):
        driver = self.driver
        table_data = []

        header_elements = driver.find_elements(
            By.CSS_SELECTOR, '#table1 > thead > tr > th.header')
        header_list = [
            header.text for header in header_elements if not header.text == 'Action']
        rows_elements = driver.find_elements(
            By.CSS_SELECTOR, '#table1 > tbody > tr')

        for row in rows_elements:
            field_elements = row.find_elements(By.CSS_SELECTOR, 'td')
            row_data = {header_list[i]: field_elements[i].text
                        for i in range(len(header_list))}
            table_data.append(row_data)

        print('\n', table_data)

Usé este código para imprimir la tabla con PrettyTable

 pip3 install prettytable
from prettytable import PrettyTable
        table = PrettyTable()
        
        for column in table_columns:
            table.add_column(column[0],column)
        
        table.del_row(0)

        print(table)

Yo lo hice con diccionarios para mas placer

def test_b_sort_tables(self):
        link = self.driver.find_element(
            By.XPATH, '/html/body/div[2]/div/ul/li[41]/a')
        link.click()
        data_list = []
        list_len = self.driver.find_elements(
            By.XPATH, '//*//table[@id="table1"]/tbody/tr')
        for i in range(len(list_len)):
            info = self.driver.find_elements(
                By.XPATH, f'//*//table[@id="table1"]/tbody/tr[{i+1}]/td')
            user_info = [x.text for x in info]
            # de aqui en adelante es para poner los valores en diccionarios
            dict = {"Last Name": "", "First Name": "",
                    "Email": "", "Due": "", "Web Site": ""}
            dict.update({"Last Name": user_info[0]})
            dict.update({"First Name": user_info[1]})
            dict.update({"Email": user_info[2]})
            dict.update({"Due": user_info[3]})
            dict.update({"Web Site": user_info[4]})
            data_list.append(dict)
        print(data_list)

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,jsmith@...], [], 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