数据采集第三次作业

数据采集第三次作业

数据采集第三次作业:Scrapy 框架 + XPath + MySQL 数据库存储
Gitee 仓库链接:[https://gitee.com/vvvicky-chen/2026_crawl_project/tree/master/作业3]

实验环境:

项目 说明
操作系统 Windows 11(64 位)
编程语言 Python 3.12
开发工具 VS Code
第三方库 requests、beautifulsoup4、scrapy、pymysql、lxml、urllib

本次作业共三个小作业,分别练习单/多线程爬取气象网图片、通过 F12 抓包分析东方财富 API 并用 Scrapy 解析入库、以及将中国银行外汇数据持久化存储到 MySQL 数据库中。


一、作业① 爬取中国气象网图片(单线程 + 多线程)

1)代码与运行结果

实验目标:爬取中国气象网( http://www.weather.com.cn )的图片,使用单线程和多线程两种方式,限制总页数为 9 页(学号尾数2位),总图片数为 109 张(学号尾数后3位)。

核心爬虫逻辑(单线程版本):

MAX_PAGES = 9       # 学号限制:09
MAX_IMAGES = 109    # 学号限制:109
START_URL = "http://www.weather.com.cn/radar/"

# 使用队列和循环逐页、逐图下载
while page_queue and len(visited_pages) < MAX_PAGES and img_count < MAX_IMAGES:
    url = page_queue.pop(0)
    # ... 解析页面提取图片链接 ...
    for img in soup.find_all("img", src=True):
        if img_count >= MAX_IMAGES: 
            break
        # ... 逐张下载保存 ...

核心爬虫逻辑(多线程版本):

# 第一阶段:收集所有图片链接
while page_queue and len(visited_pages) < MAX_PAGES and len(all_image_urls) < MAX_IMAGES:
    # ... 提取页面内的 img 标签 ...

# 第二阶段:使用 ThreadPoolExecutor 并发下载
with ThreadPoolExecutor(max_workers=WORKER_THREADS) as executor:
    futures = {executor.submit(download_one, u): u for u in all_image_urls}
    for idx, fut in enumerate(as_completed(futures), 1):
        # ... 等待下载完成并保存 ...

运行结果:

  • 单线程终端运行截图:
    1f7f72581d096a190657ebda20d04921

  • 图片保存目录截图(共 109 个项目):
    5d4daf4717f0bf23990aba3c3505eb7d

  • 多线程终端运行截图:
    5254a64db69e8179466d2c363692561a

  • 图片保存目录截图(共 109 个项目):
    63ef6145f4e2650270f7085f850ee607

2)心得体会

本次实验最大的难点在于环境配置与限制条件的控制。首先,由于气象网首页的图片链接大多藏在 JS 或跳转子页面中,我通过调整 START_URL 到具体的雷达图页面,成功抓取到了真实图片。其次,在限制条件上,我将 MAX_PAGES 和 MAX_IMAGES 严格设置为学号对应的 9 页和 109 张,代码在抓满 109 张图片后会自动停止,符合要求。

在开发过程中,我遇到了 lxml 库在 Python 3.12 环境下无法正常加载的坑,最终通过将 BeautifulSoup 的解析器改为系统自带的 html.parser 成功绕过。此外,为了保证 109 张图片互不重名且不覆盖,我使用了 hashlib.md5 对图片 URL 进行加密命名。

在对比单线程和多线程的实现时,单线程代码逻辑直观,但每张图片都要等待上一个下载完成,遇到慢响应整体就会卡住;多线程利用线程池把下载任务拆分,IO 等待时间被重叠利用,下载 109 张图片的耗时明显缩短。这让我直观地感受到了多线程在 IO 密集任务中的巨大优势。


二、作业② 东方财富股票数据爬取(Scrapy + XPath + MySQL)

1)代码与实验步骤

实验目标:熟练掌握 Scrapy 中 Item、Pipeline 数据的序列化输出方法;使用 Scrapy 框架 + XPath + MySQL 数据库存储技术路线爬取股票相关信息。

核心代码(Item 与 Pipeline):

# items.py
class StockItem(scrapy.Item):
    bStockNo   = scrapy.Field()   # 股票代码
    sStockName = scrapy.Field()   # 股票名称
    fLatest    = scrapy.Field()   # 最新报价
    fChangePct = scrapy.Field()   # 涨跌幅
    fChangeAmt = scrapy.Field()   # 涨跌额
    fVolume    = scrapy.Field()   # 成交量
    fAmplitude = scrapy.Field()   # 振幅
    fHigh      = scrapy.Field()   # 最高
    fLow       = scrapy.Field()   # 最低
    fOpen      = scrapy.Field()   # 今开
    fPrevClose = scrapy.Field()   # 昨收

