优化BeautifulSoup爬虫 爬取Fleetmon海事新闻导出指定格式CSV
Fleetmon海事新闻爬虫优化方案
原有代码问题
- 语法缩进错误:分页循环内的请求、解析逻辑未正确缩进,仅会处理循环最后一次的响应
- 链接格式错误:列表页提取的是相对路径,未拼接域名直接请求会返回404
- 元素定位错误:原有详情页选择器与站点实际DOM结构不匹配,无法提取到目标字段
- 反爬策略缺失:列表页请求未携带合法请求头,极易被站点拦截
- 容错机制缺失:遇到请求超时、页面元素不存在时程序直接崩溃
- 无效链接未过滤:会抓取到导航、广告、标签页等非新闻详情链接,浪费请求资源
- 分页范围过小:默认仅爬取1页内容,无法获取全量数据
优化后完整代码
import requests from bs4 import BeautifulSoup import pandas as pd import time from urllib.parse import urljoin # 基础配置 BASE_URL = 'https://www.fleetmon.com/maritime-news/' HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 'Accept-Language': 'en-US,en;q=0.9', 'Referer': 'https://www.fleetmon.com/' } # 配置爬取页数,全量爬取可修改为列表页实际总页数 MAX_PAGE = 10 REQUEST_DELAY = 1 # 请求间隔,单位秒,避免被封 SAVE_PATH = './fleetmon_news.csv' def get_news_links(): """获取所有新闻详情页链接""" news_links = set() for page in range(1, MAX_PAGE + 1): try: resp = requests.get( BASE_URL, params={'page': page}, headers=HEADERS, timeout=10 ) resp.raise_for_status() soup = BeautifulSoup(resp.text, 'html.parser') # 精准定位新闻卡片的标题链接,过滤无效链接 card_links = soup.select('a.article-card__title[href*="/maritime-news/"]') for link in card_links: full_url = urljoin(BASE_URL, link['href']) news_links.add(full_url) print(f'第{page}页链接提取完成,当前累计有效链接{len(news_links)}条') time.sleep(REQUEST_DELAY) except Exception as e: print(f'第{page}页链接提取失败:{str(e)}') continue return list(news_links) def parse_news_detail(url): """解析单条新闻详情页内容""" try: resp = requests.get(url, headers=HEADERS, timeout=10) resp.raise_for_status() soup = BeautifulSoup(resp.text, 'html.parser') # 提取标题 title = soup.select_one('h1.article-header__title').text.strip() # 提取分类:取面包屑最后一个分类文本 category = soup.select('nav.breadcrumbs a')[-1].text.strip() # 提取发布时间,格式与要求一致 pub_time = soup.select_one('span.article-meta__date').text.strip() # 提取正文:拼接所有段落文本,去掉多余空白 content_paragraphs = soup.select('div.article__content p') content = '\n'.join([p.text.strip() for p in content_paragraphs if p.text.strip()]) return { 'News_title': title, 'category': category, 'publish_date_time': pub_time, 'news': content } except Exception as e: print(f'详情页解析失败[{url}]:{str(e)}') return None if __name__ == '__main__': all_links = get_news_links() print(f'共获取到{len(all_links)}条有效新闻链接,开始解析详情') news_data = [] for idx, link in enumerate(all_links): item = parse_news_detail(link) if item: news_data.append(item) print(f'进度:{idx+1}/{len(all_links)}') time.sleep(REQUEST_DELAY) # 按指定字段顺序导出CSV,编码适配Excel打开不乱码 df = pd.DataFrame(news_data, columns=['News_title', 'category', 'publish_date_time', 'news']) df.to_csv(SAVE_PATH, index=False, encoding='utf-8-sig') print(f'爬取完成,共保存有效新闻{len(news_data)}条,文件保存路径:{SAVE_PATH}')
使用说明
- 全量爬取时,先访问新闻列表页,拉到最底部分页栏查看总页数,将代码中
MAX_PAGE变量修改为对应数值即可 - 若爬取过程中出现连接被拒绝的情况,可适当调大
REQUEST_DELAY的数值,降低请求频率 - 导出的CSV默认保存在代码运行的同级目录,可修改
SAVE_PATH自定义保存位置 - 代码自动过滤解析失败的条目,不会因单条新闻异常中断整体爬取流程
内容的提问来源于stack exchange,提问作者Aimal
相关产品推荐
相关产品推荐

