数据采集实践第三次作业

Gitee作业链接:
https://gitee.com/FunkTank/crawl_project/tree/master/作业3

作业①

要求:指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。使用scrapy框架分别实现单线程和多线程的方式爬取。
–务必控制总页数(学号尾数2位)、总下载的图片数量(尾数后3位)等限制爬取的措施。

1.作业内容

spider代码
import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem

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

    def parse(self, response):
        # 提取所有图片的src属性
        images = response.css('img::attr(src)').getall()
        for img in images:
            if img.startswith('http'):
                yield {'image_urls': [img]}
            else:
                # 处理相对链接
                absolute_link = response.urljoin(img)
                yield {'image_urls': [absolute_link]}

class WeatherImagesPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            yield scrapy.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("Item contains no images")
        item['image_paths'] = image_paths
        return item

运行结果:

2.心得体会

在处理网页数据抓取任务时,我们不可避免地会面临网页结构变化带来的挑战。这次实验让我深刻认识到编写灵活且健壮的代码的重要性,尤其是在面对可能随时变化的网页结构时。实验初期,我们遇到了由于HTML结构修改导致的选择器失效问题,特别是对于温度信息的提取部分。这不仅突显了网页结构的不稳定性,也强调了选择器设计的灵活性。在编写选择器时,我们应尽量避免硬编码具体的标签或类名,而是采用更通用的选择器或结合多个选择器来提高匹配的准确性。

作业②

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

1.作业内容

点击查看代码
import requests
import sqlite3
import json
import logging

# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 数据库操作类
class StockDatabase:
    def __init__(self, db_name):
        self.conn = sqlite3.connect(db_name)
        self.create_table()
        logging.info(f"Database '{db_name}' connected.")

    def create_table(self):
        """创建存储股票信息的表格"""
        c = self.conn.cursor()
        c.execute('''
            CREATE TABLE IF NOT EXISTS stock_info (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                stock_code TEXT,
                stock_name TEXT,
                latest_price REAL,
                change_percent REAL,
                change_amount REAL,
                volume TEXT,
                turnover TEXT,
                amplitude REAL,
                high REAL,
                low REAL,
                open_price REAL,
                yesterday_close REAL
            )
        ''')
        self.conn.commit()
        logging.info("Table 'stock_info' is ready.")

    def save_stock_data(self, stock_data):
        """将股票数据保存到数据库"""
        c = self.conn.cursor()
        for stock in stock_data:
            stock_record = (
                stock.get('f12'),  # 股票代码
                stock.get('f14'),  # 股票名称
                stock.get('f2'),   # 最新报价
                stock.get('f3'),   # 涨跌幅
                stock.get('f4'),   # 涨跌额
                stock.get('f5'),   # 成交量
                stock.get('f6'),   # 成交额
                stock.get('f7'),   # 振幅
                stock.get('f15'),  # 最高价
                stock.get('f16'),  # 最低价
                stock.get('f17'),  # 今开
                stock.get('f18')   # 昨收
            )
            try:
                c.execute('''
                    INSERT INTO stock_info 
                    (stock_code, stock_name, latest_price, change_percent, change_amount, volume, turnover, amplitude, high, low, open_price, yesterday_close)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ''', stock_record)
                logging.info(f"Stock data for '{stock.get('f14')}' saved.")
            except Exception as e:
                logging.error(f"Error saving stock data: {e}")
        self.conn.commit()

    def display_stock_data(self):
        """显示数据库中的股票数据"""
        c = self.conn.cursor()
        c.execute("SELECT * FROM stock_info")
        rows = c.fetchall()

        # 打印表头
        print(f"{'序号':<5} {'股票代码':<10} {'股票名称':<10} {'最新报价':<10} {'涨跌幅':<10} {'涨跌额':<10} {'成交量':<10} {'成交额':<15} {'振幅':<10} {'最高':<10} {'最低':<10} {'今开':<10} {'昨收':<10}")

        # 打印每行数据
        for row in rows:
            latest_price = float(row[3]) if row[3] not in ('-', None) else 0.0
            change_percent = float(row[4]) if row[4] not in ('-', None) else 0.0
            change_amount = float(row[5]) if row[5] not in ('-', None) else 0.0
            amplitude = float(row[8]) if row[8] not in ('-', None) else 0.0
            high = float(row[9]) if row[9] not in ('-', None) else 0.0
            low = float(row[10]) if row[10] not in ('-', None) else 0.0
            open_price = float(row[11]) if row[11] not in ('-', None) else 0.0
            yesterday_close = float(row[12]) if row[12] not in ('-', None) else 0.0

            print(f"{row[0]:<5} {row[1]:<10} {row[2]:<10} "
                  f"{latest_price:<10.2f} {change_percent:<10.2f} {change_amount:<10.2f} "
                  f"{row[6]:<10} {row[7]:<15} {amplitude:<10.2f} {high:<10.2f} "
                  f"{low:<10.2f} {open_price:<10.2f} {yesterday_close:<10.2f}")

    def close_connection(self):
        """关闭数据库连接"""
        self.conn.close()
        logging.info("Database connection closed.")