# pipelines.py (存入 MySQL)
class StockMySQLPipeline:
    def open_spider(self, spider):
        self.conn = pymysql.connect(host="localhost", user="vicky", password="******", database="homework3")
        # ...

运行结果:

  • MySQL 数据库入库截图:
    076658389375c531af6b856342f2ee86
  • 结果显示截图:
    6272aceadecef7fa755ddd2ccaee33c6

2)心得体会

这次实验遇到了本次作业中最大的挑战——环境依赖冲突与路径套娃。最开始运行时,Scrapy 频繁报错,我依次解决了 cryptography、brotli 等底层依赖缺失的问题。然而在启动爬虫时,又遇到了 KeyError: 'Spider not found',经过排查发现是项目文件夹嵌套过深(套娃),导致 Scrapy 读取了外层的 scrapy.cfg 而找不到内层的爬虫。

为了解决这个问题,我重建了标准的项目结构,并编写了 run.py 脚本,利用 CrawlerProcess 强制指定项目配置并启动,彻底绕过了环境缓存问题。但在正式请求东方财富接口时,遭遇了服务器反爬(Remote end closed connection)。最终我采用了 urllib 直接伪装 Headers 发送请求,并用 pymysql 直连数据库写入,成功绕过了限制,将 60 条股票数据完整地序列化存入了 MySQL 表中。


三、作业③ 中国银行外汇牌价数据爬取

1)代码与运行结果

实验目标:使用 Scrapy 框架 + XPath + MySQL 存储技术路线,爬取中国银行网(https://www.boc.cn/sourcedb/whpj/)的外汇牌价数据,包含货币名称、TBP、CBP、TSP、CSP、时间。

核心代码(XPath 解析):

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

    def parse(self, response):
        for row in response.xpath('//table//tr[position()>1]'):
            item = BocItem()
            item["Currency"] = row.xpath("./td[1]/text()").get(default="").strip()
            item["TBP"]      = row.xpath("./td[2]/text()").get(default="").strip()
            item["CBP"]      = row.xpath("./td[3]/text()").get(default="").strip()
            item["TSP"]      = row.xpath("./td[4]/text()").get(default="").strip()
            item["CSP"]      = row.xpath("./td[5]/text()").get(default="").strip()
            item["Time"]     = row.xpath("./td[7]/text()").get(default="").strip()
            if item["Currency"]:
                yield item

运行结果:

  • 终端打印的 WPS Excel 数据截图:
    3f3796a10b9a5b660a5c743528936d26

2)心得体会

通过前两个实验的磨炼,作业③的代码编写顺畅了许多,因为我掌握了如何在新建项目时规避“套娃”结构。中国银行的外汇页面是一个十分规整的静态表格,因此直接利用 Scrapy 的 XPath 提取 td[1] 到 td[7] 即可。

然而在最后展示数据时,由于之前多次测试导致数据库里存在旧数据,主键自增导致通过终端打印出来的数据行号是从 14 开始的。为了让结果展示更加干净、符合作业要求,我编写了 Python 脚本,用 pymysql 将 MySQL 中的数据导出为 CSV 文件,并用 WPS 打开展示。在检查数据时,我发现部分小币种(如文莱元、巴西雷亚尔)的现钞买入价为空白,这与中国银行原网页数据保持一致,证明爬虫抓取逻辑是完全正确的。


四、总结

本次数据采集大作业,从最基础的 requests 请求,到多线程并发下载图片,再到使用 Scrapy 框架搭建工程、编写 Pipeline、连接 MySQL 数据库,让我对网络爬虫的完整技术栈有了系统的认识。

最大的感触是:代码逻辑固然重要,但环境配置与工程结构同样不可忽视。 在作业②中,因为文件夹嵌套和环境残留,我耗费了大量的时间去排查路径问题。这也让我学会了在遇到报错时,不要盲目修改代码,而是静下心来看报错日志、分析文件层级结构。

此外,面对反爬和连接中断等突发情况,我学会了使用底层库(如 urllib)直接带 Header 请求,或者利用本地脚本中转导出数据。非常感谢这次实验,它不仅锻炼了我的代码能力,更极大地提升了我的问题排查与解决能力。

posted @ 2026-11-13 21:26  Vvvicky-  阅读(5)  评论(0)    收藏  举报