Python多线程爬虫触发break后无法进入else分支问题求助
代码问题排查
1. 分页序号逻辑错位
你当前的page_no是遍历结果时手动累加的,和实际传入下载函数的页码没有绑定,一旦修改爬取起始页码,序号会完全错位,容易误判无数据的页码。
2. 多线程执行逻辑和终止需求不匹配
executor.map()被调用的瞬间,就会把你传入的range范围内所有页码的下载任务全部提交给线程池。不管后续有没有break终止遍历,所有请求早就已经发出去了。你的break只是停止处理已经返回的结果,既不会阻止后续请求发送,也不会继续处理剩余的返回结果,自然不会进入后续的else分支。
你的业务逻辑是「查到某页无数据就终止爬取后续页」,本身是串行依赖的,必须等前一页确认有数据,再决定要不要爬下一页,直接全量提交多线程任务本来就不符合这个逻辑。
3. break语句直接终止遍历
你在查到无数据的时候直接break跳出了遍历返回结果的for循环,后续的页面结果当然不会被处理,自然不会进入else分支。
修复方案
可以改成批量提交多线程任务的模式:每次提交少量页面的爬取任务,处理完当前批次确认都有数据,再提交下一批,一旦某页无数据,就不再提交新任务,既保留多线程的效率,也符合终止爬取的需求。
修复后的参考代码如下:
import concurrent import functools import concurrent.futures import requests from urllib3.exceptions import InsecureRequestWarning import csv from bs4 import BeautifulSoup as bs requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # 修改下载函数,同时返回页码和页面内容,避免序号错位 def download_page(session, page_no): url = 'https://bidplus.gem.gov.in/bidlists?d=1&page_no=' + str(page_no) print('URL created: ' + url) resp = session.get(url, verify=False) return page_no, resp.text def scrap_bid_data(): NUMBER_THREADS = 5 BATCH_SIZE = 10 # 每次爬10页,再判断要不要继续 start_page = 1 max_page = 50 stop_flag = False with open('GEMconcurrent_1004.csv', 'w', newline='') as out_file: f = csv.writer(out_file) f.writerow(['Bidnumber', 'Items', 'Quantity', 'Department', 'Enddate','pageNumber']) with requests.Session() as session: page_downloader = functools.partial(download_page, session) with concurrent.futures.ThreadPoolExecutor(max_workers=NUMBER_THREADS) as executor: while start_page <= max_page and not stop_flag: # 计算当前批次的页码范围 end_batch = min(start_page + BATCH_SIZE - 1, max_page) batch_pages = range(start_page, end_batch + 1) # 提交当前批次的任务 results = executor.map(page_downloader, batch_pages) # 处理结果 for page_no, page in results: soup_data = bs(page, 'lxml') extracted_data = soup_data.find('div', {'id': 'pagi_content'}) if extracted_data is None or len(extracted_data) == 0: print('No data at page number', page_no) stop_flag = True break # 跳出当前批次处理循环 else: for idx in range(len(extracted_data)): if idx % 2 == 1: bid_data = extracted_data.contents[idx].text.strip().split('\n') if len(bid_data) > 1 and len(bid_data[8]) > 1: bidno = bid_data[0].split(":")[-1] items = bid_data[8].split(":")[-1] qnty = int(bid_data[9].split(':')[1].strip()) dept = (bid_data[10] + bid_data[15].strip()).split(":")[-1] edate = bid_data[20].split("End Date:")[-1] f.writerow([bidno, items, qnty, dept, edate, page_no]) # 处理完一批,更新下一批的起始页码 start_page = end_batch + 1 scrap_bid_data()
内容的提问来源于stack exchange,提问作者user14220199
相关产品推荐
相关产品推荐

