diff --git a/gallery/painting/gismeteo/api.py b/gallery/painting/gismeteo/api.py index 713d263..9c0b444 100644 --- a/gallery/painting/gismeteo/api.py +++ b/gallery/painting/gismeteo/api.py @@ -2,10 +2,10 @@ import datetime import logging from typing import Any, Dict, List -import aiohttp from aiocache import cached from bs4 import BeautifulSoup +from gallery.sketch.source import ApiSource from gallery.sketch.weather.api import WeatherApi from gallery.sketch.weather.catalog import LocationId from gallery.sketch.weather.model import WeatherResponse, WeatherValue @@ -17,33 +17,23 @@ logger = logging.getLogger("gismeteo") class GismeteoApi(WeatherApi): - BASE_URL = "https://www.gismeteo.ru" + SOURCE = ApiSource( + "https://www.gismeteo.ru", + cookies={ + "cf_clearance": ( + "zPj8qeaYZWp5BUZsWIRD7kd32s59IDWWaVvD7GGf6b4-1724254262-1.2.1.1-" + "R1PaHoqlm4OEgHfrOg6oAJmVmFJ7053JI_QDjIFlCAmbNY0oyw1J4QtgmEkAkTWF4C99X5hpXmZB2en." + "9Ey3NL0kzaE10UGBHPli5OLxHNKC3wQ34xmxTxg6GcKouXOPRgqo92lVRpQP7ghvDmvvT_" + "S6YXDbRonkamptWhZiAsIhsnIQg2Mt7Lk1fxSEVbaey_RXVQBvaR2ofiFgIavcbQPRFSvJ." + "Ct_FujmymTAZWh1XHTptK32kur81NXyPuARThetOKQm8AklaMa855Oom6Bms1a4F9b0yEHSd2inA4j3FR7uauu8hhwO0Z2goa3." + "ktpXXAQLz6dm.fBEWIHD7wooda15X57tCcDOely4hxhO4LK2lIH6TbOr." + "rZQAiRRhltHiCizP1Ou2SiF_81lb1umoDh739tQD1cnw_USnL." + "JwTHvkB00ZnaHRMi4FCBrsKIE0NxJO6kiv1QANqumzf5.8_pSIGRo2cKeO8xCydduDNFbStdOmLR6FU7ZKqVu" + ) + }, + ) CACHE_TTL = 10 * 60 - USER_AGENT = ( - "Mozilla/5.0 (X11; Linux x86_64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/126.0.0.0 Safari/537.36" - ) - COOKIE = ( - "cf_clearance=U28mYVC0ENu88vorlL_CWmWOoevvXp0vb4xCqfqYC9s-" - "1722273367-1.0.1.1-" - "IDV73azTHY0V.NAnmEvok3zf5HHEkvF098pmya7IiqRRB5nk3FhbLCb0AeWm_kpTFqi1niFk2mYN_ramGTSl0A" - ) - - async def _request(self, endpoint: str) -> str: - url = f"{self.BASE_URL}/{endpoint}" - logger.info(url) - async with aiohttp.ClientSession( - headers={ - "User-Agent": self.USER_AGENT, - "Cookie": self.COOKIE, - }, - raise_for_status=True, - ) as session: - async with session.request("GET", url) as response: - return await response.text() - def _parse_oneday(self, date: datetime.date, data: str) -> WeatherResponse: result: List[Dict[str, Any]] = [] soup = BeautifulSoup(data, features="html.parser") @@ -88,10 +78,10 @@ class GismeteoApi(WeatherApi): @cached(ttl=CACHE_TTL) async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse: - data = await self._request(f"weather-{location_id}/{datehelp.dump(date)}") + data = await self.SOURCE.request(f"weather-{location_id}/{datehelp.dump(date)}") return self._parse_oneday(date, data) @cached(ttl=CACHE_TTL) async def get_days(self, location_id: str, days: int) -> WeatherResponse: - data = await self._request(f"weather-{location_id}/{days}-days") + data = await self.SOURCE.request(f"weather-{location_id}/{days}-days") return self._parse_manydays(data) diff --git a/gallery/painting/matchtv/api.py b/gallery/painting/matchtv/api.py index 2ae35c6..ab3b64b 100644 --- a/gallery/painting/matchtv/api.py +++ b/gallery/painting/matchtv/api.py @@ -1,39 +1,21 @@ import datetime import logging -import aiohttp from aiocache import cached from bs4 import BeautifulSoup from gallery.sketch.schedule.api import ScheduleApi from gallery.sketch.schedule.catalog import ChannelId from gallery.sketch.schedule.model import Channel, Schedule, ScheduleValue +from gallery.sketch.source import ApiSource logger = logging.getLogger("matchtv") class MatchTvApi(ScheduleApi): - BASE_URL = "https://matchtv.ru" + SOURCE = ApiSource("https://matchtv.ru") CACHE_TTL = 30 * 60 - USER_AGENT = ( - "Mozilla/5.0 (X11; Linux x86_64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/126.0.0.0 Safari/537.36" - ) - - async def _request(self, endpoint: str) -> str: - url = f"{self.BASE_URL}/{endpoint}" - logger.info(url) - async with aiohttp.ClientSession( - headers={ - "User-Agent": self.USER_AGENT, - }, - raise_for_status=True, - ) as session: - async with session.request("GET", url) as response: - return await response.text() - async def get_channels(self) -> list[str]: return [ ChannelId.MATCH_TV, @@ -50,7 +32,7 @@ class MatchTvApi(ScheduleApi): self, channel_id: str, date: datetime.date ) -> Schedule: endpoint = f"channel/{channel_id}/tvguide?date={date:%d-%m-%Y}" - data = await self._request(endpoint) + data = await self.SOURCE.request(endpoint) soup = BeautifulSoup(data, features="html.parser") values = [] channel_name = soup.select_one(".caption__heading").text.split("|")[0].strip() diff --git a/gallery/sketch/source.py b/gallery/sketch/source.py new file mode 100644 index 0000000..ccef5a0 --- /dev/null +++ b/gallery/sketch/source.py @@ -0,0 +1,40 @@ +import logging + +import aiohttp + +logger = logging.getLogger("source") + + +class ApiSource: + DEFAULT_USER_AGENT = ( + "Mozilla/5.0 (X11; Linux x86_64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/126.0.0.0 Safari/537.36" + ) + DEFAULT_TIMEOUT = 30.0 + + def __init__( + self, + base_url, + user_agent: str = DEFAULT_USER_AGENT, + timeout: float = DEFAULT_TIMEOUT, + cookies: dict[str, str] | None = None, + ): + self._base_url = base_url + self._user_agent = user_agent + self._timeout = timeout + self._cookies = cookies + + async def request(self, endpoint: str) -> str: + url = f"{self._base_url}/{endpoint}" + logger.info(url) + headers = { + "User-Agent": self._user_agent, + } + async with aiohttp.ClientSession( + headers=headers, + cookies=self._cookies, + raise_for_status=True, + ) as session: + async with session.request("GET", url, timeout=self._timeout) as response: + return await response.text()