作業①
要求:指定一個網站,爬取這個網站中的所有的所有圖片,例如:中國氣象網(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圖片。這樣不僅方便了部落格的讀者理解整個爬蟲的工作流程,也使得部落格內容更加生動和直觀。透過這次實驗,我深刻體會到了網路爬蟲技術在資料獲取中的重要性,以及資料處理和儲存的複雜性。我學會了如何分析網頁結構,編寫高效的爬蟲指令碼,以及如何將資料儲存到資料庫中。此外,我也認識到了資料清洗和錯誤處理的重要性,它們是保證資料質量和程式穩定性的關鍵。