数据采集与融合第三次作业

码云仓库地址 https://gitee.com/sun-jiahui22/crawl_project
作业1仓库地址 https://gitee.com/sun-jiahui22/crawl_project/tree/master/作业3/实验3.1
作业2的仓库地址 https://gitee.com/sun-jiahui22/crawl_project/tree/master/作业3/实验3.2
作业3的仓库地址 https://gitee.com/sun-jiahui22/crawl_project/tree/master/作业3/实验3.3
学号姓名 102202115 孙佳会

作业①

要求
指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。使用scrapy框架分别实现单线程和多线程的方式爬取。

–务必控制总页数(学号尾数2位)、总下载的图片数量(尾数后3位)等限制爬取的措施。
输出信息:

将下载的Url信息在控制台输出,并将下载的图片存储在images子文件中,并给出截图。

爬取图片

🌱实现思路

爬虫 (weather.py):

WeatherSpider类通过分页链接递归抓取多个页面,每个页面上的所有图片链接被提取并保存到WeatherSpiderItem中。爬虫抓取的最大页面数和图片数由settings.py中的配置MAX_PAGES和MAX_IMAGES控制。

Item (items.py):

WeatherSpiderItem类定义爬虫抓取的数据结构

Pipeline (pipelines.py):

该管道负责管理图片的下载过程,包括发起请求、过滤掉下载失败的图片

配置 (settings.py):

爬虫的行为在settings.py中进行配置,例如:控制最大页面数(MAX_PAGES)。控制每个页面抓取的最大图片数(MAX_IMAGES)。配置图片下载的位置和存储格式等。

🎨主要代码

1.settings.py:

# 控制总页数
MAX_PAGES = 5  # 这里设置为5页,根据学号尾数进行调整
# 控制总下载的图片数量
MAX_IMAGES = 115  # 这里设置为115张

IMAGES_STORE = 'images'  # 图片存储目录
ITEM_PIPELINES = {
  'weather_spider.pipelines.WeatherSpiderPipeline': 1,
}

2.pipelines.py:

class WeatherSpiderPipeline(ImagesPipeline):
  def get_media_requests(self, item, info):
      for url in item['image_urls']:
          yield scrapy.Request(url)

  def item_completed(self, results, item, info):
      # 过滤没有下载成功的图片
      if not item['images']:
          raise DropItem("Item contains no images")
      return item

3.items.py:

class WeatherSpiderItem(scrapy.Item):
  image_urls = scrapy.Field()
  images = scrapy.Field()

4.weather.py:

class WeatherSpider(scrapy.Spider):
  name = 'weather'
  allowed_domains = ['weather.com.cn']
  start_urls = ['http://www.weather.com.cn/']

  def parse(self, response):
      pages = response.css('div.page a::attr(href)').getall()
      for page in pages[:self.settings.getint('MAX_PAGES')]:
          yield response.follow(page, self.parse_page)

  def parse_page(self, response):
      image_urls = response.css('img::attr(src)').getall()
      item = WeatherSpiderItem()
      item['image_urls'] = image_urls[:self.settings.getint('MAX_IMAGES')]
      # 输出每个图片的下载URL
      for url in item['image_urls']:
          self.logger.info(f'Downloading image from URL: {url}')

      yield item

运行结果:

终端输出:


images文件夹:

由于网站上图片总数并没有115张,所以只爬取了能爬取到的所有图片。

单线程爬取

scrapy默认多线程,如果需要单线程,在settings.py里进行设置:

# 下载延迟
DOWNLOAD_DELAY = 1  # 单线程下载速度

# 单线程模式
CONCURRENT_REQUESTS = 1

爬取结果与多线程一样,不再展示。

心得体会

  • 这次开发Scrapy爬虫项目,我对数据抓取、处理和存储有了更加深入的理解,尤其是Scrapy框架的高效性和灵活性。
  • Scrapy框架为我们提供了清晰的结构,爬虫的各个部分(爬虫、Item、Pipeline等)相互配合,使得开发过程变得系统化。特别是在处理大规模数据抓取时,Scrapy能够自动化地管理请求、响应和数据存储,大大提高了效率。

作业②

要求
熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取股票相关信息。
候选网站
东方财富网:https://www.eastmoney.com/

输出信息
MySQL数据库存储和输出格式如下:
表头英文命名例如:序号id,股票代码:bStockNo……,由同学们自行定义设计

