Kinopoisk按Genre获取随机电影报错AttributeError求助
问题:指定类型筛选随机电影时触发AttributeError
开发一款可推送随机电影的Telegram Bot(当前控制台测试,后续重构),全随机电影功能正常,但按指定类型筛选时出现异常,报错信息如下:
错误栈
C:\Users\Iwlj4s\Desktop\pm\telegramMovieBot\.venv\Scripts\python.exe C:\Users\Iwlj4s\Desktop\pm\telegramMovieBot\getMovies\main_pars.py Genre: Anime User Input: Anime Genre Key: genre_1750 Traceback (most recent call last): File "C:\Users\Iwlj4s\Desktop\pm\telegramMovieBot\getMovies\main_pars.py", line 130, in <module> movie_data.get_full_rnd_movie_data(True) File "C:\Users\Iwlj4s\Desktop\pm\telegramMovieBot\getMovies\main_pars.py", line 111, in get_full_rnd_movie_data self.poster_img_href = self.poster_div.find("img")["src"] ^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'find'
相关代码(main_pars.py)
import requests from selenium import webdriver from bs4 import BeautifulSoup from selenium.webdriver.common.by import By user_input = input("Genre: ") class ParsSettings: def __init__(self): self.url = "https://www.kinopoisk.ru/chance/" self.url_for_img = "https://www.kinopoisk.ru/" self.response = requests.get(self.url) self.driver = webdriver.Chrome() self.user_genre_key = None self.genres = { 'genre_1750 ': 'Anime', 'genre_22': 'biography', 'genre_3': 'action movie', 'genre_13': 'Western', 'genre_19': 'Military', 'genre_17': 'Detective', 'genre_456': 'Kids', 'genre_12': 'documentary', 'genre_8': 'Drama', 'genre_23': 'History', 'genre_6': 'Comedy', 'genre_15': 'short film', 'genre_16': 'Crime', 'genre_7': 'Melodrama', 'genre_21': 'Music', 'genre_14': 'Cartoon', 'genre_9 ': 'Musical', 'genre_10': 'Adventure', 'genre_11': 'Family', 'genre_24': 'Sport', 'genre_4': 'Thriller', 'genre_1': 'Horror', 'genre_2': 'Fantastic', 'genre_18': 'Film-Noir', 'genre_5': 'Fantasy' } class GetRandomMovieData(ParsSettings): def __init__(self): super().__init__() self.poster_div = None self.poster_img_href = None self.poster_img = None self.film_name_div = None self.film_name = None self.film_genre_div = None self.film_genre = None self.film_rating_div = None self.film_rating_imb = None self.film_desc_div = None self.film_desc = None self.driver.get(self.url) self.list = self.driver.find_element(By.ID, "genreListTitle") self.button = self.driver.find_element(By.CLASS_NAME, "button") self.new_html = None self.soup = None def find_user_genre(self, genre): """ dict genre: keys == class <li> in HTML || values == genres :return: genre key by user input genre, like: Anime: genre_1750 """ for key in self.genres: if self.genres[key] == genre: self.user_genre_key = key print("User Input:", user_input) print("Genre Key: ", self.user_genre_key) return self.user_genre_key print("No Same Gener") return None def get_rnd_movie(self): self.button.click() self.new_html = self.driver.page_source self.soup = BeautifulSoup(self.new_html, "lxml") self.driver.quit() return self.soup def get_rnd_gener_movie(self): self.find_user_genre(user_input) self.list.click() self.checkbox = self.driver.find_element(By.CLASS_NAME, self.user_genre_key) if self.checkbox: self.user_genre_key = self.user_genre_key self.checkbox.click() self.button.click() self.new_html = self.driver.page_source self.soup = BeautifulSoup(self.new_html, "lxml") self.driver.quit() return self.soup else: print("No Same Genre") return None def get_full_rnd_movie_data(self, genre=False): self.soup = None if not genre: self.soup = self.get_rnd_movie() elif genre: self.soup = self.get_rnd_gener_movie() self.poster_div = self.soup.find("div", class_="poster") self.poster_img_href = self.poster_div.find("img")["src"] self.poster_img = self.url_for_img + self.poster_img_href self.film_name_div = self.soup.find("div", class_="filmName") self.film_name = self.film_name_div.find("a").text self.film_genre_div = self.soup.find("div", class_="gray") self.film_genre = self.film_genre_div.text.strip() self.film_rating_div = self.soup.find("div", class_="rating") self.film_rating_imb = self.film_rating_div.text.strip() self.film_desc_div = self.soup.find("div", class_="syn") self.film_desc = self.film_desc_div.text.strip() movie_data = GetRandomMovieData() # For now just switch False - for get full random movie and True - for genre random movie movie_data.get_full_rnd_movie_data(True) print("Movie Name:", movie_data.film_name) print("Genre:", movie_data.film_genre) print("Rating:", movie_data.film_rating_imb) print("Description:", movie_data.film_desc)
类型选择区域HTML片段
<div class="left_select"> <div onclick="KP.filter.openSelect('genreList');" id="genreListTitle" class="selectCustom">выберите жанры</div> <ul onmouseout="KP.filter.selectBlockClose=false;" onmouseover="KP.filter.selectBlockClose=true;" class="genreList selectList" id="genreList" style="display: block;"> <li class="genre_1750 selectItem"><label><input type="checkbox" onchange="KP.filter.toggleCheckbox('genre', this);" data-name="аниме" value="1750" name="m_act[genre][]"> аниме</label></li> <li class="genre_22 selectItem"><label><input type="checkbox" onchange="KP.filter.toggleCheckbox('genre', this);" data-name="биография" value="22" name="m_act[genre][]"> биография</label></li> <li class="genre_3 selectItem"><label><input type="checkbox" onchange="KP.filter.toggleCheckbox('genre', this);" data-name="боевик" value="3" name="m_act[genre][]"> боевик</label></li> <li class="genre_13 selectItem"><label><input type="checkbox" onchange="KP.filter.toggleCheckbox('genre', this);" data-name="вестерн" value="13" name="m_act[genre][]"> вестерн</label></li>
问题原因
- 类型键存在多余空格:
genres字典中'Anime'对应的键是'genre_1750 '(末尾带空格),但HTML中li的class是genre_1750(无空格),导致Selenium无法找到目标元素,后续未触发正确的筛选,页面没有加载出指定类型的电影内容,BeautifulSoup解析后找不到poster等元素,触发NoneType错误。 - 缺少页面等待机制:点击类型列表、复选框后,页面可能未完全加载就获取源码,导致soup内容不完整,无法提取电影信息。
- 复选框定位错误:直接点击li元素而非内部的input复选框,可能未触发实际的筛选状态变更。
- 逻辑冗余与异常缺失:代码中存在无意义的赋值语句,且未对
find方法的返回值做None判断,一旦元素找不到就直接崩溃。
修复方案
1. 修正类型字典的键
移除genres字典中键的多余空格,确保与HTML中的class完全匹配:
self.genres = { 'genre_1750': 'Anime', 'genre_22': 'biography', 'genre_3': 'action movie', 'genre_13': 'Western', 'genre_19': 'Military', 'genre_17': 'Detective', 'genre_456': 'Kids', 'genre_12': 'documentary', 'genre_8': 'Drama', 'genre_23': 'History', 'genre_6': 'Comedy', 'genre_15': 'short film', 'genre_16': 'Crime', 'genre_7': 'Melodrama', 'genre_21': 'Music', 'genre_14': 'Cartoon', 'genre_9': 'Musical', 'genre_10': 'Adventure', 'genre_11': 'Family', 'genre_24': 'Sport', 'genre_4': 'Thriller', 'genre_1': 'Horror', 'genre_2': 'Fantastic', 'genre_18': 'Film-Noir', 'genre_5': 'Fantasy' }
2. 添加显式等待
引入WebDriverWait和expected_conditions,确保元素加载完成后再操作:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC
3. 修正复选框定位与操作逻辑
在get_rnd_gener_movie中,先等待类型列表展开,再定位li下的input复选框并点击:
def get_rnd_gener_movie(self): genre_key = self.find_user_genre(user_input) if not genre_key: print("No Same Genre") return None self.list.click() # 等待类型列表展开 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.ID, "genreList")) ) # 定位li元素,再找内部的复选框 li_element = self.driver.find_element(By.CLASS_NAME, genre_key) checkbox = li_element.find_element(By.TAG_NAME, "input") checkbox.click() self.button.click() # 等待电影内容加载完成 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CLASS_NAME, "poster")) ) self.new_html = self.driver.page_source self.soup = BeautifulSoup(self.new_html, "lxml") self.driver.quit() return self.soup
4. 增加异常判断,避免NoneType崩溃
在get_full_rnd_movie_data中,对每个find结果做None判断:
def get_full_rnd_movie_data(self, genre=False): self.soup = None if not genre: self.soup = self.get_rnd_movie() elif genre: self.soup = self.get_rnd_gener_movie() if not self.soup: print("Failed to get page source") return self.poster_div = self.soup.find("div", class_="poster") if self.poster_div: img_tag = self.poster_div.find("img") if img_tag and "src" in img_tag.attrs: self.poster_img_href = img_tag["src"] self.poster_img = self.url_for_img + self.poster_img_href self.film_name_div = self.soup.find("div", class_="filmName") if self.film_name_div: name_tag = self.film_name_div.find("a") if name_tag: self.film_name = name_tag.text self.film_genre_div = self.soup.find("div", class_="gray") if self.film_genre_div: self.film_genre = self.film_genre_div.text.strip() self.film_rating_div = self.soup.find("div", class_="rating") if self.film_rating_div: self.film_rating_imb = self.film_rating_div.text.strip() self.film_desc_div = self.soup.find("div", class_="syn") if self.film_desc_div: self.film_desc = self.film_desc_div.text.strip()
内容的提问来源于stack exchange,提问作者silent_nn
相关产品推荐
相关产品推荐

