alizhgir commited on
Commit
0024afc
1 Parent(s): 442d2f1

init commit

Browse files
Files changed (6) hide show
  1. Main.py +39 -0
  2. model/bert.py +45 -0
  3. pages/Recommend_page.py +67 -0
  4. pages/Results.py +12 -0
  5. parsing.ipynb +177 -0
  6. requirements.txt +62 -0
Main.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+
4
+ st.header("""
5
+ Проект по рекомендациям книг различного жанра📚
6
+ """, divider='blue')
7
+ st.info("### Только на этом сервисе ты сможешь найти лучший аналог своей любимой книги🔝")
8
+
9
+ st.image('images/preview_image.png', caption='Картинка сгенерирована DALL-E')
10
+
11
+ st.write("""
12
+ ### Уникальный состав команды:
13
+ \n- ##### Алиса Жгир 💥
14
+ \n- ##### Тигран Арутюнян 💥
15
+ \n- ##### Руслан Волощенко 💥
16
+ """)
17
+
18
+ st.info("""
19
+ ### Цель проекта:
20
+ \n- ##### Построить алгоритм RecSys, способный предлагать пользователю лучшие рекомендации, \
21
+ отталкиваясь от его предпочтений, желаний и настроения.
22
+ """)
23
+
24
+ st.info("""
25
+ ### Задачи:
26
+ \n- ##### Построить алгоритм парсинга информации с книжного сайта ✅
27
+ \n- ##### Полученные данные очистить и сделать рабочий Dataset ✅
28
+ \n- ##### Создать RecSys, способную делать релеватные рекомендации для конкретного пользователя ✅
29
+ \n- ##### Построить Streamlit приложение для общедоступного пользования ✅
30
+ """)
31
+
32
+ st.info("""
33
+ ### Используемые технологии (Стек проекта):
34
+ \n- ##### Python
35
+ \n- ##### Языковая модель ruBERT-tiny
36
+ \n- ##### Библиотеки: BeautifulSoup4, Sentence Transformers, faiss, transformers и др.
37
+ \n- ##### Cosine similarity для рекомендаций
38
+ \n- ##### Hugging Face & Streamlit
39
+ """)
model/bert.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import torch
4
+ import faiss
5
+
6
+ from transformers import AutoTokenizer, AutoModel
7
+
8
+
9
+ weight = "cointegrated/rubert-tiny2"
10
+
11
+ tokenizer = AutoTokenizer.from_pretrained(weight)
12
+ model = AutoModel.from_pretrained(weight)
13
+
14
+ vectors_annotation = np.load('datasets/annotation_embeddings2.npy')
15
+ data_frame = pd.read_csv('datasets/cleaned_final_books.csv')
16
+
17
+ MAX_LEN = 512
18
+
19
+ faiss_index = faiss.IndexFlatL2(312)
20
+
21
+ faiss_index.add(vectors_annotation)
22
+
23
+
24
+ def recommend(text, top_k):
25
+
26
+ tokenized_text = tokenizer.encode(text, add_special_tokens=True, truncation=True, max_length=MAX_LEN)
27
+ tokenized_text = torch.tensor(tokenized_text).unsqueeze(0)
28
+
29
+ with torch.inference_mode():
30
+ predict = model(tokenized_text)
31
+
32
+ vector = predict[0][:, 0, :].squeeze().cpu().numpy()
33
+
34
+ vector = np.array([vector])
35
+ value_metrics, index = faiss_index.search(vector, k=top_k)
36
+
37
+ recommend_books = data_frame.iloc[index.reshape(top_k,)][['category_name', 'author', 'title', 'age', 'annotation']].reset_index(drop=True)
38
+ recommend_books = recommend_books.rename({'category_name': 'Жанр', 'author': 'Автор', 'title': 'Название книги', \
39
+ 'age': 'Возрастное ограничение', 'annotation': 'Аннотация'}, axis=1)
40
+
41
+ return recommend_books
42
+
43
+
44
+
45
+
pages/Recommend_page.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import time
5
+ import base64
6
+ import requests
7
+ from PIL import Image
8
+ from io import BytesIO
9
+
10
+ from model.bert import recommend
11
+
12
+
13
+ list_genre = ['Классическая литература', 'Современная проза', 'Отечественные детективы',
14
+ 'Зарубежные детективы', 'Иронические детективы', 'Отечественная фантастика', 'Зарубежная фантастика',
15
+ 'Отечественное фэнтези', 'Зарубежное фэнтези', 'Ужасы', 'Фантастический боевик',
16
+ 'Российские любовные романы', 'Зарубежные любовные романы', 'Поэзия', 'Драматургия',
17
+ 'Публицистика', 'Биографии', 'Мемуары', 'Исторические романы', 'Комисксы и манга', 'Юмор',
18
+ 'Афоризмы и цитаты', 'Мифы легенды эпос', 'Сказки', 'Пословицы поговорки загадки', 'Прочие издания', 'Другое']
19
+
20
+
21
+ st.header("""
22
+ Рекомендательная модель🤖
23
+ """, divider='blue')
24
+
25
+ st.info("""
26
+ - ##### Именно здесь вы сможете получить ТОП-рекомендаций под ваши предпочтения и желания🔝
27
+ \n- ##### Вам предстоит лишь сделать краткое описание книги, которую вы хотели бы прочитать, и выбрать некоторые параметры поиска⚙️
28
+ """)
29
+
30
+ st.image('images/recsys_image.png', caption='Картинка сгенерирована DALL-E')
31
+
32
+ st.write("""
33
+ - ### Выбор параметров поиска:
34
+ """)
35
+
36
+ text_users = st.text_input('**Пожалуйста, опишите ваши предпочтения по выбору книги (какой она должна быть):**')
37
+
38
+ genre_book = st.selectbox('**Пожалуйста, укажите жанр книги:**', list_genre)
39
+
40
+ author = st.text_input('**Пожалуйста, укажите имя автора, если для вас это важно (❗НЕОБЯЗАТЕЛЬНО):**')
41
+
42
+ count_recommended = st.slider('**Пожалуйста, укажите какое количество рекомендаций Вы хотите получить:**', min_value=1, max_value=10, value=5)
43
+
44
+ push_button = st.button('**Получить рекомендации >>>**', type='primary')
45
+ start_time = time.time()
46
+
47
+ if push_button:
48
+
49
+ recommend_book = recommend(text_users, count_recommended)
50
+
51
+ st.write(f"""
52
+ #### Модель нашла лучшие рекомендации для Вас🎉 :
53
+ \n- ##### Это заняло всего {round(time.time() - start_time, 3)} сек.
54
+ """)
55
+ st.table(recommend_book)
56
+ time.sleep(3)
57
+ with st.sidebar:
58
+ st.info("""
59
+ #### Понравились ли Вам наши рекомендации?
60
+ """)
61
+
62
+ col1, col2 = st.columns(2)
63
+
64
+ with col1:
65
+ st.button('**Да, очень**🔥', type='primary')
66
+ with col2:
67
+ st.button('**Нет,можно лучше**👎🏻', type='primary')
pages/Results.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+
4
+ st.write("""
5
+ # Итоги и результаты работы по проекту🔥
6
+ """)
7
+
8
+ st.info("""
9
+ #### История о том, как мы знали, что BERT выиграет гонку, но решили использовать все существующие инструменты для векторизации текста.
10
+ """)
11
+
12
+
parsing.ipynb ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "nbformat": 4,
3
+ "nbformat_minor": 0,
4
+ "metadata": {
5
+ "colab": {
6
+ "provenance": []
7
+ },
8
+ "kernelspec": {
9
+ "name": "python3",
10
+ "display_name": "Python 3"
11
+ },
12
+ "language_info": {
13
+ "name": "python"
14
+ }
15
+ },
16
+ "cells": [
17
+ {
18
+ "cell_type": "code",
19
+ "execution_count": null,
20
+ "metadata": {
21
+ "id": "Rfoz1Nim_nx_"
22
+ },
23
+ "outputs": [],
24
+ "source": [
25
+ "from bs4 import BeautifulSoup\n",
26
+ "import lxml\n",
27
+ "import xml.etree.ElementTree as ET\n",
28
+ "import csv\n",
29
+ "import pandas as pd\n",
30
+ "import requests\n",
31
+ "from bs4 import BeautifulSoup, element\n",
32
+ "import pandas as pd\n",
33
+ "import csv\n",
34
+ "from bs4 import element\n"
35
+ ]
36
+ },
37
+ {
38
+ "cell_type": "code",
39
+ "source": [
40
+ "def get_book_links(category_id, base_url=\"https://www.biblio-globus.ru/catalog/category?id=\"):\n",
41
+ " \"\"\"Извлекает ссылки на книги из категории с указанным id.\"\"\"\n",
42
+ " page_number = 1\n",
43
+ "\n",
44
+ " while True:\n",
45
+ " url = f\"{base_url}{category_id}&page={page_number}\"\n",
46
+ " response = requests.get(url)\n",
47
+ " soup = BeautifulSoup(response.text, 'html.parser')\n",
48
+ "\n",
49
+ " # Извлечение ссылок на книги\n",
50
+ " links = soup.find_all('a', class_='img_link')\n",
51
+ " if not links:\n",
52
+ " print(f\"Сканирование category_id {category_id} page_number {page_number} завершено.\")\n",
53
+ " break # Выход из цикла, если страница не содержит ссылок\n",
54
+ "\n",
55
+ " for link in links:\n",
56
+ " book_link = link.get('href')\n",
57
+ " if book_link and book_link.startswith('/product/'):\n",
58
+ " full_link = f\"https://www.biblio-globus.ru{book_link}\"\n",
59
+ " # Запись в CSV-файл\n",
60
+ " with open('book_links.csv', 'a', newline='', encoding='utf-8') as file:\n",
61
+ " writer = csv.writer(file)\n",
62
+ " writer.writerow([full_link])\n",
63
+ "\n",
64
+ " page_number += 1\n",
65
+ "\n",
66
+ "categories = [226, 227, 241, 242, 248, 250, 251, 6168, 6169, 6170, 6171, 262, 263, 6173, 6174, 6176, 6177, 6178, 6179, 6180, 6181, 6182, 6183, 6184, 6186, 6187, 6188, 6189] # Добавьте остальные категории по необходимости\n",
67
+ "\n",
68
+ "# Создание заголовка CSV-файла\n",
69
+ "with open('book_links.csv', 'w', newline='', encoding='utf-8') as file:\n",
70
+ " writer = csv.writer(file)\n",
71
+ " writer.writerow(['book_link'])\n",
72
+ "\n",
73
+ "# Получение ссылок для каждой категории\n",
74
+ "for category in categories:\n",
75
+ " get_book_links(category)"
76
+ ],
77
+ "metadata": {
78
+ "colab": {
79
+ "base_uri": "https://localhost:8080/"
80
+ },
81
+ "id": "_sl6wNU5SzEI",
82
+ "outputId": "ca837e22-8919-4066-b2da-b376697c9971"
83
+ },
84
+ "execution_count": 21,
85
+ "outputs": [
86
+ {
87
+ "output_type": "stream",
88
+ "name": "stdout",
89
+ "text": [
90
+ "Сканирование category_id 226 page_number 151 завершено.\n",
91
+ "Сканирование category_id 227 page_number 413 завершено.\n",
92
+ "Сканирование category_id 241 page_number 90 завершено.\n",
93
+ "Сканирование category_id 242 page_number 99 завершено.\n",
94
+ "Сканирование category_id 248 page_number 5 завершено.\n",
95
+ "Сканирование category_id 250 page_number 89 завершено.\n",
96
+ "Сканирование category_id 251 page_number 96 завершено.\n",
97
+ "Сканирование category_id 6168 page_number 33 завершено.\n",
98
+ "Сканирование category_id 6169 page_number 34 завершено.\n",
99
+ "Сканирование category_id 6170 page_number 9 завершено.\n",
100
+ "Сканирование category_id 6171 page_number 13 завершено.\n",
101
+ "Сканирование category_id 262 page_number 12 завершено.\n",
102
+ "Сканирование category_id 263 page_number 16 завершено.\n",
103
+ "Сканирование category_id 6173 page_number 32 завершено.\n",
104
+ "Сканирование category_id 6174 page_number 3 завершено.\n",
105
+ "Сканирование category_id 6176 page_number 4 завершено.\n",
106
+ "Сканирование category_id 6177 page_number 18 завершено.\n",
107
+ "Сканирование category_id 6178 page_number 10 завершено.\n",
108
+ "Сканирование category_id 6179 page_number 1 завершено.\n",
109
+ "Сканирование category_id 6180 page_number 1 завершено.\n",
110
+ "Сканирование category_id 6181 page_number 8 завершено.\n",
111
+ "Сканирование category_id 6182 page_number 35 завершено.\n",
112
+ "Сканирование category_id 6183 page_number 4 завершено.\n",
113
+ "Сканирование category_id 6184 page_number 3 завершено.\n",
114
+ "Сканирование category_id 6186 page_number 6 завершено.\n",
115
+ "Сканирование category_id 6187 page_number 64 завершено.\n",
116
+ "Сканирование category_id 6188 page_number 73 завершено.\n",
117
+ "Сканирование category_id 6189 page_number 3 завершено.\n"
118
+ ]
119
+ }
120
+ ]
121
+ },
122
+ {
123
+ "cell_type": "code",
124
+ "source": [
125
+ "def scrape_book_data(url):\n",
126
+ " collect = []\n",
127
+ " response = requests.get(url)\n",
128
+ " soup = BeautifulSoup(response.text, 'html.parser')\n",
129
+ " content = {\n",
130
+ " 'image': soup.find('meta', property=\"og:image\")['content'] if soup.find('meta', property=\"og:image\") else '',\n",
131
+ " 'author': soup.find('meta', property=\"og:book:author\")['content'] if soup.find('meta', property=\"og:book:author\") else '',\n",
132
+ " 'title': soup.find('meta', property=\"og:title\")['content'] if soup.find('meta', property=\"og:title\") else '',\n",
133
+ " 'annotation': soup.find('div', {\"class\": \"collapse\", \"id\": \"collapseExample\"}) if soup.find('div', {\"class\": \"collapse\", \"id\": \"collapseExample\"}) else ''\n",
134
+ " }\n",
135
+ "\n",
136
+ " if content['annotation'] != '' and content['annotation'].children:\n",
137
+ " for el in content['annotation'].children:\n",
138
+ " if isinstance(el, element.Tag):\n",
139
+ " el.decompose()\n",
140
+ " collect.append(url)\n",
141
+ " collect.append(content['image'])\n",
142
+ " collect.append(content['title'])\n",
143
+ " collect.append(content['author'])\n",
144
+ " collect.append(content['annotation'].get_text(strip=True) if content['annotation'] != '' else '')\n",
145
+ " return collect\n",
146
+ "\n",
147
+ "# Загрузка списка URL из файла\n",
148
+ "urls_df = pd.read_csv('book_links(1).csv')\n",
149
+ "\n",
150
+ "# Открытие файла для записи результатов\n",
151
+ "with open('books.csv', 'w', newline='', encoding='utf-8') as csvfile:\n",
152
+ " writer = csv.writer(csvfile, escapechar='\\\\', quoting=csv.QUOTE_MINIMAL)\n",
153
+ " writer.writerow(['page_url', 'image_url', 'author', 'title', 'annotation'])\n",
154
+ "\n",
155
+ " for index, row in urls_df.iterrows():\n",
156
+ " book_data = scrape_book_data(row['book_link'])\n",
157
+ " writer.writerow(book_data)\n",
158
+ " print(f\"Информация о книге: {row['book_link']} записана в файл books.csv\")\n",
159
+ "\n"
160
+ ],
161
+ "metadata": {
162
+ "id": "8U8VSC8KTONT"
163
+ },
164
+ "execution_count": 23,
165
+ "outputs": []
166
+ },
167
+ {
168
+ "cell_type": "code",
169
+ "source": [],
170
+ "metadata": {
171
+ "id": "TAxdA0XLTVhg"
172
+ },
173
+ "execution_count": 22,
174
+ "outputs": []
175
+ }
176
+ ]
177
+ }
requirements.txt ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ altair==5.2.0
2
+ attrs==23.1.0
3
+ blinker==1.7.0
4
+ cachetools==5.3.2
5
+ certifi==2023.11.17
6
+ charset-normalizer==3.3.2
7
+ click==8.1.7
8
+ faiss-cpu==1.7.4
9
+ filelock==3.13.1
10
+ fsspec==2023.12.2
11
+ gitdb==4.0.11
12
+ GitPython==3.1.40
13
+ huggingface-hub==0.19.4
14
+ idna==3.6
15
+ importlib-metadata==6.11.0
16
+ Jinja2==3.1.2
17
+ joblib==1.3.2
18
+ jsonschema==4.20.0
19
+ jsonschema-specifications==2023.11.2
20
+ markdown-it-py==3.0.0
21
+ MarkupSafe==2.1.3
22
+ mdurl==0.1.2
23
+ mpmath==1.3.0
24
+ networkx==3.2.1
25
+ numpy==1.26.2
26
+ packaging==23.2
27
+ pandas==2.1.4
28
+ Pillow==10.1.0
29
+ protobuf==4.25.1
30
+ pyarrow==14.0.1
31
+ pydeck==0.8.1b0
32
+ Pygments==2.17.2
33
+ python-dateutil==2.8.2
34
+ pytz==2023.3.post1
35
+ PyYAML==6.0.1
36
+ referencing==0.32.0
37
+ regex==2023.10.3
38
+ requests==2.31.0
39
+ rich==13.7.0
40
+ rpds-py==0.13.2
41
+ safetensors==0.4.1
42
+ scikit-learn==1.3.2
43
+ scipy==1.11.4
44
+ six==1.16.0
45
+ smmap==5.0.1
46
+ streamlit==1.29.0
47
+ sympy==1.12
48
+ tenacity==8.2.3
49
+ threadpoolctl==3.2.0
50
+ tokenizers==0.15.0
51
+ toml==0.10.2
52
+ toolz==0.12.0
53
+ torch==2.1.1
54
+ tornado==6.4
55
+ tqdm==4.66.1
56
+ transformers==4.36.1
57
+ typing_extensions==4.9.0
58
+ tzdata==2023.3
59
+ tzlocal==5.2
60
+ urllib3==2.1.0
61
+ validators==0.22.0
62
+ zipp==3.17.0