#python #python-3.x
Вопрос:
Я работал над приложением, связанным с вводом-выводом, в котором я буду запускать несколько сценариев одновременно, в зависимости от того, какие аргументы я буду вызывать для сценария и т. Д.: monitor.py —s=»сидсвенскан», monitor.py -ss=»Би-би-си» и т. Д. и т. Д.
from __future__ import annotations
from abc import abstractmethod
from typing import ClassVar, Dict
from typing import Optional
import attr
import requests
from selectolax.parser import HTMLParser
@attr.dataclass
class Info:
"""Scraped info about news"""
all_articles: set = attr.ib(factory=set)
store: str = attr.ib(factory=str)
name: Optional[str] = attr.ib(factory=str)
image: Optional[str] = attr.ib(factory=str)
class Scraper:
scrapers: ClassVar[Dict[str, Scraper]] = {}
domain: ClassVar[str]
def __init_subclass__(cls) -> None:
Scraper.scrapers[cls.domain] = cls
@classmethod
def for_url(cls, domain, url) -> Scraper:
return cls.scrapers[domain](url)
@abstractmethod
def scrape_feed(self):
pass
@abstractmethod
def scrape_product(self):
pass
class BBCScraper(Scraper):
domain = 'BBC'
def __init__(self, url):
self.url = url
def scrape_feed(self):
with requests.get(self.url) as rep:
# FIXME Better way than this atleast :P
if rep:
doc = HTMLParser(rep.text)
all_articles = {
f"https://www.BBC.se{product_link.attrs['href']}" for product_link in
doc.css('td.search-productnamne > a, div.product-image > a')
}
return Info(
store="BBC",
all_articles=all_articles
)
def scrape_product(self):
with requests.get(self.url) as rep:
# FIXME Better way than this atleast :P
if rep:
doc = HTMLParser(rep.text)
# FIXME Scrape valid webelements
name = "Test"
image = "Test"
return Info(
store="BBC",
name=name,
image=image,
)
class SydsvenskanScraper(Scraper):
domain = 'Sydsvenskan'
def __init__(self, url):
self.url = url
def scrape_feed(self):
with requests.get(self.url) as rep:
# FIXME Better way than this atleast :P
if rep:
doc = HTMLParser(rep.text)
all_articles = {
f"https://Sydsvenskan.se/{product_link.attrs['href']}" for product_link in
doc.css('div.product-image > a, td.search-productnamne > a')
}
return Info(
store="Sydsvenskan",
all_articles=all_articles
)
def scrape_product(self):
with requests.get(self.url) as rep:
# FIXME Better way than this atleast :P
if rep:
doc = HTMLParser(rep.text)
# FIXME Scrape valid webelements
name = "Test"
image = "Test"
return Info(
store="Sydsvenskan",
name=name,
image=image,
)
if __name__ == "__main__":
#FIXME Use arguments instead
domain = 'BBC'
url = 'https://www.bbc.co.uk/'
scraper = Scraper.for_url(domain, url)
r = scraper.scrape_feed()
print(r)
Как вы можете видеть в настоящее время, я «жестко закодирован»:
domain = 'BBC'
url = 'https://www.bbc.co.uk/'
который в настоящее время будет передаваться вместо аргументов.
Однако, как мы видим, если я начну добавлять больше «магазинов/новостных сайтов», class Scraper
например, на 40 разных сайтах, будет довольно сложно перейти к правильному коду, если вы хотите сохранить или внести какие-либо изменения.
Интересно, как я могу в таком случае разделить код на разные файлы, где etc Sydsvenska будет для себя, а BBC будет сама по себе. Тогда мне будет легче поддерживать код в будущем, если будут какие-либо изменения.
Комментарии:
1. Я просто кое-что здесь проверяю. Вы, кажется, хорошо разбираетесь в python. У вас есть подсказки типа с абстрактными классами и все такое. Но ваш вопрос выглядит так, как будто вы не знаете, как вырезать и вставить код в другой файл, а затем импортировать этот файл в сценарий. Так ли это на самом деле? Если нет, то не могли бы вы уточнить, что вы ищете?
2. Я бы сказал, действительно. Я хорошо знаю python, но также получил отличные учебные пособия, которым нужно следовать, чтобы понять больше. @Mythalorian, Моя настоящая проблема в том, что я не знаю, как я могу разделить его «по-хорошему». Это означает, что вместо того, чтобы все царапины в одном файле скрипта были разделены. Единственное, что я могу придумать, это разрезать код на несколько файлов, а затем импортировать его в
class Scraper
, но нужно ли очищать импорт 40 разных сайтов, если мы собираемся использовать только один, основываясь на вводимых аргументах? (Я также пытаюсь избежать динамического импорта, если это сценарий)
Ответ №1:
Хорошо, я понимаю, что вы ищете. И, к сожалению, должен сказать, что вам не повезло. По крайней мере, насколько я знаю python. Вы можете сделать это двумя способами.
- Используйте importlib для поиска в папке/пакете, содержащем эти файлы, и импорта их в список или в список, который необходимо извлечь. Однако вы сказали, что хотите избежать этого, но в любом случае вам придется использовать importlib. И № 2-это причина, по которой.
- Используйте базовый класс, который при наследовании
__init__
своего вызова добавляет производный класс в список или объект, в котором он хранится, и вы можете получить его с помощью объекта класса. Однако проблема здесь в том, что если вы переместите свой производный класс в новый файл, этот код не будет выполняться до тех пор, пока вы его не импортируете. Таким образом, вам все равно потребуется явно импортировать файл или неявно импортировать его через importlib (динамический импорт).
Таким образом, вам придется использовать importlib (динамический импорт) в любом случае.
Комментарии:
1. О, я понимаю, так что в обоих случаях в любом случае можно использовать importlib, если я хочу импортировать только необходимые новости, которые я хочу отслеживать, если я прав. Имеет ли значение/смысл импортировать все новостные магазины (даже если их 40) или лучше использовать dynamic только для импорта необходимого?
2. Если бы это был сервер или непрерывно работающий процесс, то да, я бы предложил импортировать все 40. Но, похоже, ваш сценарий-одноразовый. В этом случае я бы просто динамически импортировал только то, что вам нужно. Если каждый раз, когда вы запускаете свой скрипт, он импортирует 40 модулей, но вы используете только один, то это бессмысленно и замедлит время запуска.
3. Но замедлится ли только запуск, или все приложение тоже замедлится, если вы запустите его 24/7? Так как это приложение для веб-крапинга/обхода веб-страниц. Он будет работать 24/7. Если бы это было хорошей информацией для добавления 🙂 Но да, мой сценарий будет одноразовым, так как я буду использовать аргументы для запуска конкретного магазина новостей. Но опять же, если это только в начале, это медленно, тогда я был бы в порядке, чтобы реализовать весь импорт тогда?
4. Извините, позвольте мне уточнить. Я должен удалить непрерывно работающий процесс. На самом деле вам следует импортировать все 40, если это был сервер, на который вы отправляете запрос, который может быть для любого из 40. И это только замедлит запуск, потому что, как только вы выполните динамический импорт, вам не нужно будет делать это снова, если программа не завершится. Просто не забудьте сохранить ссылку на модуль, который вы динамически импортировали, и используйте ее для совершения звонков.
5. Я думаю, что хорошее начало было бы здесь. Я использовал importlib для чего-то похожего на то, о чем вы говорите, но это было несколько лет назад. Но импорт, чтобы помнить, использует импорт в стиле имени пакета, а не на основе папок. docs.python.org/3/library/…