数据采集作业3

第三次作业

一、作业内容

作业①:

要求:指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。实现单线程和多线程的方式爬取。
–务必控制总页数(学号尾数2位)、总下载的图片数量(尾数后3位)等限制爬取的措施。
输出信息: 将下载的Url信息在控制台输出,并将下载的图片存储在images子文件中,并给出截图。

查看作业3代码:作业3代码文件夹

单线程核心代码:3.1.1.py

限制最多爬取3页,最多下载103张图片

class SingleThreadImageCrawler:
    def __init__(self, max_pages=3, max_images=103):
        self.base_url = "http://www.weather.com.cn"
        self.max_pages = max_pages
        self.max_images = max_images
        self.downloaded_images = 0
        self.visited_pages = set()
        self.downloaded_urls = set()  # 用于记录已经下载的图片URL,防止重复

使用BeautifulSoup解析HTML,找到所有标签

        soup = BeautifulSoup(response.text, 'html.parser')
        self.visited_pages.add(url)

        # 查找所有图片标签
        img_tags = soup.find_all('img')
        img_urls = []

单线程运行截图:

4

5

4

多线程核心代码:3.1.2.py

页面解析和图片下载分离,可以并行执行

    self.page_queue = queue.Queue()    # 页面URL队列
    self.image_queue = queue.Queue()   # 图片URL队列

使用锁保护共享资源

    self.lock = threading.Lock()       # 线程锁

线程管理和协调

# 启动页面爬取线程
    page_threads = []
    for i in range(min(self.num_threads, 2)):  # 使用较少的线程进行页面爬取
        thread = threading.Thread(target=self.page_crawler)
        thread.daemon = True  # 守护线程,主线程结束时自动退出
        thread.start()
        page_threads.append(thread)

    # 等待页面队列处理完成
    self.page_queue.join()

    # 启动图片下载线程
    download_threads = []
    for i in range(self.num_threads):
        thread = threading.Thread(target=self.image_downloader)
        thread.daemon = True
        thread.start()
        download_threads.append(thread)

    # 等待图片队列处理完成
    self.image_queue.join()

多线程运行截图:

4

5

6

心得体会

多线程通过并行处理,显著减少总执行时间,比单线程要快

作业②

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

查看作业3代码:作业3代码文件夹

核心代码:stock_spider

items.py定义爬虫要提取的数据字段,后续可以通过Pipeline进行各种处理操作

class StockSpiderItem(scrapy.Item):
    # 定义数据字段
    stock_code = scrapy.Field()      # 股票代码
    stock_name = scrapy.Field()      # 股票名称
    latest_price = scrapy.Field()    # 最新报价
    change_percent = scrapy.Field()  # 涨跌幅
    change_amount = scrapy.Field()   # 涨跌额
    volume = scrapy.Field()          # 成交量(手)
    turnover = scrapy.Field()        # 成交额
    amplitude = scrapy.Field()       # 振幅(%)
    high = scrapy.Field()            # 最高
    low = scrapy.Field()             # 最低
    open_price = scrapy.Field()      # 今开
    close_price = scrapy.Field()     # 昨收
    crawl_time = scrapy.Field()      # 爬取时间

Pipeline用于数据清理,存储.这里将Item数据序列化到数据库

def process_item(self, item, spider):
    if not hasattr(self, 'connection') or not self.connection:
        spider.logger.error("数据库连接不可用")
        return item

    try:
        insert_sql = """
        INSERT INTO stock_data 
        (stock_code, stock_name, latest_price, change_percent, change_amount, 
         volume, turnover, amplitude, high, low, open_price, close_price, crawl_time)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        ON DUPLICATE KEY UPDATE
        latest_price=VALUES(latest_price),
        change_percent=VALUES(change_percent),
        change_amount=VALUES(change_amount),
        volume=VALUES(volume),
        turnover=VALUES(turnover),
        amplitude=VALUES(amplitude),
        high=VALUES(high),
        low=VALUES(low),
        open_price=VALUES(open_price),
        close_price=VALUES(close_price)
        """

        adapter = ItemAdapter(item)
        values = (
            adapter.get('stock_code', ''),
            adapter.get('stock_name', ''),
            adapter.get('latest_price', 0),
            adapter.get('change_percent', 0),
            adapter.get('change_amount', 0),
            adapter.get('volume', ''),
            adapter.get('turnover', ''),
            adapter.get('amplitude', ''),
            adapter.get('high', 0),
            adapter.get('low', 0),
            adapter.get('open_price', 0),
            adapter.get('close_price', 0),
            adapter.get('crawl_time', '')
        )

        self.cursor.execute(insert_sql, values)
        self.connection.commit()
        spider.logger.info(f"成功存储股票数据: {adapter.get('stock_code')} - {adapter.get('stock_name')}")

    except Exception as e:
        spider.logger.error(f"插入数据失败: {str(e)}")
        if hasattr(self, 'connection'):
            self.connection.rollback()

    return item

运行截图:

7

8

9
查看总记录数

8
查看前10条记录

9

心得体会:

学会了使用mysql存储数据,以及使用spider框架进行爬虫。并且对Scrapy的结构更加清晰。

作业③:

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

查看作业3代码:作业3代码文件夹

核心代码:forex_crawler

1、根据页面结构分析
8

9
2、跳过表头行,只获取数据行

rows = response.xpath('//div[@class="publish"]//tr[position()>1]')

3、逐字段提取

for row in rows:
    # 检查是否是数据行(有8个td)
    tds = row.xpath('./td')
    if len(tds) == 8:
        item = ForexItem()
        
        # 使用XPath提取每个字段
        item['currency_name'] = self.extract_text(tds[0])  # 第1个td:货币名称
        item['tbp'] = self.extract_text(tds[1])           # 第2个td:现汇买入价
        item['cbp'] = self.extract_text(tds[2])           # 第3个td:现钞买入价
        item['tsp'] = self.extract_text(tds[3])           # 第4个td:现汇卖出价
        item['csp'] = self.extract_text(tds[4])           # 第5个td:现钞卖出价
        item['middle_rate'] = self.extract_text(tds[5])   # 第6个td:中行折算价
        item['publish_date'] = self.extract_text(tds[6])  # 第7个td:发布日期
        item['publish_time'] = self.extract_text(tds[7])  # 第8个td:发布时间

4、由于输出结果只需要6个字段,所有在 pipelines.py 的数据库插入语句中,只选择了需要的6个字段

insert_sql = """
INSERT INTO forex_data 
(currency_name, tbp, cbp, tsp, csp, publish_time)  -- 这里只有6个字段
VALUES (%s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
tbp=VALUES(tbp),
cbp=VALUES(cbp),
tsp=VALUES(tsp),
csp=VALUES(csp),
publish_time=VALUES(publish_time)
"""

运行结果:

8

9

心得体会:

学会使用XPath定位到包含数据的表格行,并逐个提取每个单元格的数据。并且通过Pipeline在爬取过程中实时存储数据

posted @ 2025-11-22 13:58  yeeopuuo  阅读(9)  评论(0)    收藏  举报