爬虫程序陷入死循环无法终止,请求排查解决
亚马逊土耳其站爬虫死循环问题排查与修复
问题概述
基于Python开发的亚马逊土耳其站爬虫,使用requests、BeautifulSoup和multiprocessing.Pool实现批量商品数据爬取,设计逻辑为每次批量处理12页数据(如1-12页、13-24页依次推进),但主进程main函数中的while循环始终无法终止,陷入死循环。
核心原因
- 主进程无终止触发逻辑:传入
main函数的loop变量始终为True,没有任何逻辑修改该值,导致while循环无限执行。 - 子进程终止不影响主进程:
urlAmazon函数中,检测到页面无分页容器时调用exit(),仅终止当前子进程,无法将“无更多页面”的信号传递给主进程,主进程仍会继续创建新的爬取任务。 - 额外潜在问题:多进程直接写入同一个文件
products.txt存在竞态条件,可能导致内容错乱;exit()终止子进程的方式不够优雅,易引发资源泄漏。
修复方案
1. 子进程返回状态,主进程控制循环终止
将urlAmazon中的exit()替换为返回布尔值,标记当前页面是否存在分页容器(即是否还有更多页面)。主进程收集所有子进程的返回结果,若存在任意一个子进程返回“无更多页面”,则设置loop为False终止循环。
2. 处理多进程异步任务结果
使用apply_async的get()方法获取子进程的返回值,判断是否需要终止主循环。
3. 解决多进程文件写入竞态问题
使用multiprocessing.Lock确保同一时间只有一个进程写入文件,避免内容错乱。
修改后完整代码
from selenium import webdriver import requests from bs4 import BeautifulSoup import time from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from multiprocessing.pool import Pool from multiprocessing import Lock # 初始化文件锁,解决多进程写文件竞态问题 file_lock = Lock() def urlAmazon(url=str(), paging_method=int()): headers = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"} url = url.format(paging_method) req = requests.get(url, headers=headers) soup = BeautifulSoup(req.content, "lxml") # 捕获页面结构变化导致的异常 try: productList = soup.find("div","s-main-slot s-result-list s-search-results sg-row").findAll("div","sg-col-4-of-24 sg-col-4-of-12 s-result-item s-asin sg-col-4-of-16 sg-col s-widget-spacing-small sg-col-4-of-20") except AttributeError: productList = [] # 检查是否存在分页容器,返回布尔值给主进程 has_pagination = bool(soup.find("div","a-section a-text-center s-pagination-container")) print(paging_method, len(productList)) for product in productList: try: productName = product.find("h2").getText(strip=True) except AttributeError: productName = "Unknown Product" try: productPrice = product.find("span","a-offscreen").getText(strip=True) except: productPrice = None # 使用文件锁确保安全写入 with file_lock: with open("products.txt","a",encoding="UTF-8") as f: f.write(f"{productName}: {productPrice} - {paging_method}\n") return has_pagination def main(pool): page = 1 url = "https://www.amazon.com.tr/s?i=electronics&bbn=13709880031&rh=n%3A13709880031%2Cp_n_fulfilled_by_amazon%3A21345978031&dc&page={}&rnid=21345970031&ref=sr_pg_2" loop = True while loop: results = [] # 提交12页的爬取任务 for i in range(11,-1,-1): p = pool.apply_async(func=urlAmazon, args=[url, 12*page - i]) results.append(p) # 等待所有子进程完成,获取返回结果 has_more_pages = all([res.get() for res in results]) # 若有任意一个页面无分页,则终止循环 if not has_more_pages: loop = False page += 1 print("End of the code") if __name__ == '__main__': pool = Pool() start = time.perf_counter() main(pool) # 关闭进程池,等待所有子进程完成 pool.close() pool.join() finish = time.perf_counter() print(f"Total time: {round(finish-start,2)} seconds")
关键修改点说明
- 子进程状态返回:
urlAmazon不再调用exit(),而是返回has_pagination布尔值,告知主进程当前页面是否存在分页。 - 主进程循环控制:主进程等待当前批次所有子进程完成,若任意子进程返回
False(无分页),则设置loop=False终止循环。 - 文件写入安全:添加
multiprocessing.Lock,确保多进程下文件写入的原子性,避免内容错乱。 - 进程池资源清理:主进程结束前调用
pool.close()和pool.join(),确保所有子进程完成后再退出,避免资源泄漏。
内容的提问来源于stack exchange,提问作者thisisoptimumprimetho
相关产品推荐
相关产品推荐