# 获取股票数据
def get_stock_data():
    url = 'https://push2.eastmoney.com/api/qt/clist/get?cb=jQuery112409840494931556277_1633338445629&pn=1&pz=10&po=1&np=1&fltt=2&invt=2&fid=f3&fs=b:MK0021&fields=f12,f14,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f18,f15,f16,f17,f23'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0'
    }
    response = requests.get(url, headers=headers)

    # 去除不必要的字符,提取有效的JSON部分
    response_text = response.text.split('(', 1)[1].rsplit(')', 1)[0]

    stock_data = json.loads(response_text)['data']['diff']  # 解析JSON并提取有用的字段
    return stock_data

# 主函数
def main():
    # 创建数据库连接
    db = StockDatabase('eastmoney_stock.db')

    # 获取股票数据
    stock_data = get_stock_data()

    # 保存数据到数据库
    db.save_stock_data(stock_data)

    # 显示表格数据
    db.display_stock_data()

    # 关闭数据库连接
    db.close_connection()

if __name__ == '__main__':
    main()

运行结果:

2.心得体会

通过本次作业,我深入掌握了Scrapy中Item和Pipeline的使用,实现了数据的序列化输出。利用XPath解析网页内容,我成功爬取了股票相关信息,并将其存储到MySQL数据库中。整个过程让我熟悉了Scrapy框架的数据处理流程,提升了数据抓取、解析和存储的技能,深刻体会到数据管道在项目中的重要性,为后续数据处理打下了坚实基础。

作业③

要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取外汇网站数据。

1.作业内容

spider代码
import scrapy
from boc_exchange_rates.items import BocExchangeRatesItem


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

    def parse(self, response):
        data = response.body.decode()
        selector = scrapy.Selector(text=data)
        data_lists = selector.xpath('//table[@align="left"]/tr')
        for row in data_lists:
            item = BocExchangeRatesItem()
            item['currency'] = row.xpath('./td[1]/text()').get()
            item['cash_buy'] = row.xpath('./td[2]/text()').get()
            item['cash_sell'] = row.xpath('./td[3]/text()').get()
            item['spot_buy'] = row.xpath('./td[4]/text()').get()
            item['spot_sell'] = row.xpath('./td[5]/text()').get()
            item['bank_rate'] = row.xpath('./td[6]/text()').get()
            yield item

运行结果:

2.心得体会

本次作业中,我熟练掌握了Scrapy框架中Item和Pipeline的数据序列化输出方法。通过结合XPath解析,我成功从外汇网站抓取了所需数据,并利用Scrapy的Pipeline将数据存储到MySQL数据库中。整个过程让我深刻理解了数据抓取、清洗、存储的完整流程,提升了数据处理能力,并为后续大数据分析奠定了基础。。

posted @ 2024-11-26 01:55  FungusTanion  阅读(37)  评论(0)    收藏  举报