Files

160 lines
6.4 KiB
Python

import random
from urllib.parse import urlparse
import scrapy
from utils.config import config_manager
from db.redis_database import REDIS_SYNC_CLIENT
from utils.const import CATALOG_DATA
from utils.runtime_const import SPORTS_ARTIFACTS
class SportVideoSpider(scrapy.Spider):
name = "sport_video"
categories = config_manager.get_scraper_config(name, "categories")
custom_settings = {
"ITEM_PIPELINES": {
"mediafusion_scrapy.pipelines.TorrentDownloadAndParsePipeline": 100,
"mediafusion_scrapy.pipelines.SportVideoParserPipeline": 200,
"mediafusion_scrapy.pipelines.MovieStorePipeline": 300,
"mediafusion_scrapy.pipelines.RedisCacheURLPipeline": 400,
},
}
def __init__(self, scrape_all: str = "false", *args, **kwargs):
super(SportVideoSpider, self).__init__(*args, **kwargs)
self.scrape_all = scrape_all.lower() == "true"
self.redis = REDIS_SYNC_CLIENT
self.scraped_urls_key = "sport_video_scraped_urls"
def __del__(self):
self.redis.close()
def start_requests(self):
for category, url in self.categories.items():
yield scrapy.Request(url, self.parse, meta={"category": category})
def parse(self, response, **kwargs):
category = response.meta["category"]
# Parse the current page URL to extract the base path for comparison
current_category_base = urlparse(response.url).path.rsplit(".", 1)[0]
if self.scrape_all:
# Navigate through the pagination
page_links = set(
response.css("div[id^='wb_Pagination'] ul li a::attr(href)").getall()
)
for link in page_links:
# Ensure the link is not just a 'Next' or 'Prev' button
if current_category_base in link:
yield response.follow(
link, self.parse_page, meta={"category": category}
)
# Only scrape the first page
yield from self.parse_page(response)
def parse_page(self, response):
category = response.meta["category"]
catalog_mapped = CATALOG_DATA.get(category)
generic_posters = SPORTS_ARTIFACTS[catalog_mapped]["poster"]
generic_backgrounds = SPORTS_ARTIFACTS[catalog_mapped]["background"]
# Generalized selector for all content blocks
content_blocks = response.css('div[id^="wb_LayoutGrid"]')
for content in content_blocks:
# Extract only the first part of the title
title_words = content.css('div[id^="wb_Text"] strong::text').getall()
title = "".join(title_words).replace("(NEW)", "").strip()
# Extract poster URL
poster_path = content.css('div[id^="wb_PhotoGallery"] img::attr(src)').get()
if poster_path:
poster = response.urljoin(poster_path)
background = poster
else:
poster = random.choice(generic_posters)
background = random.choice(generic_backgrounds)
torrent_data = {
"title": title.strip(),
"poster": poster,
"background": background,
"parsed_data": {"title": title.strip()},
"source": "sport-video.org.ua",
"is_add_title_to_poster": True,
"catalog": category,
"type": "movie",
"scraped_url_key": self.scraped_urls_key,
}
# Extract torrent page link
torrent_page_link = content.css('div[id^="wb_Shape"] a::attr(href)').get()
torrent_link = content.css('a[href$=".torrent"]::attr(href)').get()
if title and torrent_page_link:
torrent_page_link = response.urljoin(torrent_page_link)
# Check if URL has been scraped before
if self.redis.sismember(self.scraped_urls_key, torrent_page_link):
self.logger.info(
f"Skipping already scraped URL: {torrent_page_link}"
)
continue
torrent_data["webpage_url"] = torrent_page_link
yield response.follow(
torrent_page_link,
self.parse_torrent_page,
meta={"item": torrent_data},
)
elif title and torrent_link:
# Check if URL has been scraped before
if self.redis.sismember(self.scraped_urls_key, torrent_link):
self.logger.info(f"Skipping already scraped URL: {torrent_link}")
continue
torrent_data["torrent_link"] = response.urljoin(torrent_link)
yield torrent_data
def parse_torrent_page(self, response):
# Retrieve passed item data
base_item = response.meta["item"]
# Initialize variables to hold metadata and the current metadata block
metadata_blocks = []
current_block = {}
table_rows = response.css("table tr")
for row in table_rows:
header = (
row.css("td.cell0 strong::text")
.extract_first(default="")
.strip()
.lower()
.replace(" ", "_")
)
value = " ".join(row.css("td.cell0:nth-child(2) *::text").extract()).strip()
# When encountering a 'description', it means a new torrent metadata block starts
if header == "description" and current_block:
# Save the current block and start a new one
metadata_blocks.append(current_block)
current_block = {}
# Add the metadata to the current block
current_block[header] = value
# add the last block
if current_block:
metadata_blocks.append(current_block)
# Extract all torrent links
torrent_links = response.css('a[href$=".torrent"]::attr(href)').getall()
# Correctly match metadata blocks to torrent links
for i, link in enumerate(torrent_links):
item = base_item.copy() # Create a new item for each torrent link
# If there's corresponding metadata, add it to the item
if i < len(metadata_blocks):
item.update(metadata_blocks[i])
item["torrent_link"] = response.urljoin(link)
yield item