Python网页爬虫无限循环问题排查(附代码)
爬虫无限循环问题排查与修复(针对books.toscrape.com)
核心问题分析
你的爬虫出现无限循环、重复爬取的原因主要有三点:
1. 分类index_number逻辑完全错误
网站每个分类的URL后缀是固定的(比如Travel分类是travel_2,Mystery是mystery_3),index_number是分类的序号,从2开始依次对应每个分类。但你的代码在每个分类循环里都把index_number初始化为2,还试图在while的else块递增——而while循环只有遇到break才会退出,else块永远不会执行,导致所有分类都用index_number=2(即Travel分类的URL)爬取,重复爬同一个分类的内容。
2. 分页请求失败时的错误处理
当请求分页URL(比如page-2.html)返回非200时,你会重新请求该分类的基础页面(index.html),而基础页面必然存在书籍数据,导致if not books:永远触发不了break,page_number持续递增,每次都重复爬取该分类的第一页,形成无限循环。
3. 分页判断逻辑缺失
没有判断当前分类是否存在下一页,只是盲目递增page_number,导致无效请求后触发重复爬取。
修复方案与代码
关键修改点
- 给每个分类匹配正确的index_number:根据分类在列表中的顺序,从2开始依次赋值
- 修正分页逻辑:请求分页URL失败时直接退出循环,不再回退到基础页面
- 先爬取分类基础页面,再判断是否存在分页按钮,决定是否继续爬取后续页面
修复后的完整代码
from bs4 import BeautifulSoup import requests home_url = "http://books.toscrape.com/index.html" base_url = "http://books.toscrape.com/catalogue/category/books/{}_{}/index.html" page_url_format = "http://books.toscrape.com/catalogue/category/books/{}_{}/page-{}.html" response = requests.get(home_url) response.encoding = response.apparent_encoding soup = BeautifulSoup(response.text, "lxml") ul_element = soup.find("ul", class_="nav nav-list") genres = ul_element.find_all("a") list_genres = [] main_genres = [] for genre in genres: text = genre.text.strip() list_genres.append(text) del list_genres[0] # 移除第一个"Books"全部分类 # 给每个分类匹配正确的index_number(从2开始) genre_index_map = {} for idx, genre_name in enumerate(list_genres): formatted_genre = genre_name.replace(" ", "-").lower() genre_index_map[formatted_genre] = idx + 2 # index从2开始对应第一个分类Travel with open("genre.txt", "w", encoding="utf-8") as file: for genre, index_number in genre_index_map.items(): page_number = 1 while True: # 先构建当前页码的URL if page_number == 1: current_url = base_url.format(genre, index_number) else: current_url = page_url_format.format(genre, index_number, page_number) response_page = requests.get(current_url) response_page.encoding = response_page.apparent_encoding # 请求失败直接退出循环,说明该页码不存在 if response_page.status_code != 200: break soup = BeautifulSoup(response_page.text, "lxml") books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3") # 没有书籍也退出 if not books: break # 解析书籍信息 for book in books: cost_element = book.find("p", class_="price_color") cost = cost_element.text.strip() h3_tag = book.find("h3") title_element = h3_tag.find("a") title = title_element["title"] stock_availability_element = book.find("p", class_="instock availability") stock_availability = stock_availability_element.text.strip() class_star = book.find("p", class_=True) p_class = class_star["class"] stars = next((star for star in ["One", "Two", "Three", "Four", "Five"] if star == p_class[1]), "Unknown") file.write(f"Title: {title}\n") file.write(f"Price: {cost}\n") file.write(f"Stock Availability: {stock_availability}\n") file.write(f"Stars: {stars}\n") file.write("\n") # 判断是否存在下一页 next_page = soup.find("li", class_="next") if not next_page: break page_number += 1 print("爬取完成")
修复说明
- genre_index_map:通过枚举分类列表的索引,给每个分类分配正确的index_number,确保每个分类使用对应的URL
- 分页判断:每次爬取后检查页面是否有
next按钮,没有则说明没有更多页面,直接退出循环 - 请求失败处理:请求分页URL返回非200时直接退出,避免重复爬取基础页面
- 优化解析逻辑:用
next()简化星级判断,避免不必要的循环;统一处理文本strip,减少冗余
内容的提问来源于stack exchange,提问作者Nouri_Dawalibi
相关产品推荐
相关产品推荐

