Желаемый результат:
После запуска docker-compose up
я хочу, чтобы сервер запустился (docker-compose запускает базу данных и веб-службы), а затем, после того, как сервер запущен и работает, запускает скрипт python, содержащий код веб-очистки, который помещает очищенное содержимое в БД (и только запустить его один раз). Код использует модели Django для хранения данных в БД.
Проблема:
После запуска docker-compose up
база данных и сервер работают правильно, я могу получить доступ к сайту на localhost и получить ответы API,
но кажется, что скрипт очистки никогда не выполняется. Я нашел способ запуска кода с использованием функции ready
, встроенной в Django, но он не работает должным образом (он останавливает работу сервера до завершения всего процесса очистки, что не является оптимальным, так как я хочу, чтобы запуск выполнялся фон). Когда я попытался использовать отдельную службу для запуска сценария, даже использование depends_on
не работало, служба все еще работала, как только веб-служба работала, а затем отказывала из-за «Файл не найден». Как правильно поступить?
Код:
docker-compose.yml
version: '3.6'
services:
db:
image: postgres:10.1-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
web:
build: .
image: teonite_scraper
command: bash -c "python /teonite_webscraper/teonite_webscraper/manage.py migrate && python /teonite_webscraper/teonite_webscraper/manage.py runserver 0.0.0.0:8080 && python /teonite_webscraper/teonite_webscraper/scraper/scrape.py"
volumes:
- .:/teonite_webscraper
ports:
- 8080:8080
environment:
- SECRET_KEY=changemeinprod
depends_on:
- db
volumes:
postgres_data:
scrape.py
(скребковый скрипт):
import requests
from bs4 import BeautifulSoup
from teonite_webscraper.scraper.helpers import get_links
from teonite_webscraper.scraper.models import Article, Author
import json
import re
print('Starting...')
# For implementation check helpers.py, grabs all the article links from blog
links = get_links('https://teonite.com/blog/')
# List of objects to batch inject into DB to save I/Os
objects_to_inject = []
links_in_db = list(Article.objects.all().values_list('article_link', flat=True))
authors_in_db = list(Author.objects.all().values_list('author_stub', flat=True))
for link in links:
if not link in links_in_db:
# Grab article page
blog_post = requests.get(link)
# Prepare soup
soup = BeautifulSoup(blog_post.content, 'lxml')
# Gets the json with author data from page meta
json_element = json.loads(soup.find_all('script')[1].get_text())
# All of the below can be done within Articles() as parameters, but for clarity
# I prefer separate lines, and DB models cannot be accessed outside
# ready() at this stage anyway so refactoring to separate function wouldn't be possible
post_data = Article()
post_data.article_link = link
post_data.article_content = soup.find('section', class_='post-content').get_text()
# Regex only grabs the last part of author's URL that contains the "nickname"
author_stub = re.search(r'\/(\w+\-?_?\.?\w+)\/$', json_element['author']['url']).group(1)
# Check if author is already in DB if so assign the key.
if author_stub in authors_in_db:
post_data.article_author = Author.objects.get(author_stub=author_stub)
else:
# If not, create new DB Authors item and then assign.
new_author = Author(author_fullname=json_element['author']['name'],
author_stub=author_stub)
new_author.save()
# Unlike links which are unique, author might appear many times and we only grab
# them from DB once at the beginning, so adding it here to the checklist to avoid trying to
# add same author multiple times
authors_in_db.append(author_stub)
post_data.article_author = new_author
post_data.article_title = json_element['headline']
# Append object to the list and continue
objects_to_inject.append(post_data)
Article.objects.bulk_create(objects_to_inject)
print('Done collecting data!')