股票代码 股票名称 最新报价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 最低 今开 昨收
301522 N上大 79.85 1060.61 72.97 595675 5031370784.78 582.56 102.00 61.92 61.92 6.88

爬取股票信息

🌱解析网页结构:

1.选择爬取“沪深京A股”板块


可以得到url为:https://quote.eastmoney.com/center/gridlist.html#hs_a_board

2.股票信息结构


根据此,设计XPATH爬取相应所需信息

3.翻页处理


根据此,设计翻页逻辑

🌱实现思路

1. Selenium 模拟浏览器爬取

  • 东方财富网的股票数据是通过 JavaScript 动态加载的,Scrapy 无法直接抓取。
  • 所以,使用 Selenium 来模拟浏览器,加载完整的页面内容,再用 Selenium 定位和提取动态加载的数据。这样可以确保数据在页面上完全加载后再进行解析。

2. MySQL 数据库准备

  • 在 MySQL 中创建数据库和表,数据库 stock_db,表 stock_data 。
  • 如果数据库中尚未存在 stock_data 表,创建该表以存储股票相关数据

3. 爬虫编写

初始化 Selenium

  • 在爬虫类的 __init__ 方法中,初始化 Selenium WebDriver,配置 ChromeOptions 为无头模式(headless)并设置 User-Agent。

解析网页

  • parse 方法中,使用 Selenium 加载东方财富网的目标 URL(例如:https://quote.eastmoney.com/center/gridlist.html#hs_a_board)。
  • 等待页面加载完成后,定位包含股票信息的表格。
  • 使用显式等待(如 WebDriverWaitexpected_conditions)确保数据完全加载,避免出现元素未加载导致定位失败的情况。

数据提取

  • 通过 Selenium 定位包含股票信息的行,逐行提取股票的各项数据,包括代码、名称、最新报价、涨跌额等。
  • 使用 Selenium 的 find_elements_by_xpath 方法来获取每一行的单元格数据,确保定位路径正确。
  • 将提取的数据存储到 StockItem 对象中,便于后续在 pipeline 中处理。

4. 数据存储

连接数据库

  • open_spider 方法中,建立与 MySQL 的连接。
  • close_spider 方法中,关闭数据库连接。

数据插入

  • process_item 方法中,将每条爬取到的数据插入到数据库表 stocks 中。
  • 检查字段格式,例如确保日期和浮点数的格式符合 MySQL 的要求。
  • 使用 try-except 处理插入时可能出现的异常,例如重复插入、格式不符等。

🎨主要代码:

  • 初始化爬虫并加载页面,使用 Selenium 的 driver.get() 方法加载东方财富网的目标页面。设置 max_pages 为最大页面数(5页),并初始化current_page为 1,表示从第一页开始爬取数据。
  • 使用显式等待(WebDriverWait)确保页面加载完成,并定位包含股票信息的表格行( 元素)。一旦找到该表格,将行数据存入 rows 列表。
    def parse(self, response):
        self.driver.get(response.url)
        max_pages = 5
        current_page = 1

        while current_page <= max_pages:
            # 等待页面加载必要的元素
            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.XPATH, '//table[@id="table_wrapper-table"]/tbody/tr'))
            )
            rows = self.driver.find_elements(By.XPATH, '//table[@id="table_wrapper-table"]/tbody/tr')
            print(f"Processing page {current_page} with {len(rows)} rows")

            for row in rows:
                cells = row.find_elements(By.TAG_NAME, 'td')
                
  • 翻页操作,尝试点击“下一页”按钮。使用显式等待确保“下一页”按钮可点击,然后点击按钮并递增 current_page。等待页面刷新,以确保新数据加载完成。在页面刷新期间,使用 EC.staleness_of(rows[0]) 等待上一页的元素失效。如果无法找到“下一页”按钮,或点击出现异常,则终止循环。
# 检查是否有下一页并且尝试点击
                try:
                    next_button = WebDriverWait(self.driver, 10).until(
                        EC.element_to_be_clickable(
                            (By.XPATH, '//a[@class="next paginate_button"]'))
                    )
                    next_button.click()
                    current_page += 1
                    # 等待页面刷新
                    WebDriverWait(self.driver, 10).until(
                        EC.staleness_of(rows[0])
                    )
                except Exception as e:
                    print(f"No more pages or unable to click next. Error: {e}")
                    break

  • Scrapy 数据管道,用于将爬取的股票数据存储到 MySQL 数据库中
