引言
在信息爆炸的今天,数据已成为知识的重要来源。然而,数据往往分散在互联网的各个角落,尤其是那些层层叠叠、链接迭迭的网页中。作为一个Python开发者或数据爬虫工程师,弄清楚如何高效地从多级页面中提取数据是一项宝贵的技能。这就是Scrapy出场的时候了。
Scrapy简介
Scrapy是一个快速的、高级的网页爬取框架,用Python编写,旨在爬取网页并从中提取出结构化数据。Scrapy借助其强大的API,使工程师能够编写爬虫规则来一次性收集来自多个页面上数以千计的项目数据——而无需人手动介入。
下面我以爬取国外的一个新闻网站上的新闻为例讲解一下如何使用Scrapy爬取多级页面数据。
我的思路是这样的,首先在第一级页面上获取每篇新闻的网页链接,再根据每篇新闻的链接进入到每篇新闻的网页获取其特定内容,比如新闻标题,新闻发布时间,新闻内容等。
创建Scrapy项目
安装Scrapy
首先确保Python环境已经安装,然后执行以下命令安装Scrapy:
pip install scrapy
开启新项目
通过Scrapy提供的命令行工具可以轻松创建项目:
scrapy startproject crawel_tehran_times
定义项目结构
一个Scrapy项目由以下几部分组成:
items.py
:定义要爬取的数据结构。middlewares.py
:定制不同类型的中间件。pipelines.py
:处理数据的管道文件。settings.py
:配置爬虫设置。spiders/
:放置爬虫代码的目录。
编写Scrapy爬虫
1.定义Item
在items.py
中,我们定义所需抓取数据的模型:
import scrapy
class CrawelTehranTimesItem(scrapy.Item):
# define the fields for your item here like:
# 新闻链接
news_url = scrapy.Field()
# 新闻标题
news_title = scrapy.Field()
# 新闻发布时间
release_time = scrapy.Field()
# 新闻类型
news_type = scrapy.Field()
# 图片链接
img_url= scrapy.Field()
# 新闻内容
news_content = scrapy.Field()
# 新闻标签
news_tags = scrapy.Field()
2.编写爬虫逻辑
在当前目录下输入命令,将在mySpider/spider目录下创建一个名为tehran_times的爬虫,并指定爬取域的范围:
scrapy genspider tehran_times tehrantimes.com
此时会在spiders包下生成一个tehran_times.py文件,你需要在该文件下编写爬虫逻辑获取你想要的数据。
import scrapy
from crawel_tehran_times.spiders.utils import time_deal
from crawel_tehran_times.items import CrawelTehranTimesItem
# 定义一个爬虫类,继承自scrapy.Spider
class TehranTimesSpider(scrapy.Spider):
# 爬虫的名字
name = "tehran_times"
# 允许爬取的域名
allowed_domains = ["tehrantimes.com"]
# 初始页码
page = 1
# 默认的url
default_url = 'https://www.tehrantimes.com'
# 基础的url,用于翻页
base_url = 'https://www.tehrantimes.com/page/archive.xhtml?mn=2&wide=0&dy=2&ms=0&pi={}&yr=2024&tp=698'
# 初始的url列表
start_urls = [base_url.format(page)]
# 结束的页码
page_end = 100
# 解析函数,用于解析响应
def parse(self, response):
# 提取新闻的url
news_urls = response.xpath('//main//div/section/div/section/div/ul/li/h3/a/@href').extract()
for news_url in news_urls:
# 如果url中包含'pdf',则跳过
if 'pdf' in news_url:
continue
# 创建一个CrawelTehranTimesItem实例
item = CrawelTehranTimesItem()
# 设置新闻的url
item['news_url'] = self.default_url + news_url
# 发起请求,获取新闻的详细信息
yield scrapy.Request(item['news_url'], meta={'item': item}, callback=self.detail_parse)
# 提取下一页的url
nextPage = response.xpath("//main//div/section/div/section/div/ul/li/a[contains(text(),'Next')]/@href").extract()
# 如果当前页码小于结束页码,并且存在下一页,那么继续爬取下一页
if self.page < self.page_end and len(nextPage) != 0:
# 页码加1
self.page = self.page + 1
# 发起请求,爬取下一页
yield scrapy.Request(self.default_url + nextPage[0], callback=self.parse)
else:
# 如果没有下一页,或者已经达到结束页码,那么结束爬虫
return None
# 详细解析函数,用于解析新闻的详细信息
def detail_parse(self, response):
# 获取上一级已经爬取的数据
item = response.meta['item']
# 提取新闻的标题
item['news_title'] = response.xpath("//main//div/section/article/div/h2/text()").extract()[0]
# 提取新闻的发布时间,并进行处理
release_time = response.xpath("//main/div//div/section/article/div/div[@class='item-date half-left']/text()").extract()[0]
# 提取新闻的类型
item['news_type'] = response.xpath("//main/div//div/section/article/div/div/ol/li/a/text()").extract()[0]
# 处理发布时间
item['release_time'] = time_deal(release_time)
# 提取图片的url
item['img_url'] = response.xpath("//main/div//div/section/article/div/figure/a/img/@src").extract()[0]
# 提取新闻的内容
news_content = response.xpath("//main/div//div/section/article/div/div/p/text()").extract()
# 替换新闻内容中的双引号
news_content = [content.replace('"','') for content in news_content]
# 提取新闻的标签
news_tags = response.xpath("//main/div//div/section/article/div/section/ul/li/a/text()").extract()
# 将标签列表转换为字符串
item['news_tags'] = ','.join(news_tags)
# 将内容列表转换为字符串
item['news_content'] = ''.join(news_content)
# 返回item
return item
3.保存爬取到的数据
你可以将爬取到的数据保存成你任何想要的格式,例如csv表中,数据库中,例如我就是将爬取到的数据保存在数据库中。
保存数据的逻辑代码是在项目目录下的pipelines.py文件中编写的。
# 导入logging模块,用于记录日志
import logging
# 导入itemadapter模块,用于处理不同类型的item
from itemadapter import ItemAdapter
# 定义一个管道类,用于处理爬虫获取的item
class CrawelTehranTimesPipeline:
def process_item(self, item, spider):
# 打印item
# print(item)
# 返回item
return item
# 导入用于获取项目设置的模块
from scrapy.utils.project import get_project_settings
# 导入pymysql模块,用于连接MySQL数据库
import pymysql
# 定义一个MySQL管道类,用于将爬虫获取的item存储到MySQL数据库
class MysqlPipeline:
# 创建数据表的方法
def create_table(self):
# SQL语句,用于创建数据表
sql = """
CREATE TABLE `tehran_times` (
`news_id` INT AUTO_INCREMENT PRIMARY KEY,
`news_url` varchar(255) DEFAULT NULL,
`news_title` varchar(255) DEFAULT NULL,
`release_time` datetime DEFAULT NULL,
`news_content` text,
`img_url` varchar(255) DEFAULT NULL,
`news_tags` varchar(255) DEFAULT NULL,
`news_type` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"""
# 执行SQL语句
self.cursor.execute(sql)
# 爬虫开始时的方法
def open_spider(self, spider):
# 获取项目设置
settings = get_project_settings()
# 获取数据库的各种配置
self.host = settings['DB_HOST']
self.port = settings['DB_PORT']
self.user = settings['DB_USER']
self.password = settings['DB_PASSWROD']
self.name = settings['DB_NAME']
self.charset = settings['DB_CHARSET']
# 连接数据库
self.connect()
# 创建数据表
self.create_table()
# 连接数据库的方法
def connect(self):
# 使用pymysql连接MySQL数据库
self.conn = pymysql.connect(
host=self.host,
port=self.port,
user=self.user,
password=self.password,
db=self.name,
charset=self.charset
)
# 获取游标
self.cursor = self.conn.cursor()
# 处理item的方法
def process_item(self, item, spider):
# SQL语句,用于将item插入到数据表
sql = 'insert into tehran_times(news_url,news_title,release_time,news_content,img_url,news_tags,news_type) values("{}","{}","{}","{}","{}","{}","{}")'.format(
item['news_url'], item['news_title'], item['release_time'], item['news_content'], item['img_url'], item['news_tags'], item['news_type'])
# 尝试执行SQL语句
try:
self.cursor.execute(sql)
# 提交事务
self.conn.commit()
# 记录日志
logging.info('插入数据成功')
# 如果出现异常
except Exception as e:
# 回滚事务
self.conn.rollback()
# 记录日志
logging.info('插入失败进行了回滚',e)
# 返回item
return item
# 爬虫结束时的方法
def close_spider(self, spider):
# 关闭游标
self.cursor.close()
# 关闭数据库连接
self.conn.close()
4.进行项目配置
在项目目录下的settings.py文件中为项目设置一些参数
# 不遵守网站Robots协议
ROBOTSTXT_OBEY = False
# 项目日志级别为INFO
LOG_LEVEL = 'INFO'
因为我爬的是国外的网站因此需要使用代理。
DOWNLOADER_MIDDLEWARES = {
# "crawel_tehran_times.middlewares.CrawelTehranTimesDownloaderMiddleware": 543,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 543
}
HTTP_PROXY = '代理地址:端口号' # HTTP代理
HTTPS_PROXY = '代理地址:端口号' # HTTPS代理
我还在pipelines.py文件中自定义了管道对象,所以我要将我自定义的管道加入到ITEM_PIPELINES中。
ITEM_PIPELINES = {
# "crawel_tehran_times.pipelines.CrawelTehranTimesPipeline": 300,
'crawel_tehran_times.pipelines.MysqlPipeline':301
}
我是将数据保存到了数据库中,因此需要在配置文件中设置我的数据库参数
# 参数中一个端口号 一个是字符集 都要注意
DB_HOST = '数据库服务器的主机名或IP地址'
# 端口号是一个整数
DB_PORT = 3306
DB_USER = '用户名'
DB_PASSWROD = '密码'
DB_NAME = '数据库名字'
# utf-8的杠不允许写
DB_CHARSET = 'utf8'
运行爬虫
现在我们可以运行爬虫,让Scrapy开始工作,你可以在cmd命令行中运行以下命令
scrapy crawl 爬虫名称
如果你不想呢么麻烦你可以在项目目录下新建一个start.py文件,输入以下代码也可以运行爬虫。
from scrapy import cmdline
cmdline.execute(["scrapy", "crawl", "爬虫名字"])
总结
爬虫技术旨在高效地获取有价值的数据。确保遵守所爬取网站的robots.txt
规则,并尊重数据的版权和隐私政策。祝您在自动化数据提取和分析领域一路顺风。