На початку подумаємо з чого складається типовий інтернет-магазин? Я виділив 3 основні функціональні блоки:
Каталог товарів. Кошик користувача. Оформлення замовле
Ці три частини складають основу і без них неможливо уявити роботу магазину. Почнемо із першої частини т.к. вона найбільш трудомістка.
Каталог товарів.
Якщо ви вирішили створити свій інтернет-магазин, то, очевидно, знаєте приклади готових рішень, які вам підходять найбільше і відображають вашу ідею та асортимент продукції. Нерозумно цим не скористатися і не скопіювати інформацію з інших сайтів. Я говорю саме про інформацію про товари, ціни, картинки, опис і т.д. Наявність інструменту для автоматичного збору подібних даних може заощадити колосальну кількість часу, яка потрібна для наповнення вашого майбутнього інтернет магазину. Найпростішим буде створення деякого скрипту, який би ходив сайтом-прикладом і збирав все необхідне, складаючи у вигляді певної структури папок і файлів. Далі цю структуру ми передамо іншій програмі, яка заповнить нашу базу даних і сам сайт.
Тепер обговоримо структуру каталогу даних, назвемо його data.
Припустимо, ми продаємо штори та хочемо створити інтернет магазин штор. Візьмемо якийсь приклад. У видачі “магазин штор” у мене першим вийшов https://pangardin.com.ua Думаю, було б логічним підкаталоги назвати ім’ям інтернет-ресурсу. Але крім каталогу продукції, на всіх сайтах присутні і статичні сторінки з інформацією про власника, доставку, зворотний зв’язок та ін. Думаю що подібну інформацію можна оформити у спеціальних текстових файлах у корені каталогу data.
Орієнтовна структура каталогу даних:
. data
..pages
...main.txt
...about.txt
...contact.txt
..pangardin.com.ua
...tul
...shtory
...furnitura
Як видно, ми розбиваємо товари за каталогами, називатимемо їх каталог-категоріями, при цьому всередині таких категорій необхідний якийсь мета файл з інформацією про цю категорію.
Можна скористатися форматом yml, як на мене, це найбільш читальний варіант оформлення структурованих даних.
Так і назвемо цей файл – meta.yml. З урахуванням багатомовності цей файл може виглядати так:
content_type: category
is_published: true
order: 1
title_ru: Тюль.
title_ua: Тюль.
desc_ru: |
Самая лучшая <strong>тюль</strong>!
desc_ua: |
Дуже гарна тюль!
meta_title_ru: Купить тюль. Интернет-магазин штор.
meta_title_ua: Створення iнтернет магазина штор
meta_keywords_ru: тюль шторы купить интернет-магазин
meta_keywords_ua: ….
meta_description_ru: ….
meta_description_ua: ….
Я заложил минимум информации о каталоге. Первым делом я определяю тип информации, для того чтобы отличать описание категории и товара и определять в каком каталоге находится программа. Либо это каталог-категория, либо каталог-товар. Далее идет признак опубликованности на сайте, сортировка и информация для страницы категории.
Подобным образом можно оформить и статичные страницы в файлах *.yml в каталоге pages. Например так:
content_type: page
is_published: true
title_ru: Разработка интернет-магазина штор.
desc_ru: |
Как разработать интернет магазин на <strong>python</strong>!
Примерная структура каталога товарной позиции:
.tul-metis
..images
...1.png
...2.png
meta.yml
description.md
Для опису товару я визначив файл description.md, який використовує формат markdown для розмітки тексту, але дозволяє використовувати і html нативний html.
Файл опису товарної позиції.
name_slug: shtory-raduga
name_ru: Веселка штори.
meta_title_ua: Перші кроки Python.
meta_keywords_ua: Перші кроки Python.
meta_description_ua: Перші кроки Python.
is_published: false
Для простоти, поки оперуватимемо однією, російською локалізацією.
Отже, наше наступне завдання – опитати сайт-джерело скриптом та створити необхідну структуру каталогів. Для цього я використовуватиму бібліотеки Python requests і BeatifulSoup.
Бібліотекою requests ми будемо генерувати запити HTTP на сервер і отримувати HTML код сторінок.
Бібліотекою BeatifulSoup ми цей HTML парсим і знаходимо в ньому елементи (теги), що цікавлять, і забираємо їх вміст.
На початку нас цікавить сама структура каталогу. Її можна знайти на головній сторінці у спливаючому блоці.
HTML код цього списку виглядає так:
<ul class="sub-menu">
<li class="menu-item ..."><h4 class='mega_menu_title'>Категорії</h4>
<ul class="sub-menu">
<li class="menu-item ..."><a href="..."><span class="avia-bullet"></span>Тканини-компаньйони/a></li>
...
Я прибрав зайві css класи для наочності. Що ми маємо - це два вкладені списки ul з категоріями та підкатегоріями.
Підкатегорії, крім назви, мають і посилання на сторінку з товарами, її ми також використовуватимемо для отримання інформації про продукти кожної взятої категорії.
Усі наведені нижче приклади наведено для операційної системи на базі Debian.
Створення віртуального оточення.
virtualenv -p python3 venv3
Після створення віртуального оточення необхідно його активувати, набравши таку команду в консолі.
. ./venv3/bin/activate
Встановити наші бібліотеки можна такими командами.
pip install requests
pip install beatifulsoup4
Якщо у вас не встановлена програма pip, то можна поставити її так:
sudo apt install python-pip
Нижче наведено Python код із докладними коментарями, який розбирає структуру категорій та виводить її на екран терміналу.
#!/usr/bin/env python
# імпортуємо бібліотеки
import requests
from bs4 import BeautifulSoup
print("Getting catalog")
# робимо запит на отримання головної сторінки
url = 'https://pangardin.com.ua'
r = requests.get(url)
# розпізнаємо HTML, створюючи спеціальний об'єкт soup
soup = BeautifulSoup(r.text, 'html.parser')
# нзнаходимо головний (батьківський) елемент списку категорій
ulsout = soup.find('ul',{'class': 'sub-menu'})
# у циклі проходимо по всіх його елементах
for ulout in ulsout:
liout = ulout.find('li')
if liout != -1:
print('.'+liout.text)
# для ккожної батьківської категорії знаходимо дочірній список підкатегорій
ulins = ulout.find('ul')
# у циклі проходимо за підкатегоріями
for liin in ulins.findAll('li'):
print('..'+liin.text)
Висновок програми:
.Тканини-Компаньйони
..Тканини-Компаньйони
..Тюль
..Портьєрні тканини
..Готові штори
..Штори нитки
..Постільна білизна
..Фурнітура
….
Все що тут відбувається – це перебираються ul – списки у двох вкладених циклах.
Тепер залишилося перевести назви до трансліту та створити потрібні каталоги.
Транслітувати можна бібліотекою python-slugify і робиться це передачею потрібного рядка у функцію slugify.
name_slug = slugify(name)
Для зручності програму завжди поділяють на дрібніші підпрограми, наприклад функції.
Функцію збереження категорії на диск можна описати так:
def save_category(cat_name,parent_name):
''' охорона каталогів-категорій '''
# транслітеруємо російську назву категорії
cat_name_slug = slugify(cat_name)
# транслітеруємо російську назву підкатегорії
parent_name_slug = slugify(parent_name)
# виводимо налагоджувальне повідомлення в консоль
print("Creating category %s with parent %s" % (cat_name,parent_name))
# формуємо шлях для створення каталогу
dir_name = os.path.join(DATA_DIR,cat_name_slug)
# створюємо каталог, якщо його не існує
if not os.path.isdir(dir_name):
os.mkdir(dir_name)
Для того, щоб використовувати таку функцію, її необхідно викликати, передавши два параметри - назву категорії та підкатегорії.
Це робитимемо у внутрішньому циклі.
for liin in ulins.findAll('li'):
save_category(liin.text, liout.text)
Повний код програми та результат його роботи наведено нижче.
#!/usr/bin/env python
import requests
from bs4 import BeautifulSoup
import os
from slugify import slugify
### створюю каталоги з даними
if not os.path.isdir('data'):
os.mkdir('data')
if not os.path.isdir(os.path.join('data','pangardin.com.ua')):
os.mkdir(os.path.join('data','pangardin.com.ua'))
###
DATA_DIR = os.path.join('data','pangardin.com.ua')
def save_category(cat_name,parent_name):
''' сохранение каталогов-категорий '''
cat_name_slug = slugify(cat_name)
parent_name_slug = slugify(parent_name)
print("Creating category %s with parent %s" % (cat_name,parent_name))
dir_name = os.path.join(DATA_DIR,cat_name_slug)
if not os.path.isdir(dir_name):
os.mkdir(dir_name)
print("Getting catalog")
url = 'https://pangardin.com.ua'
r = requests.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
ulsout = soup.find('ul',{'class': 'sub-menu'})
for ulout in ulsout:
liout = ulout.find('li')
if liout != -1:
ulins = ulout.find('ul')
for liin in ulins.findAll('li'):
save_category(liin.text, liout.text)
Доповнимо функцію save_category створенням meta.yml файлів, що мінімально описують категорії.
У ці файли будемо писати імена та псевдоніми поточної та батьківської категорії.
def save_category(cat_name,parent_name):
''' береження каталогів-категорій '''
# транслітеруємо російську назву категорії
cat_name_slug = slugify(cat_name)
# транслітеруємо російську назву підкатегорії
parent_name_slug = slugify(parent_name)
# виводимо налагоджувальне повідомлення в консоль
print("Creating category %s with parent %s" % (cat_name,parent_name))
# формуємо шлях для створення каталогу
dir_name = os.path.join(DATA_DIR,cat_name_slug)
# створюємо каталог, якщо його не існує
if not os.path.isdir(dir_name):
os.mkdir(dir_name)
# створюємо рядок в який вставляємо назви категорій
meta_info = '''
content_type: category
is_published: true
name_slug: %s
name_ru: %s
parent_slug: %s
parent_name_ru: %s
order: 1
descr: |
''' % (cat_name_slug,cat_name,parent_name_slug,parent_name)
with open(os.path.join(dir_name,'meta.yml'),'w') as f:
f.write(meta_info)
Функція проходу сторінкою каталогу
def save_goods(url,category):
# Отримуємо сторінку категорії
r = requests.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
# знаходимо список товарів
products = soup.find('ul',{'class': 'products'}).findAll('li')
for product in products:
# Заголовок
title = product.find('div',{'class': 'inner_product_header'}).find('h3').text
title_slug = slugify(title)
# фоормуємо шлях
path = os.path.join(DATA_DIR,category,title_slug)
# створюємо каталог товару
print("Saving ... %s" % title_slug)
if not os.path.isdir(path):
os.mkdir(path)
# знаходимо посилання на сторінку товару
link = product.find('div',{"class": "inner_cart_button"}).find('a').get('href')
# зберігаємо позицію
save_position(link,path)
Поглянемо на те, як виглядає HTML код позиції товару на сторінці категорії
https://pangardin.com.ua/category/complect/artdeco/
<li class="...">
<div class="inner_product wrapped_style ">
<div class="avia_cart_buttons single_button">
<div class="inner_cart_button">
<a href="https://pangardin.com.ua/magazin/ballades/" >Перегляд товару/a>
</div>
</div>
<a href="https://pangardin.com.ua/magazin/ballades/">
<div class="thumbnail_container">
<img src="..." class="attachment-shop_catalog wp-post-image" alt="Ballade">
</div>
<div class="inner_product_header">
<h3>Ballades</h3>
<span class="price"><span class="amount">208грн.</span>
<span class="amount">366грн.</span>
</span>
</div>
</a>
</div>
</li>
Після отримання елемента li
products = soup.find(‘ul’,{‘class’: ‘products’}).findAll(‘li’)
ми вибрали блок із класом inner_product_header і використовували заголовок для знаходження назви товару.
itle = product.find(‘div’,{‘class’: ‘inner_product_header’}).find(‘h3’).text
Тепер необхідно отримати URL адресу сторінки з описом товару.
link = product.find(‘div’,{“class”: “inner_cart_button”}).find(‘a’).get(‘href’)
Опишемо функцію збереження позиції.
def save_position(link,path):
r = requests.get(link)
with open('log.html','w') as f:
f.write(r.text)
soup = BeautifulSoup(r.text, 'html.parser')
print(link)
#sys.exit('s')
# знаходимо посилання на зображення
img_link = soup.find('a',{"class": "MagicZoomPlus"}).get('href')
print("Downloading pic %s" % img_link)
# забираємо картинку
r = requests.get(img_link, stream=True)
# створюємо каталог images
path_to_img = os.path.join(path,'images')
if not os.path.isdir(path_to_img):
os.mkdir(path_to_img)
# зберігаємо картинку
image_full_path = os.path.join(path_to_img,'1.png')
if r.status_code == 200:
with open(image_full_path, 'wb') as f:
r.raw.decode_content = True
shutil.copyfileobj(r.raw, f)
# знаходимо назву та опис
title = soup.find('h1',{"class": "product_title"}).text
description = soup.find('div',{"itemprop": "description"}).text
# формуємо вміст meta.yml
meta_info = '''
name_slug: %s
name_ru: %s
meta_title_ru: %s
meta_keywords_ru: %s
meta_description_ru: %s
is_published: true
description_ru: |
%s
''' % (slugify(title),title,title,title,title,description)
# запись в файл
with open(os.path.join(path,'meta.yml'),'w') as f:
f.write(meta_info)
При пошуку картинки ми спочатку знаходимо елемент div з класом inner_cart_button, а потім у ньому посилання, з якого атрибут href. Завантаживши сторінку опису товару знайдемо елемент із зображенням у вихідному коді.
<a class="MagicZoomPlus" href="https://pangardin.com.ua/wp-content/uploads/2013/04/DSC00047.jpg" >
<img itemprop="image" src="..." >
<div class="MagicZoomPup">
</div>
...
</a>
Отримати на нього посилання можна таким рядком коду, де ми шукаємо за класом MagicZoomPlus.
img_link = soup.find('a',{"class": "MagicZoomPlus"}).get('href')
Щоб отримати більш докладну і “чисту” інформацію про товар знадобиться деяка вправність у роботі зі структурою HTML сторінки.
Швидше за все, треба буде відсівати зайву інформацію та вибирати те, що вам потрібно для майбутнього сайту.
Приклад нашого сайту-джерела написаний на Wordpress і для кожного нового сайту знадобиться написати свій власний парсер контенту.