class StockPipeline:
    def open_spider(self, spider):
        # 连接 MySQL 数据库
        self.conn = mysql.connector.connect(
            host=spider.settings.get('MYSQL_HOST'),
            user=spider.settings.get('MYSQL_USER'),
            password=spider.settings.get('MYSQL_PASSWORD'),
            database=spider.settings.get('MYSQL_DATABASE')
        )
        self.cursor = self.conn.cursor()

  • StockItem 类用于存储爬取到的股票数据,并定义了与股票相关的多个字段。每个字段使用 scrapy.Field() 进行声明,以便 Scrapy 能够识别并处理这些字段。
class StockItem(scrapy.Item):
    id = scrapy.Field()
    bStockNo = scrapy.Field()      # 股票代码
    bStockName = scrapy.Field()    # 股票名称
    bLastPrice = scrapy.Field()    # 最新报价
    bChangeRate = scrapy.Field()   # 涨跌幅
    bChangeAmount = scrapy.Field() # 涨跌额
    bVolume = scrapy.Field()       # 成交量
    bAmplitude = scrapy.Field()    # 振幅
    bHigh = scrapy.Field()         # 最高价
    bLow = scrapy.Field()          # 最低价
    bOpen = scrapy.Field()         # 今开
    bPrevClose = scrapy.Field()    # 昨收

运行结果:


爬取到了所需股票信息。

心得体会

在这次项目中,我学习并掌握了如何在 Scrapy 项目中集成 Selenium 和 MySQL 数据库。

  • 动态页面处理的挑战

在现代网页开发中,许多内容是通过 JavaScript 动态加载的,传统的 Scrapy 抓取方式无法直接获取这些数据。通过将 Selenium 集成到 Scrapy 中,可以加载并解析动态内容,从而实现对更多复杂页面的抓取。这让我认识到,在数据爬取中灵活运用多种工具是非常重要的。

  • 数据存储的重要性

在实际项目中,数据的持久化存储是非常关键的一步。通过将数据存储在 MySQL 数据库中,可以方便地对抓取的数据进行查询和分析。建立合理的数据库结构和配置合适的 Item Pipeline,不仅提高了数据处理的效率,也使得数据的管理更加规范化。

  • 自动化数据抓取的应用前景

在处理金融数据等实时数据时,自动化数据抓取可以极大提高数据的时效性和更新频率。这种技能在市场分析、商业情报、风险管理等领域都有广泛的应用前景。通过本次实践,我对自动化数据抓取的价值有了更深的体会,并对未来相关应用充满了期待。

遇到的问题与解决思路

在项目过程中,遇到了一些常见问题:

  • Selenium 配置问题:需要正确配置 WebDriver 和设置等待条件,确保页面加载完成后再提取数据。
  • MySQL 存储问题:正确设计数据库结构,避免数据类型不匹配或字段空值导致的错误。
  • 数据提取结构复杂:通过浏览器的开发者工具确定表格的确切结构,利用 Selenium 的 Xpath 选择器准确定位需要的字段。为了简化代码和提升稳定性,可以将提取的数据字段逐个检查和验证,确保每一列的数据提取无误。

作业③

要求
熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取外汇网站数据。
候选网站
中国银行网:https://www.boc.cn/sourcedb/whpj/
输出信息

Currency TBP CBP TSP CSP Time
阿联酋迪拉姆 198.58 192.31 199.98 206.59 11:27:14

爬取中国银行外汇数据

🌱解析网页结构:

外汇信息

根据此,设计XPATH爬取信息方式。

🌱实现思路:

由于页面内容是动态加载的,我们采用了 Selenium 驱动的 WebDriver 来加载网页,以确保页面完全呈现后再抓取数据:

1.初始化 Selenium WebDriver

在爬虫的初始化部分配置 Selenium 的 Chrome WebDriver,这样在实际爬取过程中可以自动加载动态内容。

2. 数据抓取流程

  • 通过 Selenium 加载目标网页,并设置明确的等待条件,以确保页面内容加载完成,尤其是表格内容。
  • 使用 XPath 定位页面中的表格,并对表格的每一行进行数据提取。
  • 每行数据分别对应 ForexItem 中的一个字段,并将每个 ForexItem 实例传递到数据存储管道进行处理。

3. 数据存储管道

