diff --git a/coin.py b/coin.py new file mode 100755 index 0000000..2deab1e --- /dev/null +++ b/coin.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +############################################################ +### Project: coin +### File: __init__.py +### Description: main file of coin +### Version: 1.0 +############################################################ +import sys, os, glob +import PyQt6 +import gui +import tess + +def main(): + model_names = tess.get_tess_model_names() + + if len(model_names) == 0: + print("[ERR] No tesseract model found at", MODELS_PATH, file=sys.stderr) + sys.exit(1) + + print("Available models:\n", model_names) + + app = PyQt6.QtWidgets.QApplication(sys.argv) + main_window = gui.coin_gui(model_names) + main_window.show() + sys.exit(app.exec()) + +if __name__ == '__main__': + main() diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..d91c755 --- /dev/null +++ b/constants.py @@ -0,0 +1,55 @@ +############################################################ +### Project: coin +### File: constants.py +### Description: defines all the constants +### Version: 1.0 +############################################################ +# Change this if tesseract or coin can't find any data +MODELS_PATH = '/usr/share/tessdata/' + +# Sane values for scaling +SCALE_MIN = 0.2 +SCALE_MAX = 5.0 +SCALE_DEFAULT = 1.0 +# How much (in decimal) to change the scale for every degree +SCALE_DELTA = 8 * 360 * 1 + +# Sane values for text recognition +WIDTH_MIN = 10 +HEIGHT_MIN = 10 + +# Table info +TABLE_COL_CNT = 5 +TABLE_COL_GET = 0 +TABLE_COL_DEL = 1 +TABLE_COL_UPC = 2 +TABLE_COL_NAME = 3 +TABLE_COL_PRICE = 4 +TABLE_HEADERS = [" Get ", " Del ", "UPC", "Item Name", "Price"] + +# Cookies for upc.py +COOKIE_DICT_LIST = [ + { + "name": "PHPSESSID", + "value": "9n1ic479r1bteiv758gm9hk65p", + "path": "/", + "domain": "stocktrack.ca" + }, + { + "name": "cf_chl_3", + "value": "1d706187484b25c", + "path": "/", + "domain": "stocktrack.ca" + }, + { + "name": "cf_clearance", + "value": "Wp8tAMUKLdS3a4Y9AT09BIlZKx4x120uC1QzBQTUluQ-1710517775-1.0.1.1-hMEP8oeggZHBkylkwkQfi2p57H6zUUvGG40d_M4vGqOqg2Zh7wZsg6KrGl3XkDUn3mXAqyZrTqlQfd5pgHCZWQ", + "path": "/", + "domain": "stocktrack.ca" + }, + { + "name": "fp", + "value": "26f4acb9b23415f921bba6977b68d55f", + "path": "/", + "domain": "stocktrack.ca" + }] diff --git a/gui.py b/gui.py index e4b98e6..8346165 100644 --- a/gui.py +++ b/gui.py @@ -1,5 +1,9 @@ -# This module handles the GUI of coin -import pytesseract +############################################################ +### Project: coin +### File: gui.py +### Description: handles the GUI of coin +### Version: 1.0 +############################################################ import cv2, os, sys from PIL import Image import PyQt6 @@ -9,161 +13,86 @@ from PyQt6.QtWidgets import * from PyQt6 import uic from PyQt6 import QtCore, QtGui, QtWidgets import glob -import upc -from upc import get_name_from_upc - -# [!!!] Change this if tesseract or coin can't find any data -MODELS_PATH = '/usr/share/tessdata/' - -# [!!!] Sane values for scaling -SCALE_MIN = 0.3 -SCALE_MAX = 4.0 -# How much (in decimal) to change the scale for every degree -SCALE_DELTA = 8 * 360 * 1 - -# [!!!] Sane values for recognition -WIDTH_MIN = 10 -HEIGHT_MIN = 10 - -# Column Information -TABLE_COL_CNT = 5 -TABLE_COL_GET = 0 -TABLE_COL_DEL = 1 -TABLE_COL_UPC = 2 -TABLE_COL_NAME = 3 -TABLE_COL_PRICE = 4 - -# Automatically finds all available language models -models_list = glob.glob(MODELS_PATH + "*.traineddata") -model_names = [] -for path in models_list: - base_name = os.path.basename(path) - base_name = os.path.splitext(base_name)[0] - model_names.append(base_name) -if len(model_names) == 0: - print("No tesseract models found at", MODELS_PATH, "!", file=sys.stderr) - sys.exit(0) +from upc import * +from constants import * +import tess class UPCWorker(QObject): finished = pyqtSignal() update_name = pyqtSignal(list) - - def __init__(self, upcList): - super().__init__() - self.upcList = upcList + # requires: upc_list is a list of list of two elements with the + # first element being the UPC's row number and the second being a + # string, containing the UPC + def __init__(self, upc_lst): + super().__init__() + self.upc_lst = upc_lst + + # get_items_name(): get the names of all items using + # get_name_from_upc from the list of upc_list. Emits a pair of + # values stored in a list, the first element is the row number + # and the second is the item's name def get_items_name(self): - for upc in self.upcList: + for upc in self.upc_lst: name = get_name_from_upc(upc[1]) self.update_name.emit([upc[0], name]) self.finished.emit() -class coin_app(QtWidgets.QMainWindow): - def __init__(self): +class coin_gui(QtWidgets.QMainWindow): + # requires: model_names must be valid and not empty + def __init__(self, model_names): + print("Initializing GUI...") + QtWidgets.QMainWindow.__init__(self) self.ui = uic.loadUi('coin.ui', self) - self.image = None - self.ui.openButton.clicked.connect(self.open) + self.ui.openButton.clicked.connect(self.open_image) self.ui.getNameButton.clicked.connect(self.get_all) self.ui.saveFileButton.clicked.connect(self.save_file) self.ui.saveDbButton.clicked.connect(self.save_database) - self.rubberBand = QRubberBand(QRubberBand.Shape.Rectangle, self) + self.rubber_band = QRubberBand(QRubberBand.Shape.Rectangle, self) self.ui.photo.setMouseTracking(True) self.ui.photo.installEventFilter(self) self.ui.photo.setAlignment(PyQt6.QtCore.Qt.AlignmentFlag.AlignTop) - - self.model = model_names[0] - print("Model selected as:", self.model) - self.models.addItems(model_names) - self.models.currentTextChanged.connect(self.update_now) - self.models.setCurrentIndex(model_names.index(self.model)) - + self.image = None self.scale = 1.0 self.items = None - self.upcCheckTimer = QTimer(self) - self.upcCheckTimer.timeout.connect(self.pushUPCToThread) - self.upcCheckTimer.start(1500) + if len(model_names) == 0: + print("[ERR] No tesseract model given to coin_gui!", file=sys.stderr) + sys.exit(1) + self.model_names = model_names + self.update_model(self.model_names[0]) + self.models.addItems(self.model_names) + self.models.currentTextChanged.connect(self.update_model) + self.models.setCurrentIndex(self.model_names.index(self.model)) + + self.upc_chk_timer = QTimer(self) + self.upc_chk_timer.timeout.connect(self.push_upc_to_thread) + self.upc_chk_timer.start(1500) print("Initialized timer!") - self.upcThread = QThread() - self.upcWorker = None - self.upcCheckList = [] + self.upc_thread = None + self.upc_worker = None + self.upc_chk_lst = [] - def pushUPCToThread(self): - if len(self.upcCheckList) > 0 and self.upcWorker is None: - self.upcWorker = UPCWorker(self.upcCheckList) - self.upcWorker.moveToThread(self.upcThread) - - self.upcWorker.update_name.connect(self.updateItemName) - self.upcWorker.finished.connect(self.collectUPCWorker) - self.upcThread.finished.connect(self.collectUPCThread) - - self.upcThread.started.connect(self.upcWorker.get_items_name) - self.upcThread.start() - - print("Lookup thread started!") - - self.upcCheckList = [] - - def collectUPCWorker(self): - self.upcWorker.deleteLater() - self.upcWorker = None - - def collectUPCThread(self): - self.upcThread.deleteLater() - - def updateItemName(self, name): - itemName = QStandardItem(name[1]) - itemName.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - self.items.setItem(name[0], TABLE_COL_NAME, itemName) - self.itemsTable.resizeColumnToContents(TABLE_COL_NAME) - self.itemsTable.resizeRowToContents(name[0]) - - def update_now(self, value): - self.model = value - print("Model selected as:", self.model) - - def set_table_header(self): - item = QStandardItem(" Get ") - item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - item.setEditable(False) - self.items.setItem(0, TABLE_COL_GET, item) - item = QStandardItem(" Del ") - item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - item.setEditable(False) - self.items.setItem(0, TABLE_COL_DEL, item) - item = QStandardItem("UPC") - item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - item.setEditable(False) - self.items.setItem(0, TABLE_COL_UPC, item) - item = QStandardItem("Item Name") - item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - item.setEditable(False) - self.items.setItem(0, TABLE_COL_NAME, item) - item = QStandardItem("Price") - item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - item.setEditable(False) - self.items.setItem(0, TABLE_COL_PRICE, item) - self.itemsTable.resizeColumnsToContents() - - def open(self): + # open_image(): prompts to open an image, if an error occurrs, the + # image within the photo view will not be updated, otherwise, the + # image will be updated and stored in self.pixmap + def open_image(self): filename = QFileDialog.getOpenFileName(self, 'Select File') if filename[0] == "": print("No file was selected or opened!") return None - if self.items is not None: - self.items.deleteLater() - print("Opening file: ", filename[0]) self.image = cv2.imread(str(filename[0])) if self.image is None: print("Error: image is empty!", file=sys.stderr) + print("No file was opened!") return None frame = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB) @@ -171,11 +100,12 @@ class coin_app(QtWidgets.QMainWindow): frame.strides[0], QImage.Format.Format_RGB888) self.pixmap = QPixmap.fromImage(image) - self.ui.photo.setPixmap(self.pixmap) - self.scale = 1.0 - print("Updated photo!") - print("Photo scale set to:", self.scale) + self.scale = SCALE_DEFAULT + self.update_scale() + if self.items is not None: + self.items.deleteLater() + self.items = QStandardItemModel() self.itemsTable.setModel(self.items) self.items.setColumnCount(TABLE_COL_CNT) @@ -187,6 +117,8 @@ class coin_app(QtWidgets.QMainWindow): return None + # save_file(): prompt to save a file, will write to the file in + # .csv format def save_file(self): filename = QFileDialog.getSaveFileName(self, 'Select File') @@ -203,53 +135,107 @@ class coin_app(QtWidgets.QMainWindow): print(name, file=out, end=", ") print(self.items.item(i,TABLE_COL_PRICE).text().replace("\n", " "), file=out) - out.close() + out.close() self.statusBar().showMessage("Saved to: " + filename[0]) + # save_database(): saves to a database def save_database(self): print("Not implemented!") - def scale_update(self): + # set_table_header(): sets the headers of the table when a new + # image is opened + def set_table_header(self): + for i in range(TABLE_COL_CNT): + item = QStandardItem(TABLE_HEADERS[i]) + item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + item.setEditable(False) + self.items.setItem(0, i, item) + + self.itemsTable.resizeColumnsToContents() + + # update_scale(): update the image with the new scale + def update_scale(self): self.ui.photo.setPixmap(self.pixmap.scaled( int(self.scale * self.pixmap.width()), int(self.scale * self.pixmap.height()))) - print("Photo scale set to:", self.scale) + print("Photo scale set to:", self.scale) self.statusBar().showMessage("Photo scale set to: " + str(self.scale)) - def image_to_text(self, cropped_image): - gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY) - gray = cv2.medianBlur(gray, 1) - crop = Image.fromarray(gray) - text = pytesseract.image_to_string(crop, lang=self.model).strip() - print("Text selected:", text) - return text + # push_upc_to_thread(): runs a new thread when there are items in + # self.upc_chk_lst and self.upc_thread is available + def push_upc_to_thread(self): + if len(self.upc_chk_lst) > 0 and self.upc_worker is None: + self.upc_thread = QThread() + + self.upc_worker = UPCWorker(self.upc_chk_lst) + self.upc_worker.moveToThread(self.upc_thread) - def get_row(self): - button = self.sender() - row = self.itemsTable.indexAt(button.pos()).row() - upc = self.items.item(row, TABLE_COL_UPC).text() - self.upcCheckList.append([row, upc]) - print("Started getting name of UPC:", upc) - self.statusBar().showMessage("Started getting name of UPC: " + upc) - self.pushUPCToThread() + self.upc_worker.update_name.connect(self.update_item_name) + self.upc_worker.finished.connect(self.upc_thread.quit) + self.upc_worker.finished.connect(self.delete_upc_worker) + self.upc_thread.finished.connect(self.delete_upc_thread) + self.upc_thread.started.connect(self.upc_worker.get_items_name) + self.upc_thread.start() + + print("Lookup thread started!") + + self.upc_chk_lst = [] + + # delete_upc_worker(): wrapper for self.upc_worker.deleteLater() + def delete_upc_worker(self): + self.upc_worker.deleteLater() + self.upc_worker = None + + # delete_upc_thread(): wrapper for self.upc_thread.deleteLater() + def delete_upc_thread(self): + print("Lookup thread is finished!") + self.upc_thread.deleteLater() + + # update_item_name(name): name is a two-element list with the + # first element being the row in which is item is located, and + # the second being a string containing the item's name + def update_item_name(self, name): + item = QStandardItem(name[1]) + item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + self.items.setItem(name[0], TABLE_COL_NAME, item) + self.itemsTable.resizeColumnToContents(TABLE_COL_NAME) + self.itemsTable.resizeRowToContents(name[0]) + + # get_all(): get the names of items in every row that doesn't have + # a name in it def get_all(self): for i in range(1, self.items.rowCount()): - if self.items.item(i, TABLE_COL_NAME) is None or self.items.item(i, TABLE_COL_NAME).text() == "N/A": + if (self.items.item(i, TABLE_COL_NAME) is None or + self.items.item(i, TABLE_COL_NAME).text() == "N/A"): upc = self.items.item(i, TABLE_COL_UPC).text() print("Started getting name of UPC:", upc) self.statusBar().showMessage("Started getting name of UPC: " + upc) - self.upcCheckList.append([i, upc]) - self.pushUPCToThread() + self.upc_chk_lst.append([i, upc]) + self.push_upc_to_thread() + # get_item_at_row(): get the name of the item at the row where the + # "Get" button was clicked + def get_item_at_row(self): + button = self.sender() + row = self.itemsTable.indexAt(button.pos()).row() + upc = self.items.item(row, TABLE_COL_UPC).text() + self.upc_chk_lst.append([row, upc]) + print("Started getting name of UPC:", upc) + self.statusBar().showMessage("Started getting name of UPC: " + upc) + self.push_upc_to_thread() + + # del_row(): delete the row where the "Del" button was clicked def del_row(self): button = self.sender() index = self.itemsTable.indexAt(button.pos()) self.items.removeRow(index.row()) print("Removed row", index.row()) + self.statusBar().showMessage("Removed row " + str(index.row())) + # populate_buttons(): put buttons on the table when creating a new row def populate_buttons(self): del_button = QPushButton("Del") self.itemsTable.setIndexWidget( @@ -261,17 +247,22 @@ class coin_app(QtWidgets.QMainWindow): self.itemsTable.setIndexWidget( self.items.index(self.items.rowCount() - 1, TABLE_COL_GET), get_button) - get_button.clicked.connect(self.get_row) + get_button.clicked.connect(self.get_item_at_row) + # update_model(): update self.model to the one selected + def update_model(self, value): + self.model = value + print("Model selected as:", self.model) + + # add_upc(text): adds a new row containing text in the UPC column def add_upc(self, text: str): if text == "": print("No text was recognized, will not add a new item!") return None - self.statusBar().showMessage("Text selected: " + text) + item = QStandardItem(text) item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.items.setItem(self.items.rowCount(), TABLE_COL_UPC, item) - item = QStandardItem("N/A") item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.items.setItem(self.items.rowCount() - 1, TABLE_COL_NAME, item) @@ -280,69 +271,92 @@ class coin_app(QtWidgets.QMainWindow): self.items.setItem(self.items.rowCount() - 1, TABLE_COL_PRICE, item) self.populate_buttons() + self.itemsTable.resizeColumnToContents(TABLE_COL_UPC) return None + # eventFilter(source, event): handles a few things differently def eventFilter(self, source, event): - # Handling the selection box - if event.type() == QEvent.Type.MouseButtonPress and source is self.ui.photo: - self.org = self.mapFromGlobal(event.globalPosition()) - self.top_left = event.position() - self.rubberBand.setGeometry(QRect(self.org.toPoint(), QSize())) - self.rubberBand.show() + if (event.type() == QEvent.Type.MouseButtonPress and + source is self.ui.photo): + self.rubber_band_show(event) return True - elif event.type() == QEvent.Type.MouseMove and source is self.ui.photo: - if self.rubberBand.isVisible(): - pos = self.mapFromGlobal(event.globalPosition()).toPoint() - pos = QPoint(int(max(pos.x(), 0)), - int(max(pos.y(), 0))) - self.rubberBand.setGeometry( - QRect(self.org.toPoint(), - pos).normalized()) - return True - - elif event.type() == QEvent.Type.MouseButtonRelease and source is self.ui.photo: - pos = event.position() - self.top_left = QPoint(int(max(min(pos.x(), self.top_left.x()), 0)), - int(max(min(pos.y(), self.top_left.y()), 0))) - if self.rubberBand.isVisible(): - self.rubberBand.hide() - rect = self.rubberBand.geometry() - self.x1 = int(self.top_left.x() / self.scale) - self.y1 = int(self.top_left.y() / self.scale) - width = rect.width() / self.scale - height = rect.height() / self.scale - self.x2 = int(self.x1 + width) - self.y2 = int(self.y1 + height) - else: - return False - if width >= WIDTH_MIN and height >= HEIGHT_MIN and self.image is not None: - print("Cropped image:", self.x1, self.y1, self.x2, self.y2) - self.crop = self.image[self.y1:self.y2, self.x1:self.x2] - # cv2.imwrite("cropped.png", self.crop) - self.text = self.image_to_text(self.crop) - self.add_upc(self.text) - ##self.ui.textEdit() - return True - - # Resizing the photo - elif event.type() == QEvent.Type.Wheel and self.image is not None and source is self.ui.photo: - modifiers = QApplication.keyboardModifiers() - if modifiers == Qt.KeyboardModifier.ControlModifier: - self.scale = self.scale + event.angleDelta().y() / SCALE_DELTA - if self.scale < SCALE_MIN: - self.scale = SCALE_MIN - if self.scale > SCALE_MAX: - self.scale = SCALE_MAX - self.scale_update() - return True - elif event.type() == QEvent.Type.MouseButtonDblClick and source is self.ui.photo: - self.open() + elif (event.type() == QEvent.Type.MouseMove and + source is self.ui.photo and self.rubber_band.isVisible()): + self.rubber_band_redraw(event) return True + + elif (event.type() == QEvent.Type.MouseButtonRelease and + source is self.ui.photo and self.rubber_band.isVisible()): + return self.rubber_band_select(event) + + elif (event.type() == QEvent.Type.Wheel and + self.image is not None and source is self.ui.photo and + QApplication.keyboardModifiers() == + Qt.KeyboardModifier.ControlModifier): + self.image_resize(event) + return True + + elif (event.type() == QEvent.Type.MouseButtonDblClick and + source is self.ui.photo): + self.open_image() + return True + return False -app = QtWidgets.QApplication(sys.argv) -mainWindow = coin_app() -mainWindow.show() -sys.exit(app.exec()) + # rubber_band_show(event): shows the rubber band for selection + def rubber_band_show(self, event): + self.org = self.mapFromGlobal(event.globalPosition()) + self.top_left = event.position() + self.rubber_band.setGeometry(QRect(self.org.toPoint(), QSize())) + self.rubber_band.show() + + # rubber_band_redraw(event): resizes the rubber band for selection + def rubber_band_redraw(self, event): + pos = self.mapFromGlobal(event.globalPosition()).toPoint() + pos = QPoint(int(max(pos.x(), 0)), + int(max(pos.y(), 0))) + self.rubber_band.setGeometry( + QRect(self.org.toPoint(), + pos).normalized()) + + # rubber_band_select(event): processes the rubber band for selection + def rubber_band_select(self, event): + pos = event.position() + self.top_left = QPoint(int(max(min(pos.x(), self.top_left.x()), 0)), + int(max(min(pos.y(), self.top_left.y()), 0))) + + self.rubber_band.hide() + rect = self.rubber_band.geometry() + self.x1 = int(self.top_left.x() / self.scale) + self.y1 = int(self.top_left.y() / self.scale) + width = rect.width() / self.scale + height = rect.height() / self.scale + self.x2 = int(self.x1 + width) + self.y2 = int(self.y1 + height) + + if (width >= WIDTH_MIN and height >= HEIGHT_MIN and + self.image is not None): + print("Cropped image:", self.x1, self.y1, self.x2, self.y2) + crop = self.image[self.y1:self.y2, self.x1:self.x2] + + text = tess.image_to_text(self.model, crop) + print("Text selected:", text) + self.statusBar().showMessage("Text selected: " + text) + self.add_upc(text) + return True + + return False + + # image_resize(event): resize the image when scrolling with Ctrl + def image_resize(self, event): + self.scale = self.scale + event.angleDelta().y() / SCALE_DELTA + + if self.scale < SCALE_MIN: + self.scale = SCALE_MIN + + if self.scale > SCALE_MAX: + self.scale = SCALE_MAX + + self.update_scale() diff --git a/tess.py b/tess.py new file mode 100644 index 0000000..707cfcd --- /dev/null +++ b/tess.py @@ -0,0 +1,31 @@ +############################################################ +### Project: coin +### File: tess.py +### Description: handles all pytesseract-related operations +### Version: 1.0 +############################################################ +import sys, os, glob +import cv2 +from PIL import Image +import pytesseract +from constants import * + +# get_tess_model_names(models_path): automatically finds all available +# language models in models_path +def get_tess_model_names(models_path=MODELS_PATH): + models_list = glob.glob(models_path + "*.traineddata") + model_names = [] + for path in models_list: + base_name = os.path.basename(path) + base_name = os.path.splitext(base_name)[0] + model_names.append(base_name) + return model_names + +# image_to_text(model, cropped_image): use the model with the name +# model to try to recognize the text in cropped_image +def image_to_text(model, cropped_image): + gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY) + gray = cv2.medianBlur(gray, 1) + crop = Image.fromarray(gray) + text = pytesseract.image_to_string(crop, lang=model).strip() + return text diff --git a/upc.py b/upc.py index 5c9826a..986a50f 100644 --- a/upc.py +++ b/upc.py @@ -1,10 +1,18 @@ +############################################################ +### Project: coin +### File: upc.py +### Description: scapes the item's name by using the its UPC +### Version: 1.0 +############################################################ import time from seleniumbase import Driver -# from selenium import webdriver +from constants import * driver = Driver(uc=True) driver.implicitly_wait(5) +# get_name_from_upc(upc, wait_interval, max_tries): will try to get +# the item's name from stocktrack.ca. def get_name_from_upc(upc: str, wait_interval=1, max_tries=30) -> str: url = "https://stocktrack.ca/wm/index.php?s=wm&upc=" + upc driver.get(url) @@ -13,59 +21,39 @@ def get_name_from_upc(upc: str, wait_interval=1, max_tries=30) -> str: print("Removed leading 0's:", upc) - # Change the cookies here to match that of yours when you lookup - # any item on stocktrack.ca to bypass the Cloudflare checks - driver.add_cookie({ - "name": "PHPSESSID", - "value": "9n1ic479r1bteiv758gm9hk65p", - "path": "/", - "domain": "stocktrack.ca" - }) - driver.add_cookie({ - "name": "cf_chl_3", - "value": "1d706187484b25c", - "path": "/", - "domain": "stocktrack.ca" - }) - driver.add_cookie({ - "name": "cf_clearance", - "value": "Wp8tAMUKLdS3a4Y9AT09BIlZKx4x120uC1QzBQTUluQ-1710517775-1.0.1.1-hMEP8oeggZHBkylkwkQfi2p57H6zUUvGG40d_M4vGqOqg2Zh7wZsg6KrGl3XkDUn3mXAqyZrTqlQfd5pgHCZWQ", - "path": "/", - "domain": "stocktrack.ca" - }) - driver.add_cookie({ - "name": "fp", - "value": "26f4acb9b23415f921bba6977b68d55f", - "path": "/", - "domain": "stocktrack.ca" - }) + for cookie in COOKIE_DICT_LIST: + driver.add_cookie(cookie) driver.refresh() name = "" - str_s = "target=\"_blank\">" - str_t = "
UPC: " + upc + "
" - str_tt = "
SKU:" - times = 0 - while times < max_tries: - if __debug__: - print("Iteration No. ", times) + pattern_front = "target=\"_blank\">" + pattern_back_upc = "
UPC: " + upc + "
" + pattern_back_sku = "
SKU:" - times = times + 1 + tries = 0 + + while tries < max_tries: + if __debug__: + print("Iteration No.", tries) + + tries = tries + 1 time.sleep(wait_interval) page = str(driver.execute_script( "return document.getElementsByTagName('html')[0].innerHTML")) - t = page.find(str_t) - s = page.rfind(str_s, 0, t) - tt = page.rfind(str_tt, 0, t) + + back_upc_idx = page.find(pattern_back_upc) + front_idx = page.rfind(pattern_front, 0, back_upc_idx) + back_sku_idx = page.rfind(pattern_back_sku, 0, back_upc_idx) if __debug__: p = open("page.html", "w") print(page, file=p) - if t == -1 or s == -1: + if back_upc_idx == -1 or front_idx == -1: continue else: - name = page[s + len(str_s) : tt] + name = page[front_idx + len(pattern_front) : back_sku_idx] break + return name.replace(" ", "\n")