使用Concurrent库配合BeautifulSoup提取新闻链接内容时无法获取全部结果的问题咨询
问题分析与解决方案
你的多线程版本失效核心原因出在全局变量的线程安全问题和错误的结果收集方式上,咱们一步步拆解:
1. 全局变量的致命问题
你在ext_url里使用了全局的List_articles、List_header等列表,而且每个线程进入函数时都会执行List_articles = []这类清空操作——这意味着多个线程会同时读写这些全局变量,不仅会导致数据互相覆盖,而且先执行的线程刚添加的数据会被后执行的线程清空,最终只能拿到最后一个线程的结果。
2. 没有正确收集多线程的返回值
你用executor.submit提交了任务,但没有去获取每个任务的返回结果,而是依赖全局变量来存储数据,这完全违背了多线程任务的正确使用方式。
修正后的代码
我把代码改成了无全局变量的版本,每个线程独立处理自己的结果,最后在主线程统一收集合并:
import concurrent.futures import urllib.request import time from bs4 import BeautifulSoup import re from datetime import datetime MAX_THREADS = 30 filtered_Final_LIST = [ 'https://www.financialexpress.com/economy/finmin-asks-ministries-to-restrict-expenses-within-prescribed-limit/2410766/', 'https://www.financialexpress.com/economy/uk-inflation-hits-near-30-year-high-pressuring-boe-and-households/2410761/', 'https://www.financialexpress.com/economy/economic-recovery-yet-to-attain-durability-says-report/2410690/', 'https://www.financialexpress.com/economy/vagaries-of-weather-drive-near-13-lakh-maha-farmers-to-crop-insurance-scheme/2410030/' ] def ext_url(url): # 每个线程用自己的局部变量存储结果,避免全局冲突 List_articles = [] List_header = [] List_date = [] List_month = [] List_year = [] List_source = [] try: html = urllib.request.urlopen(url, timeout=10) print(f"Processing: {url}") htmlParse = BeautifulSoup(html, 'html.parser') # 提取文章内容 para = htmlParse.find('div', class_='entry-content wp-block-post-content') if para: List_articles.append(para.get_text()) # 提取发布日期 date_tag = htmlParse.find(itemprop="article:published_time") if date_tag and date_tag.get("content"): date = date_tag.get("content") match = re.search(r'\d{4}-\d{2}-\d{2}', date) if match: dt = datetime.strptime(match.group(), '%Y-%m-%d').date() List_month.append(dt.month) List_date.append(dt.day) List_year.append(dt.year) # 提取标题 header = htmlParse.find('h1', class_='wp-block-post-title') if header: List_header.append(header.get_text()) # 提取来源/作者 source = htmlParse.find('div', class_='author-link ie_custom_theme_multiple_authors') if source: List_source.append(source.get_text()) except Exception as e: print(f"Error processing {url}: {str(e)}") return List_articles, List_header, List_date, List_month, List_year, List_source # 主线程收集所有结果 if __name__ == "__main__": start_time = time.time() # 使用as_completed来获取每个完成的任务结果 with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: # 提交所有任务 future_to_url = {executor.submit(ext_url, url): url for url in filtered_Final_LIST} # 初始化最终的汇总列表 final_articles = [] final_headers = [] final_dates = [] final_months = [] final_years = [] final_sources = [] # 遍历完成的任务 for future in concurrent.futures.as_completed(future_to_url): url = future_to_url[future] try: articles, headers, dates, months, years, sources = future.result() # 合并结果 final_articles.extend(articles) final_headers.extend(headers) final_dates.extend(dates) final_months.extend(months) final_years.extend(years) final_sources.extend(sources) except Exception as e: print(f"Task for {url} failed: {str(e)}") print(f"Total time taken: {time.time() - start_time:.2f} seconds") # 这里可以查看最终的汇总数据 print(f"Extracted {len(final_headers)} articles")
关键改进点
- 去掉了所有全局变量,每个线程使用自己的局部变量存储结果,避免线程间的数据干扰
- 使用
concurrent.futures.as_completed来跟踪任务完成状态,并安全收集每个任务的返回值 - 添加了异常捕获,单个URL处理失败不会影响其他任务
- 优化了元素查找逻辑,增加了空值判断,避免因为页面结构变化导致报错
- 修正了原链接里多余的
"字符,避免请求失败
内容的提问来源于stack exchange,提问作者graj499
相关产品推荐
相关产品推荐

