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?

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.

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=鈥渢able1鈥漖/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:
[[鈥楲ast Name鈥, 鈥楽mith鈥, 鈥楤ach鈥, 鈥楧oe鈥, 鈥楥onway鈥橾, [鈥楩irst Name鈥, 鈥楯ohn鈥, 鈥楩rank鈥, 鈥楯ason鈥, 鈥楾im鈥橾, [鈥楨mail鈥, 鈥[email protected]鈥, 鈥[email protected]鈥, 鈥[email protected]鈥, 鈥[email protected]鈥橾, [鈥楧ue鈥, 鈥$50.00鈥, 鈥$51.00鈥, 鈥$100.00鈥, 鈥$50.00鈥橾, [鈥榃eb 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 鈥減ip 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 鈥渋鈥 en el 鈥渢d鈥 del xpath
  • y modifique la linea 27 cambiando 鈥渋鈥 por 鈥渏鈥
  • 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, { 鈥渘ame鈥 : [鈥渁鈥, 鈥渂鈥, 鈥 , 鈥, 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:
[
[鈥楲ast Name鈥, 鈥楽mith鈥, 鈥楤ach鈥, 鈥楧oe鈥, 鈥楥onway鈥橾,
[鈥楩irst Name鈥, 鈥楯ohn鈥, 鈥楩rank鈥, 鈥楯ason鈥, 鈥楾im鈥橾,
[鈥楨mail鈥, 鈥[email protected]鈥, 鈥[email protected]鈥, 鈥[email protected]鈥, 鈥[email protected]鈥橾,
[鈥楧ue鈥, 鈥$50.00鈥, 鈥$51.00鈥, 鈥$100.00鈥, 鈥$50.00鈥橾,
[鈥榃eb 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: 鈥楽mith鈥, 鈥楩rank鈥, 鈥[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