数据采集作业3

gitee链接:作业3_wzm

作业①: 使用Scrapy爬取中国气象网的图片

本次作业的目标是使用Scrapy框架,从中国气象网(http://www.weather.com.cn)爬取图片。要求实现单线程和多线程的爬取方式,并对爬取的页面数和下载的图片数量进行限制,避免对目标网站造成过大压力。

项目结构

项目结构如下:

weather_images/
├── images/                         # 存储下载的图片
│   └── full/                       # 图片下载后的实际存储位置
├── weather_images/                 # Scrapy 项目文件夹
│   ├── spiders/                    # 爬虫文件夹
│   │   ├── __init__.py             # Spiders模块的初始化文件
│   │   └── weather_spider.py       # 主要爬虫代码
│   ├── __init__.py                 # 项目初始化文件
│   ├── items.py                    # 定义数据模型
│   ├── middlewares.py              # 中间件配置
│   ├── pipelines.py                # 管道配置,用于处理下载图片
│   └── settings.py                 # Scrapy配置文件
└── scrapy.cfg                      # Scrapy全局配置文件

各文件的作用

  1. images: 用于存储爬取到的图片,full文件夹内保存所有下载完成的图片。

  2. weather_images: Scrapy项目的主文件夹,包含爬虫和项目配置。

    • spiders: 该文件夹包含所有的爬虫文件。每个爬虫文件是一个Python脚本,定义了爬取逻辑。

      • weather_spider.py: 此文件是核心的爬虫代码,定义了爬虫类 WeatherSpider,并包含具体的图片爬取逻辑。
    • items.py: 定义了爬取到的图片的模型WeatherImagesItem,包括图片URL等信息。

    • middlewares.py: 配置和定义了Scrapy中间件,通常用于请求和响应的自定义处理。

    • pipelines.py: 设置数据处理管道,负责将爬取到的数据(图片URL)进行处理并下载到指定路径。

    • settings.py: Scrapy的配置文件,用于设置项目参数,比如并发数量、下载延时等,可以在此处调整设置以实现单线程或多线程爬取。

  3. scrapy.cfg: Scrapy的全局配置文件,用于定义项目的基本信息和默认设置。

主要爬虫代码

以下是 weather_spider.py 的详细代码,用于从目标网站爬取图片,并控制最大下载图片数量和最大爬取页面数。

import scrapy
from scrapy.pipelines.images import ImagesPipeline
from weather_images.items import WeatherImagesItem

class WeatherSpider(scrapy.Spider):
    name = 'weather_spider'
    allowed_domains = ['weather.com.cn']
    start_urls = ['http://www.weather.com.cn']  # 起始页面
    image_count = 0
    max_images = 105  # 最大下载图片数量
    max_pages = 5  # 最大爬取页面数> 

    def parse(self, response):
        if self.image_count < self.max_images:
            # 提取当前页面所有图片的URL
            for img in response.css('img::attr(src)').getall():
                if self.image_count < self.max_images:
                    image_url = response.urljoin(img)  # 拼接完整URL
                    self.image_count += 1
                    yield WeatherImagesItem(image_urls=[image_url])
                    self.log(f'Downloading image: {image_url}')
                else:
                    break

            # 继续爬取下一页
            next_page = response.css('a.next::attr(href)').get()
            if next_page and self.image_count < self.max_images:
                next_page_url = response.urljoin(next_page)
                yield scrapy.Request(next_page_url, callback=self.parse)

代码说明

  • start_urls: 定义了爬虫的起始页面,爬虫从此页面开始进行爬取。
  • image_countmax_images: image_count用于记录当前已下载的图片数量,max_images=105则设定了最大下载数量。
  • max_pages: 设置最大爬取页面数为5页,避免无限制地抓取。
  • parse方法: 此方法定义了爬取和解析逻辑,使用CSS选择器提取页面中所有的图片URL,并加入下载队列。
  • next_page: 如果存在下一页链接,则继续发送请求,以递归方式爬取指定页面数量。

下载图片的处理管道 (pipelines.py)

pipelines.py 中,我们配置了 ImagesPipeline 管道,用于处理图片的下载。

from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
from scrapy import Request

class WeatherImagesPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            yield Request(image_url)

    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Image Downloaded Failed")
        item['image_paths'] = image_paths
        return item

代码说明

  • get_media_requests: 从 item 中获取图片的URL并发送请求。
  • item_completed: 检查下载是否成功,如果成功则保留图片路径,否则丢弃该 item

输出信息

爬虫运行时,会在控制台输出每张图片的下载URL,示例信息如下:

Downloading image: http://www.weather.com.cn/path/to/image1.jpg
Downloading image: http://www.weather.com.cn/path/to/image2.jpg
...

下载的图片存储路径

下载的图片会存储在 images/full 文件夹中。以下是下载后的文件夹截图:

下载后的文件夹截图

配置单线程和多线程爬取

可以通过修改 settings.py 中的 CONCURRENT_REQUESTS 参数来控制单线程和多线程爬取:

  • 单线程:将 CONCURRENT_REQUESTS 设置为 1

    CONCURRENT_REQUESTS = 1
    DOWNLOAD_DELAY = 1  # 延迟设置
    
  • 多线程:增大 CONCURRENT_REQUESTS 的值(例如 CONCURRENT_REQUESTS = 16),并根据需要调整 DOWNLOAD_DELAY

项目代码和文件存储链接

您可以在 Gitee 上查看完整的项目代码和图片存储:Gitee 项目链接


通过本次作业,实现了对中国气象网图片的爬取,并通过Scrapy设置了爬取页面数和图片数量的限制,确保在合规范围内进行数据采集。这种方法不仅适用于天气网站,还可以应用到其他图片资源丰富的站点,为进一步的数据分析和研究提供了数据支持。


作业②: 使用 Scrapy + MySQL 爬取东方财富网股票数据

本次作业的目标是熟练掌握 Scrapy 的 ItemPipeline 以及数据存储的序列化输出方法,爬取东方财富网的股票相关信息,并将数据存储到 MySQL 数据库中。

项目结构

项目的基本目录结构如下:

stock_spider/
├── scrapy.cfg                       # Scrapy 全局配置文件
├── stock_spider/                    # Scrapy 项目文件夹
│   ├── __init__.py                  # 初始化文件
│   ├── items.py                     # 定义爬取的数据结构
│   ├── middlewares.py               # 中间件配置
│   ├── pipelines.py                 # 数据管道,处理并存储数据
│   ├── settings.py                  # Scrapy 项目配置
│   └── spiders/                     # 爬虫文件夹
│       ├── __init__.py              # 初始化文件
│       └── stock_spider.py          # 核心爬虫代码

功能描述

目标是爬取东方财富网股票信息,包括以下字段:

  • 股票代码
  • 股票名称
  • 最新报价
  • 涨跌幅
  • 涨跌额
  • 成交量
  • 成交额
  • 振幅

数据存储在 MySQL 数据库中,表的字段如下:

字段名 数据类型 描述
最新报价 DOUBLE 股票当前价格
涨跌幅 DOUBLE 涨跌的百分比
涨跌额 DOUBLE 涨跌的金额
成交量 DOUBLE 交易量
成交额 DOUBLE 交易金额
振幅 DOUBLE 振幅百分比
股票代码 VARCHAR 股票代码
股票名称 VARCHAR 股票名称

代码实现

1. items.py 文件

定义数据结构 StockItem,存储爬取的数据字段:

import scrapy

class StockItem(scrapy.Item):
    股票代码 = scrapy.Field()
    股票名称 = scrapy.Field()
    最新报价 = scrapy.Field()
    涨跌幅 = scrapy.Field()
    涨跌额 = scrapy.Field()
    成交量 = scrapy.Field()
    成交额 = scrapy.Field()
    振幅 = scrapy.Field()

2. spiders/stock_spider.py 文件

爬取股票数据的核心爬虫代码:

import scrapy
import json
from stock_spider.items import StockItem

class StockSpider(scrapy.Spider):
    name = "stock_spider"
    start_urls = [
        'http://25.push2.eastmoney.com/api/qt/clist/get?pn=1&pz=20&po=1&fields=f2,f3,f4,f5,f6,f7,f12,f14'
    ]

    def parse(self, response):
        # 解析 JSON 数据
        data = json.loads(response.text)
        stock_list = data.get('data', {}).get('diff', [])
        for stock in stock_list:
            item = StockItem(
                股票代码=stock.get('f12'),
                股票名称=stock.get('f14'),
                最新报价=stock.get('f2'),
                涨跌幅=stock.get('f3'),
                涨跌额=stock.get('f4'),
                成交量=stock.get('f5'),
                成交额=stock.get('f6'),
                振幅=stock.get('f7'),
            )
            yield item

3. pipelines.py 文件

将爬取的数据存储到 MySQL 数据库:

import pymysql

class StockPipeline:
    def open_spider(self, spider):
        # 连接 MySQL 数据库
        self.connection = pymysql.connect(
            host='localhost',
            user='root',
            password='your_password',
            database='stock_db',
            charset='utf8'
        )
        self.cursor = self.connection.cursor()
        # 创建数据表
        self.cursor.execute("""
            CREATE TABLE IF NOT EXISTS stocks (
                股票代码 VARCHAR(12) PRIMARY KEY,
                股票名称 VARCHAR(32),
                最新报价 DOUBLE,
                涨跌幅 DOUBLE,
                涨跌额 DOUBLE,
                成交量 DOUBLE,
                成交额 DOUBLE,
                振幅 DOUBLE
            )
        """)

    def process_item(self, item, spider):
        # 插入数据到表
        self.cursor.execute("""
            INSERT INTO stocks (
                股票代码, 股票名称, 最新报价, 涨跌幅, 涨跌额, 成交量, 成交额, 振幅
            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
                最新报价=VALUES(最新报价),
                涨跌幅=VALUES(涨跌幅),
                涨跌额=VALUES(涨跌额),
                成交量=VALUES(成交量),
                成交额=VALUES(成交额),
                振幅=VALUES(振幅)
        """, (
            item['股票代码'], item['股票名称'], item['最新报价'], item['涨跌幅'], item['涨跌额'],
            item['成交量'], item['成交额'], item['振幅']
        ))
        self.connection.commit()
        return item

    def close_spider(self, spider):
        # 关闭数据库连接
        self.cursor.close()
        self.connection.close()

4. settings.py 文件

配置 Scrapy 项目和 Pipeline:

BOT_NAME = 'stock_spider'
SPIDER_MODULES = ['stock_spider.spiders']
NEWSPIDER_MODULE = 'stock_spider.spiders'

ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {
    'stock_spider.pipelines.StockPipeline': 300,
}
LOG_LEVEL = 'INFO'

运行项目

  1. 启动 MySQL 数据库
    确保数据库已创建:

    CREATE DATABASE stock_db;
    
  2. 运行 Scrapy 爬虫
    在项目目录下执行命令:

    scrapy crawl stock_spider
    

输出结果

爬取的数据会存储在 MySQL 数据库 stock_dbstocks 表中,运行以下 SQL 查询查看结果:

SELECT * FROM stocks LIMIT 10;

输出如下:

常见问题及解决方案

  1. MySQL 数据库连接失败

    • 确保数据库已启动。
    • 检查用户名、密码和数据库名称是否正确。
  2. 爬取被 robots.txt 限制

    • settings.py 中设置 ROBOTSTXT_OBEY = False

通过本次作业,学习了如何使用 Scrapy 爬取东方财富网的股票信息,并将数据存储到 MySQL 数据库中。这种方法可以进一步扩展,用于实时更新和分析股票数据。

作业③:使用 Scrapy + XPath + MySQL 爬取中国银行外汇数据

本次作业目标是熟练掌握 Scrapy 中 ItemPipeline 数据的序列化输出方法,爬取中国银行外汇页面(中国银行外汇牌价),提取外汇汇率数据并存储到 MySQL 数据库中。


项目结构

项目的基本目录结构如下:

boc_exchange/
├── boc_exchange/                    # Scrapy项目文件夹
│   ├── spiders/                     # 爬虫文件夹
│   │   ├── __init__.py              # 爬虫模块初始化文件
│   │   └── boc_spider.py            # 核心爬虫代码
│   ├── __init__.py                  # Scrapy项目初始化文件
│   ├── items.py                     # 定义数据结构
│   ├── pipelines.py                 # 数据存储逻辑
│   ├── settings.py                  # Scrapy配置文件
├── scrapy.cfg                       # Scrapy全局配置文件

需求分析

爬取外汇汇率数据,并存储到数据库。目标字段如下:

字段名 数据类型 描述
Currency VARCHAR 货币名称
TBP (现汇买入价) DOUBLE 汇率
CBP (现钞买入价) DOUBLE 汇率
TSP (现汇卖出价) DOUBLE 汇率
CSP (现钞卖出价) DOUBLE 汇率
Time VARCHAR 更新时间

代码实现

1. items.py

定义数据模型 Work3Item,用于存储爬取到的外汇数据:

import scrapy

class Work3Item(scrapy.Item):
    Currency = scrapy.Field()  # 货币名称
    TBP = scrapy.Field()       # 现汇买入价
    CBP = scrapy.Field()       # 现钞买入价
    TSP = scrapy.Field()       # 现汇卖出价
    CSP = scrapy.Field()       # 现钞卖出价
    Time = scrapy.Field()      # 更新时间

2. spiders/boc_spider.py

编写爬虫逻辑,从目标网页中提取汇率数据:

import scrapy
from boc_exchange.items import Work3Item

class BocSpider(scrapy.Spider):
    name = "boc_spider"
    allowed_domains = ["boc.cn"]
    start_urls = ['https://www.boc.cn/sourcedb/whpj/']

    def parse(self, response):
        # 定位数据表格中的行,跳过第一行表头
        rows = response.xpath('//table[@align="left"]//tr[position()>1]')
        for row in rows:
            item = Work3Item()
            item['Currency'] = row.xpath('./td[1]/text()').get()
            item['TBP'] = row.xpath('./td[2]/text()').get()
            item['CBP'] = row.xpath('./td[3]/text()').get()
            item['TSP'] = row.xpath('./td[4]/text()').get()
            item['CSP'] = row.xpath('./td[5]/text()').get()
            item['Time'] = row.xpath('./td[last()]/text()').get()
            yield item

3. pipelines.py

将爬取到的数据存储到 MySQL 数据库中:

import pymysql

class Work3Pipeline:
    def open_spider(self, spider):
        # 连接数据库
        try:
            self.db = pymysql.connect(
                host='127.0.0.1',
                user='root',
                password='your_password',
                database='exchange_rates',
                charset='utf8mb4'
            )
            self.cursor = self.db.cursor()
            self._create_table()
        except pymysql.MySQLError as e:
            spider.logger.error(f"Database connection failed: {e}")

    def _create_table(self):
        # 创建表
        try:
            self.cursor.execute("DROP TABLE IF EXISTS rates")
            self.cursor.execute("""
                CREATE TABLE rates (
                    Currency VARCHAR(50),
                    TBP VARCHAR(20),
                    CBP VARCHAR(20),
                    TSP VARCHAR(20),
                    CSP VARCHAR(20),
                    Time VARCHAR(50)
                )
            """)
        except pymysql.MySQLError as e:
            print(f"Table creation error: {e}")

    def process_item(self, item, spider):
        # 插入数据
        try:
            self.cursor.execute("""
                INSERT INTO rates (Currency, TBP, CBP, TSP, CSP, Time)
                VALUES (%s, %s, %s, %s, %s, %s)
            """, (
                item['Currency'], item['TBP'], item['CBP'], item['TSP'], item['CSP'], item['Time']
            ))
            self.db.commit()
        except pymysql.MySQLError as e:
            spider.logger.error(f"Failed to insert item: {e}")
        return item

    def close_spider(self, spider):
        # 关闭数据库连接
        self.cursor.close()
        self.db.close()

4. settings.py

配置 Scrapy 项目参数:

BOT_NAME = 'boc_exchange'
SPIDER_MODULES = ['boc_exchange.spiders']
NEWSPIDER_MODULE = 'boc_exchange.spiders'

ROBOTSTXT_OBEY = False
LOG_LEVEL = 'INFO'

# 配置 Pipeline
ITEM_PIPELINES = {
    'boc_exchange.pipelines.Work3Pipeline': 300,
}

操作步骤

第一步:创建 Scrapy 项目

在命令行中运行以下命令,创建 Scrapy 项目:

scrapy startproject boc_exchange

第二步:定义数据库

登录 MySQL,创建数据库:

CREATE DATABASE exchange_rates;

第三步:编写代码

将上述代码保存到项目的对应文件中:

  • items.py
  • spiders/boc_spider.py
  • pipelines.py
  • settings.py

第四步:运行爬虫

进入项目根目录,运行以下命令启动爬虫:

scrapy crawl boc_spider

第五步:查看数据

爬虫运行完成后,登录 MySQL 数据库,查询爬取到的数据:

USE exchange_rates;
SELECT * FROM rates;

数据输出

优化点

  1. 防止重复插入
    可以在数据库表中为 Currency 字段添加主键或唯一约束,并在插入时使用 ON DUPLICATE KEY UPDATE

    ALTER TABLE rates ADD UNIQUE (Currency);
    
  2. 动态调整爬取范围
    通过传递 URL 参数控制爬取的范围或实时性。


通过本次作业,实现了从中国银行外汇牌价页面爬取汇率数据,并将其存储到 MySQL 数据库中。这种方法可以进一步扩展,用于实时监控外汇汇率数据,为数据分析和金融研究提供支持。


posted @ 2024-11-21 23:24  铭102202105  阅读(61)  评论(0)    收藏  举报