
新闻爬虫的核心架构分为三层:请求层(获取网页原始数据)、解析层(提取目标信息)、清洗层(标准化数据格式),辅以存储层完成数据持久化。技术选型上,Python 凭借丰富的库生态成为首选:
requests(常规请求)、Selenium(动态渲染页面);BeautifulSoup4(HTML 解析)、lxml(高性能解析);re(正则表达式)、pandas(数据标准化);pandas.to_csv(文件存储)、pymongo(数据库存储)。新闻网站的反爬机制(如 UA 验证、IP 封锁、动态渲染)、页面结构差异(不同栏目 HTML 布局不同)、数据噪声(广告文本、冗余标签、乱码)是构建爬虫的三大核心挑战。本文将围绕这些挑战给出针对性解决方案。
以国内某新闻资讯网站的资讯栏目为例(示例使用模拟域名,实际需替换为合法目标站点),实现静态页面的新闻数据提取,核心步骤包括:请求发送、HTML 解析、目标字段提取。
python
import requests
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
import pandas as pd
import re
import time
from random import randint
class NewsCrawler:
def __init__(self):
# 初始化请求头,随机生成User-Agent
self.ua = UserAgent()
self.headers = {
'User-Agent': self.ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.baidu.com' # 模拟来路,降低反爬风险
}
# 代理配置信息
self.proxyHost = "www.16yun.cn"
self.proxyPort = "5445"
self.proxyUser = "16QMSOML"
self.proxyPass = "280651"
# 构建代理字典(支持http和https)
self.proxies = {
"http": f"http://{self.proxyUser}:{self.proxyPass}@{self.proxyHost}:{self.proxyPort}",
"https": f"https://{self.proxyUser}:{self.proxyPass}@{self.proxyHost}:{self.proxyPort}"
}
# 待提取的新闻字段
self.news_data = []
def send_request(self, url):
"""发送请求,处理基础异常(集成代理)"""
try:
# 随机延时,模拟人工访问
time.sleep(randint(1, 3))
# 添加proxies参数使用代理请求
response = requests.get(
url,
headers=self.headers,
proxies=self.proxies, # 启用代理
timeout=10,
verify=False # 忽略SSL证书验证(部分代理场景需要)
)
response.raise_for_status() # 抛出HTTP错误
response.encoding = response.apparent_encoding # 自动识别编码,解决乱码
return response.text
except requests.exceptions.ProxyError as e:
print(f"代理请求失败:{url},错误信息:{e}")
return None
except requests.exceptions.RequestException as e:
print(f"请求失败:{url},错误信息:{e}")
return None
def parse_news_list(self, list_url):
"""解析新闻列表页,提取新闻详情页链接"""
html = self.send_request(list_url)
if not html:
return
soup = BeautifulSoup(html, 'lxml')
# 定位新闻列表项(需根据目标网站调整CSS选择器)
news_items = soup.select('div.news-list > ul > li')
for item in news_items:
try:
# 提取标题和详情页链接
title_tag = item.select_one('a.news-title')
if not title_tag:
continue
title = title_tag.get_text(strip=True)
detail_url = title_tag.get('href')
# 补全相对链接
if not detail_url.startswith('http'):
detail_url = 'https://www.example.com' + detail_url
# 解析详情页
self.parse_news_detail(detail_url, title)
except Exception as e:
print(f"解析列表项失败:{e}")
continue
def parse_news_detail(self, url, title):
"""解析新闻详情页,提取内容、发布时间"""
html = self.send_request(url)
if not html:
return
soup = BeautifulSoup(html, 'lxml')
try:
# 提取发布时间(正则匹配时间格式)
time_text = soup.select_one('div.news-meta > span.publish-time').get_text()
publish_time = re.search(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', time_text).group()
# 提取新闻正文(剔除广告、冗余标签)
content_tag = soup.select_one('div.news-content')
# 移除广告标签
for ad_tag in content_tag.select('div.ad, p.ad-text'):
ad_tag.extract()
# 提取正文文本,清理空白字符
content = ''.join([p.get_text(strip=True) for p in content_tag.select('p')])
# 存入临时列表
self.news_data.append({
'title': title,
'publish_time': publish_time,
'url': url,
'content': content
})
print(f"成功解析:{title}")
except Exception as e:
print(f"解析详情页失败:{url},错误:{e}")
return
def save_data(self, save_path='news_data.csv'):
"""将清洗后的数据保存为CSV文件"""
df = pd.DataFrame(self.news_data)
# 去重(根据标题和URL去重)
df = df.drop_duplicates(subset=['title', 'url'], keep='first')
# 过滤空内容
df = df[df['content'].str.len() > 50]
df.to_csv(save_path, index=False, encoding='utf-8-sig')
print(f"数据保存完成,共{len(df)}条有效数据")
if __name__ == '__main__':
# 初始化爬虫
crawler = NewsCrawler()
# 爬取新闻列表页(示例链接,需替换为实际目标)
list_url = 'https://www.example.com/news/list?category=tech'
crawler.parse_news_list(list_url)
# 保存数据
crawler.save_data()部分新闻网站采用 JavaScript 动态渲染页面(如滚动加载、异步加载),requests无法获取渲染后的内容,需使用Selenium模拟浏览器访问:
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class DynamicNewsCrawler(NewsCrawler):
def __init__(self):
super().__init__()
# 配置Chrome无头模式,无界面运行
chrome_options = Options()
chrome_options.add_argument('--headless=new')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument(f'user-agent={self.ua.random}')
self.driver = webdriver.Chrome(options=chrome_options)
self.wait = WebDriverWait(self.driver, 10)
def send_dynamic_request(self, url):
"""发送动态页面请求,等待元素加载"""
try:
self.driver.get(url)
# 等待核心元素加载完成(根据目标网站调整)
self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'div.news-content'))
)
return self.driver.page_source
except Exception as e:
print(f"动态请求失败:{e}")
return None
# 重写详情页解析方法
def parse_news_detail(self, url, title):
html = self.send_dynamic_request(url)
if not html:
return
# 后续解析逻辑与静态版一致
soup = BeautifulSoup(html, 'lxml')
# ...(省略重复代码)
def __del__(self):
# 关闭浏览器
if hasattr(self, 'driver'):
self.driver.quit()爬取的原始新闻数据存在大量噪声,如乱码、空白字符、广告文本、重复数据、格式不统一等,需通过系统化清洗提升数据可用性。
response.apparent_encoding自动识别编码,解决中文乱码;strip()去除首尾空格,re.sub(r'\s+', ' ', text)合并连续空白;re.sub(r'【广告】|免责声明:.*', '', content));def clean_content(self, content):
# 移除HTML标签
content = re.sub(r'<[^>]+>', '', content)
# 移除emoji(匹配Unicode表情区间)
emoji_pattern = re.compile("["
u"\U0001F600-\U0001F64F" # 表情符号
u"\U0001F300-\U0001F5FF" # 符号/图标
u"\U0001F680-\U0001F6FF" # 交通/地图符号
"]+", flags=re.UNICODE)
content = emoji_pattern.sub(r'', content)
# 移除特殊符号
content = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?;:""''()()、]', '', content)
return content.strip()pandas.drop_duplicates实现;YYYY-MM-DD HH:MM:SS格式:python运行from datetime import datetime
def standardize_time(self, time_text):
# 匹配多种时间格式
time_patterns = [
r'%Y-%m-%d %H:%M:%S',
r'%Y/%m/%d %H:%M:%S',
r'%Y年%m月%d日 %H:%M',
r'%m月%d日 %H:%M'
]
for pattern in time_patterns:
try:
if '年' in time_text and not time_text.startswith('2025'):
time_text = '2025年' + time_text # 补充年份(示例)
dt = datetime.strptime(re.search(r'\d{4}.\d{2}.\d{2}.\d{2}:\d{2}', time_text).group(), pattern)
return dt.strftime('%Y-%m-%d %H:%M:%S')
except:
continue
return None无效数据过滤:剔除正文长度过短(如小于 50 字)、标题为空、链接无效的数据,避免低价值内容干扰分析。3.3 异常值处理处理缺失值:对缺失发布时间的新闻,标记为unknown或根据爬取时间填充;处理极端值:对正文过长(如包含重复内容)的新闻,截取合理长度或标记为异常数据。四、反爬策略规避与合规性反爬规避技巧:随机延时(1-3 秒),避免高频请求;轮换 User-Agent、IP 代理(商用代理池);遵守robots.txt协议,不爬取禁止访问的页面;避免爬取登录后的内容,减少账号风险。合规性要求:仅爬取公开可访问的新闻数据,不涉及隐私;爬取数据仅用于非商业研究或内部分析,不篡改、不传播;控制爬取频率,避免对目标网站服务器造成压力。五、总结与扩展本文构建的新闻爬虫实现了静态 / 动态页面的适配、核心字段提取与系统化数据清洗,可满足基础的新闻数据采集需求。实际应用中,可进一步扩展:分布式爬虫:基于Scrapy框架实现大规模并行爬取;数据入库:将清洗后的数据存入 MySQL/MongoDB,便于后续检索;增量爬取:基于发布时间实现每日增量更新,避免重复爬取;情感分析:结合 NLP 技术对新闻内容进行情感倾向、关键词提取等深度分析。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。