数据采集实践第三次作业

作业①

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

1.作业内容

点击查看代码
from bs4 import BeautifulSoup
from bs4 import UnicodeDammit
import urllib.request
import sqlite3

class WeatherDB:
    def openDB(self):
        self.con=sqlite3.connect("weathers.db")
        self.cursor=self.con.cursor()
        try:
            self.cursor.execute("create table weathers (wCity varchar(16),wDate varchar(16),wWeather varchar(64),wTemp varchar(32),constraint pk_weather primary key (wCity,wDate))")
        except:
            self.cursor.execute("delete from weathers")

    def closeDB(self):
        self.con.commit()
        self.con.close()

    def insert(self, city, date, weather, temp):
        pass


def insert(self, city, date, weather, temp):
    try:
        self.cursor.execute("insert into weathers (wCity,wDate,wWeather,wTemp) values (?,?,?,?)",
                            (city, date, weather, temp))
    except Exception as err:
        print(err)

    def show(self):
        self.cursor.execute("select * from weathers")
        rows = self.cursor.fetchall()
        print("%-16s%-16s%-32s%-16s" % ("city", "date", "weather", "temp"))
        for row in rows:
            print("%-16s%-16s%-32s%-16s" % (row[0], row[1], row[2], row[3]))


class WeatherForecast:
    def __init__(self):
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre"}
        self.cityCode = {"北京": "101010100", "上海": "101020100", "广州": "101280101", "深圳": "101280601"}

    def forecastCity(self, city):
        if city not in self.cityCode.keys():
            print(city + " code cannot be found")
            return

        url = "http://www.weather.com.cn/weather/" + self.cityCode[city] + ".shtml"
        try:
            req = urllib.request.Request(url, headers=self.headers)
            data = urllib.request.urlopen(req)
            data = data.read()
            dammit = UnicodeDammit(data, ["utf-8", "gbk"])
            data = dammit.unicode_markup
            soup = BeautifulSoup(data, "lxml")
            lis = soup.select("ul[class='t clearfix'] li")
            for li in lis:
                try:
                    date=li.select('h1')[0].text
                    weather=li.select('p[class="wea"]')[0].text
                    temp_span = li.select('p[class="tem"] span')
                    temp_i = li.select('p[class="tem"] i')
                    temp = (temp_span[0].text if temp_span else '') + "/" + (temp_i[0].text if temp_i else '')
                    print(city,date,weather,temp)
                    self.db.insert(city,date,weather,temp)
                except Exception as err:
                    print(err)
        except Exception as err:
            print(err)


    def process(self, cities):
        self.db = WeatherDB()
        self.db.openDB()


        for city in cities:
            self.forecastCity(city)

        # self.db.show()
        self.db.closeDB()

ws = WeatherForecast()
ws.process(["北京", "上海", "广州", "深圳"])
print("completed")

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.心得体会

我使用requests和BeautifulSoup库来爬取股票数据,并将其存储在SQLite数据库中。这次经历让我深刻体会到网络爬虫的复杂性和数据处理的重要性。编写爬虫时,我必须仔细分析网页结构,并编写能够适应变化的代码。处理数据时,我学会了如何高效地解析和清洗数据,并将其存储在数据库中,以便后续的查询和分析。此外,实验还让我意识到异常处理和日志记录的重要性,它们对于调试和维护爬虫程序至关重要。

作业③

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

1.作业内容

点击查看代码
import urllib.request
from bs4 import BeautifulSoup
import sqlite3

# 目标网址
url = "https://www.shanghairanking.cn/rankings/bcur/2021"

# 使用BeautifulSoup解析HTML内容
resp = urllib.request.urlopen(url)
html = resp.read()
soup = BeautifulSoup(html, 'html.parser')

# 找到包含排名信息的表格行
rows = soup.find_all('tr')

# 打印标题
print(f"排名\t学校名称{'':<6}\t省市{'':<1}\t学校类型\t总分")

# 连接到SQLite数据库(如果不存在则创建)
conn = sqlite3.connect('rank2021.db')
cursor = conn.cursor()

# 创建表
cursor.execute('''
CREATE TABLE IF NOT EXISTS university_rank (
    rank TEXT,
    uni_name TEXT,
    province TEXT,
    uni_type TEXT,
    score TEXT
)
''')

# 遍历表格的每一行,提取信息并保存到数据库
for row in rows:
    # 找到排名
    rank = row.find('td').text.strip() if row.find('td') else ""

    # 找到学校名称的span
    span = row.find('span', class_="name-cn")
    uni_name = span.text.strip() if span else ""

    # 找到剩余内容
    province = row.find_all('td')[2].text.strip() if row.find('td') else ""
    uni_type = row.find_all('td')[3].text.strip() if row.find('td') else ""
    score = row.find_all('td')[4].text.strip() if row.find('td') else ""

    # 打印提取的信息
    print(f"{rank}\t{uni_name:<10}\t{province:<3}\t{uni_type:<4}\t{score}")

    # 插入数据到数据库
    cursor.execute('''
    INSERT INTO university_rank (rank, uni_name, province, uni_type, score)
    VALUES (?, ?, ?, ?, ?)
    ''', (rank, uni_name, province, uni_type, score))

# 提交事务
conn.commit()

# 关闭数据库连接
conn.close()

2.心得体会

本次实验中我分析了目标网页的结构,确定了需要爬取的数据字段,包括学校排名、名称、类型、所在地、总分以及各个评价指标等。然后,我编写了爬虫脚本,,提取所需的数据。在数据提取过程中,我特别注意了数据的清洗和转换,确保数据的准确性和一致性。我将提取的数据存储到SQLite数据库中。创建了一个数据库和相应的表结构,并编写了插入数据的SQL语句。在数据存储过程中,我同样注重了数据的完整性和错误处理,确保即使在遇到异常情况时,程序也能优雅地处理错误并继续执行。为了记录整个分析过程,我使用了浏览器的开发者工具(F12)来调试和查看网络请求。我录制了分析过程,并使用工具将录制的视频转换为GIF图片。这样不仅方便了博客的读者理解整个爬虫的工作流程,也使得博客内容更加生动和直观。通过这次实验,我深刻体会到了网络爬虫技术在数据获取中的重要性,以及数据处理和存储的复杂性。我学会了如何分析网页结构,编写高效的爬虫脚本,以及如何将数据存储到数据库中。此外,我也认识到了数据清洗和错误处理的重要性,它们是保证数据质量和程序稳定性的关键。

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