利用scrapy完成人民网社会法制栏目下原创新闻的爬取

人民网新闻爬虫开发指南

一、环境搭建与初始化命令

所需依赖安装

1
2
3
4
# 安装Scrapy框架
pip install scrapy
# 安装MySQL连接库
pip install pymysql

项目创建与初始化

1
2
3
4
5
6
# 创建Scrapy项目
scrapy startproject people_news_crawler
# 进入项目目录
cd people_news_crawler
# 生成爬虫脚本(域名需与目标网站一致)
scrapy genspider PeopleNews people.com.cn

二、项目文件结构说明

1
2
3
4
5
6
7
8
9
people_news_crawler/
├── scrapy.cfg # 部署配置文件
└── people_news_crawler/
├── __init__.py # 初始化文件
├── items.py # 数据结构定义
├── pipelines.py # 数据处理管道
├── settings.py # 项目配置文件
└── spiders/
└── people_news.py # 爬虫核心代码

三、核心代码实现

1. 爬虫类(PeopleNewsSpider.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import scrapy
from ..items import PeopleNewsCrawlerItem

class PeopleNewsSpider(scrapy.Spider):
name = "people_news" # 爬虫名称(需与genspider命令一致)
# 生成多页URL(此处爬取1-17页,共17页)
start_urls = [f'http://society.people.com.cn/GB/86800/index{i}.html' for i in range(1, 18)]

def parse(self, response):
"""解析列表页,提取文章链接并传递至详情页解析"""
article_list = response.css('.ej_list_box.clear ul')
for article in article_list:
links = article.css('a[href]') # 筛选包含href属性的a标签
for link in links:
item = PeopleNewsCrawlerItem()
item["href"] = "http://society.people.com.cn" + link.attrib["href"] # 补全绝对URL
item["title"] = link.css('::text').get().strip() # 提取标题并去空格
# 发送详情页请求,传递item至回调函数
yield scrapy.Request(item["href"], callback=self.parse_article, meta={"item": item})

def parse_article(self, response):
"""解析详情页,提取新闻具体内容"""
item = response.meta["item"]
content_div = response.css('.col.col-1.fl') # 内容容器

# 解析发布时间与来源
info_div = content_div.css('.col-1-1.fl')
info_text = info_div.css('::text').getall() # 获取所有文本节点
if len(info_text) >= 2:
item["time"] = info_text[0].strip() # 时间(第一个文本节点)
item["source"] = info_text[1].strip() if "来源" in info_text[1] else "解析失败" # 来源
else:
item["time"] = item["source"] = "解析失败"

# 解析正文内容
content_tags = content_div.css('.rm_txt_con.cf p')
item["content"] = "\n".join([p.css('::text').get().strip() for p in content_tags if p.css('::text').get()])
item["content"] = item["content"] or "解析失败" # 处理空内容

# 解析责任编辑
editor = content_div.css('.edit.cf::text').get()
item["author"] = editor.strip() if editor else "解析失败"

yield item # 传递至管道处理

2. 数据结构定义(items.py)

1
2
3
4
5
6
7
8
9
10
import scrapy

class PeopleNewsCrawlerItem(scrapy.Item):
"""定义新闻数据字段"""
href = scrapy.Field() # 文章链接(必选)
title = scrapy.Field() # 文章标题(必选)
time = scrapy.Field() # 发布时间(可选)
source = scrapy.Field() # 来源媒体(可选)
author = scrapy.Field() # 责任编辑(可选)
content = scrapy.Field() # 正文内容(必选)

3. 数据存储管道(pipelines.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import logging
import pymysql
from scrapy.exceptions import DropItem
from itemadapter import ItemAdapter

class MySQLPipeline:
"""MySQL数据库存储管道"""
def __init__(self, host, user, password, db):
self.host = host
self.user = user
self.password = password
self.db = db
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG) # 配置日志等级

@classmethod
def from_crawler(cls, crawler):
"""从配置文件获取数据库参数"""
return cls(
host=crawler.settings.get("MYSQL_HOST"),
user=crawler.settings.get("MYSQL_USER"),
password=crawler.settings.get("MYSQL_PASSWORD"),
db=crawler.settings.get("MYSQL_DB")
)

def open_spider(self, spider):
"""爬虫启动时建立数据库连接"""
try:
self.connection = pymysql.connect(
host=self.host,
user=self.user,
password=self.password,
db=self.db,
charset="utf8mb4", # 支持存储特殊字符
cursorclass=pymysql.cursors.DictCursor
)
self.cursor = self.connection.cursor()
self.logger.info("成功连接到数据库:%s", self.db)
except Exception as e:
self.logger.error("数据库连接失败:%s", str(e))
raise

def close_spider(self, spider):
"""爬虫关闭时释放连接"""
self.connection.close()
self.logger.info("数据库连接已关闭")

def process_item(self, item, spider):
"""处理数据并插入数据库"""
try:
sql = """
INSERT INTO people_news (
href, title, time, source, author, content
) VALUES (%s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE content = VALUES(content) # 避免重复插入
"""
self.cursor.execute(sql, (
item["href"],
item["title"],
item["time"],
item["source"],
item["author"],
item["content"]
))
self.connection.commit()
self.logger.debug("成功插入数据:%s", item["title"])
except pymysql.Error as e:
self.connection.rollback() # 出错时回滚事务
self.logger.error("数据插入失败:%s", str(e))
raise DropItem(f"丢弃损坏数据:{item['title']}")
return item

4. 项目配置(settings.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
BOT_NAME = "people_news_crawler"
SPIDER_MODULES = ["people_news_crawler.spiders"]
NEWSPIDER_MODULE = "people_news_crawler.spiders"

# 数据管道配置(数字越小优先级越高)
ITEM_PIPELINES = {
"people_news_crawler.pipelines.MySQLPipeline": 300,
}

# MySQL数据库配置
MYSQL_HOST = "localhost"
MYSQL_USER = "root"
MYSQL_PASSWORD = "1234"
MYSQL_DB = "news"

# 日志配置
LOG_FILE = "scrapy.log"
LOG_LEVEL = "DEBUG"
LOG_STDOUT = True # 将日志输出到终端

# 爬虫行为配置
ROBOTSTXT_OBEY = True # 遵守robots协议(开发测试时可设为False)
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
DOWNLOAD_DELAY = 1 # 下载延迟(秒),避免爬取过快

四、运行与调试

1. 启动爬虫

1
2
3
4
5
# 标准模式(数据存入数据库)
scrapy crawl people_news

# JSON输出模式(数据保存至文件)
scrapy crawl people_news -o output.json -t json

2. 常见问题处理

  • 终端无输出:检查settings.pyLOG_STDOUT是否设为True,或通过LOG_LEVEL调整日志等级(如设为INFO)。

  • 数据库连接失败

    1. 确保 MySQL 服务已启动

    2. 检查settings.py中的数据库用户名、密码、库名是否正确

    3. 授权用户远程访问数据库:

      1
      2
      GRANT ALL PRIVILEGES ON news.* TO 'root'@'localhost' IDENTIFIED BY '1234';
      FLUSH PRIVILEGES;
  • 数据解析失败: 使用scrapy shell "目标URL"调试 CSS 选择器,确保能正确提取数据。

五、扩展建议

  1. 反爬机制应对
    • 添加随机请求头(USER_AGENT池)
    • 启用代理 IP(通过DOWNLOADER_MIDDLEWARES配置)
    • 增加重试机制(RETRY_TIMES
  2. 分布式爬取: 集成Scrapy-Redis实现分布式部署,提升爬取效率。
  3. 数据清洗增强: 使用正则表达式去除正文冗余内容(如 “本文来源:” 前缀),或通过BeautifulSoup进一步解析富文本。
  4. 性能优化
    • 调整并发请求数(CONCURRENT_REQUESTS
    • 启用 DNS 缓存(DNSCACHE_ENABLED = True

通过以上步骤,可快速搭建一个稳定的新闻爬虫系统,实现对人民网指定频道内容的定时抓取与结构化存储。


利用scrapy完成人民网社会法制栏目下原创新闻的爬取
http://example.com/2024/11/07/利用scrapy完成人民网社会法制栏目下原创新闻的爬取/
作者
John Doe
发布于
2024年11月7日
许可协议