pipelines.py 文件中,通过 MySQL 数据库管道将数据持久化存储:

  • 数据库连接管理:在 open_spider 方法中建立数据库连接,并在初次启动时检查并创建数据表。这一步可以确保数据存储有条不紊地进行,且不会重复创建表格。

  • 数据插入与格式化:在 process_item 中,将每一条数据记录插入到 MySQL 数据表中,并根据特定格式处理数值类型(如价格、变化幅度等)。

  • 关闭数据库连接:在爬虫结束时关闭数据库连接,确保没有资源泄露。

4. 配置文件设置

settings.py 中,设置了关键的全局配置,包括启用 Selenium 中间件、MySQL 数据库连接参数等。Scrapy 项目配置的模块化使得在不同环境中更改配置变得简单,同时确保项目的可移植性。

🎨主要代码

  • parse 方法是 Scrapy 中用于处理响应数据的主要方法。这里使用显式等待 (WebDriverWait) 来确保页面上需要的数据元素(外汇牌价表格)已经加载完成。我们等待的条件是页面中的表格元素(通过 XPath 定位)能够被找到。成功加载页面后,我们使用 find_elements 获取所有包含数据的行,每一行表示一个货币的外汇信息。
 def parse(self, response):
        # 使用 Selenium 加载页面
        self.driver.get(response.url)

        try:
            # 显式等待,确保表格第一个单元格加载完成
            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located(
                    (By.XPATH, '//table[@cellpadding="0" and @cellspacing="0" and @width="100%"]/tbody/tr/td'))
            )
            print("Table loaded successfully.")
        except Exception as e:
            print("Error loading table:", e)
            self.driver.quit()
            return

        # 定位表格行并提取数据
        rows = self.driver.find_elements(By.XPATH,
                                         '//table[@cellpadding="0" and @cellspacing="0" and @width="100%"]/tbody/tr')
        print(f"Found {len(rows)} rows")

        for row in rows:
            cells = row.find_elements(By.TAG_NAME, 'td')

  • ForexPipeline 类用于处理从 Scrapy 爬虫获取的数据并将其存储到 MySQL 数据库中。该类定义了打开数据库连接、创建表格、插入数据,以及关闭数据库连接的过程。(只展示部分)
class ForexPipeline:
    def open_spider(self, spider):
        self.conn = mysql.connector.connect(
            host='localhost',
            user='root',
            password='123456',
            database='forex_data'
        )
        self.cursor = self.conn.cursor()
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS forex_data (
                id INT AUTO_INCREMENT PRIMARY KEY,
                Currency VARCHAR(255),
                TBP FLOAT,
                CBP FLOAT,
                TSP FLOAT,
                CSP FLOAT,
                Time TIME
            )
        ''')
        self.conn.commit()
  • ForexItem 类定义了数据结构,用于存储和管理从 Scrapy 爬虫中提取的外汇信息。
class ForexItem(scrapy.Item):
    Currency = scrapy.Field()
    TBP = scrapy.Field()
    CBP = scrapy.Field()
    TSP = scrapy.Field()
    CSP = scrapy.Field()
    Time = scrapy.Field()

运行结果:

心得体会

  • 对scrapy框架更加熟悉,非常清晰高效。
  • 对Selenium模拟浏览器进行自动抓取、自动翻页等的操作理解更加深入

遇到的问题与解决思路

数据格式处理

  • 问题:一些数据带有符号(如百分号“%”)或其他特殊字符,直接插入 MySQL 时会出错。
  • 解决方法:在 process_item 中对数据进行清洗。例如,去除多余字符,将百分比转换为浮点数,并对空数据处理成 None,确保数据格式与数据库表结构匹配。

MySQL 数据库连接异常

  • 问题:在插入数据时出现了 MySQL 连接问题,比如“Access denied for user”或“数据库管道连接失败”。
  • 解决方法:确保数据库配置正确,包括用户名、密码和数据库名称。同时,确认 MySQL 服务已启动并允许远程访问。加入异常处理,避免程序因数据库错误而中断。

数据重复与插入错误

  • 问题:有时会出现数据重复或因为主键冲突导致的插入错误。
  • 解决方法:在数据库中增加数据唯一约束,或者在抓取过程中判断数据是否已经存在,避免重复插入。对于可能的插入错误,加入异常处理逻辑,确保数据管道稳定。
posted @ 2024-11-08 11:48  你听风在吹22  阅读(5)  评论(0编辑  收藏  举报