使用Scrapy爬取IMDB Top250时提取用户投票数的问题
解决Scrapy爬取IMDB Top250电影时提取用户投票数的问题
问题描述
我用Scrapy爬取IMDB Top250电影页面,目标是提取用户投票信息。对应的HTML文本节点结构如下:
" ("
"3.8M"
")"
用CSS选择器response.css('.ipc-rating-star--voteCount::text').getall()提取后,得到的是包含左括号、投票值、右括号的扁平列表,但我只想提取纯数值部分(比如3.8M),现有代码的提取方法未生效,结果不符合预期。完整代码如下:
movie_data = [] class IMDBSpider(scrapy.Spider): name = 'imdb_spider' def start_requests(self): headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'} yield scrapy.Request(url, headers=headers, callback=self.parse) def parse(self, response): # movie name movie_name = response.css('ul.ipc-metadata-list h3.ipc-title__text::text').getall() # movie year movie_year = response.css('.sc-c7e5f54-8.hgjcbi.cli-title-metadata-item:first-child::text').getall() # movie ratings movie_rating = response.css('ul.ipc-metadata-list span.ipc-rating-star--base::text').getall() # user votings user_vote = response.css('.ipc-rating-star--voteCount::text').getall() print(response.css('.ipc-rating-star--voteCount::text').getall()) for name, year, rating, votes in zip(movie_name, movie_year, movie_rating, user_vote): self.log(f'Processing: {name}, {year}, {rating}, {votes}') relevant_elements = self.extract_combined_str(votes) self.log(f'Relevant Elements: {relevant_elements}') movie_dict = { 'movie_name': self.extract_name(name), # Call the extract_name method using self 'movie_year': year.strip(), 'movie_rating': rating.strip(), 'user_votes': [vote for vote in votes] } movie_data.append(movie_dict) delay = random.uniform(2, 5) self.log(f'Delaying for {delay} seconds.') time.sleep(delay) def extract_name(self, name): name = name.strip() name = re.sub(r'^\d+\.\s*', '', name) return name def extract_combined_str(self, votes): # Filtra solo los elementos que contienen dígitos y paréntesis numeric_value = re.search(r'\(([\d.]+[MK]?)\)', votes) return numeric_value.group(1) if numeric_value else None
问题根源
- 数据配对错误:
user_vote是一个扁平列表,每部电影的投票信息被拆成3个独立元素(左括号、数值、右括号),但直接用zip将其与长度为250的电影名称等列表配对,导致每个votes仅拿到单个字符/元素,而非完整的投票信息组。 - 正则匹配无效:
extract_combined_str方法试图从单个字符串中匹配括号内容,但传入的参数并非完整的带括号字符串,自然无法匹配到结果。
修正方案
步骤1:分组投票信息
将user_vote按每3个元素为一组拆分,每组对应一部电影的投票相关文本。
步骤2:提取纯数值
每组的第二个元素就是目标数值(比如3.8M),直接提取即可;若需兼容结构变化,也可通过正则从拼接后的完整字符串中提取。
修正后的完整代码
import scrapy import random import time import re movie_data = [] class IMDBSpider(scrapy.Spider): name = 'imdb_spider' start_urls = ['https://www.imdb.com/chart/top'] def start_requests(self): headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'} for url in self.start_urls: yield scrapy.Request(url, headers=headers, callback=self.parse) def parse(self, response): # 提取电影名称 movie_name = response.css('ul.ipc-metadata-list h3.ipc-title__text::text').getall() # 提取上映年份 movie_year = response.css('.sc-c7e5f54-8.hgjcbi.cli-title-metadata-item:first-child::text').getall() # 提取评分 movie_rating = response.css('ul.ipc-metadata-list span.ipc-rating-star--base::text').getall() # 处理投票信息:分组后提取数值 user_vote_raw = response.css('.ipc-rating-star--voteCount::text').getall() # 每3个元素为一组,对应一部电影的投票信息 user_vote_groups = [user_vote_raw[i:i+3] for i in range(0, len(user_vote_raw), 3)] # 直接取每组的第二个元素作为纯投票数值 user_votes = [group[1].strip() for group in user_vote_groups] for name, year, rating, votes in zip(movie_name, movie_year, movie_rating, user_votes): self.log(f'Processing: {name}, {year}, {rating}, {votes}') movie_dict = { 'movie_name': self.extract_name(name), 'movie_year': year.strip(), 'movie_rating': rating.strip(), 'user_votes': votes } movie_data.append(movie_dict) delay = random.uniform(2, 5) self.log(f'Delaying for {delay} seconds.') time.sleep(delay) def extract_name(self, name): name = name.strip() name = re.sub(r'^\d+\.\s*', '', name) return name
兼容结构变化的备选方案
若担心IMDB页面结构调整,可改用正则从拼接后的完整字符串中提取数值,替换上述代码中的user_votes生成逻辑:
user_votes = [] for group in user_vote_groups: full_vote_str = ''.join(group) match = re.search(r'\(([\d.]+[MK]?)\)', full_vote_str) user_votes.append(match.group(1) if match else None)
内容的提问来源于stack exchange,提问作者alexquilis1
相关产品推荐
相关产品推荐

