You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 14:45:10