作業1
倉庫連結:https://gitee.com/jyppx000/crawl_project
作業①【結合flask】
要求:用requests和BeautifulSoup庫方法定向爬取給定網址(http://www.shanghairanking.cn/rankings/bcur/2020)的資料,螢幕列印爬取的大學排名資訊。
1.1 程式碼和圖片
import re
import urllib.request
from bs4 import BeautifulSoup
from flask import Flask, render_template_string
class UniversityRankingScraper:
"""負責抓取網頁內容並將資料解析為HTML表格"""
def __init__(self, url):
self.url = url # 儲存排名頁面的URL
self.table_html = None # 儲存生成的HTML表格
def fetch_data(self):
"""傳送HTTP請求並使用BeautifulSoup解析HTML資料"""
response = urllib.request.urlopen(self.url)
web_content = response.read()
soup = BeautifulSoup(web_content, 'html.parser')
return soup
def generate_html_table(self, soup):
"""從HTML頁面提取排名資料,並生成一個帶有表頭的HTML表格"""
table = soup.find('table')
html_table = '<table border="1" cellspacing="0" cellpadding="5">\n'
html_table += ' <tr><th>排名</th><th>學校名稱</th><th>省市</th><th>學校型別</th><th>總分</th></tr>\n'
for row in table.find_all('tr')[1:]:
cols = row.find_all('td')
rank = cols[0].get_text(strip=True)
school_name = re.sub(r'[A-Za-z]|(雙一流|985|211)|/+', '', cols[1].get_text(strip=True)).strip()
province = cols[2].get_text(strip=True)
school_type = cols[3].get_text(strip=True)
score = cols[4].get_text(strip=True)
# 將提取到的資訊新增到HTML表格中
html_table += f' <tr><td>{rank}</td><td>{school_name}</td><td>{province}</td><td>{school_type}</td><td>{score}</td></tr>\n'
html_table += '</table>'
self.table_html = html_table
return self.table_html
def get_html_table(self):
"""生成表格方法"""
soup = self.fetch_data() # 獲取網頁內容
return self.generate_html_table(soup) # 生成並返回HTML表格
class UniversityRankingApp:
"""建立一個簡單的Flask應用,展示解析後的資料表格"""
def __init__(self, scraper):
self.scraper = scraper
self.app = Flask(__name__)
# 設定路由
@self.app.route('/')
def index():
table_html = self.scraper.get_html_table() # 獲取HTML表格
return render_template_string(table_html) # 渲染表格到網頁
def run(self):
self.app.run(debug=True)
# 入口
if __name__ == '__main__':
# 排名URL
url = "https://www.shanghairanking.cn/rankings/bcur/2021"
scraper = UniversityRankingScraper(url)
app = UniversityRankingApp(scraper)
app.run()
1.2 作業心得
- 鞏固了面對結構複雜的網頁時,如何使用標籤選擇和正規表示式進行精準的內容提取。
- 進一步提升了我的資料解析和清晰能力,如:學校名稱中的“985”“211”等標籤。
- 快速學習瞭如何將資料結構化,並透過Python將其轉化為動態HTML展示出來,特別是在Flask框架中,使用
render_template_string
直接渲染HTML。 - 進一步鞏固了我個人物件導向的能力,我透過將爬蟲邏輯和Flask應用封裝在類中,使程式碼結構清晰、易於擴充套件,同時我也是我以後寫程式碼不斷在靠攏的方向。
作業②【結合django】
要求:用requests和re庫方法設計某個商城(自已選擇)商品比價定向爬蟲,爬取該商城,以關鍵詞“書包”搜尋頁面的資料,爬取商品名稱和價格。
2.1 程式碼和圖片
```settings.py``
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"api.apps.ApiConfig" # 這裡是註冊的app
]
urls.py
urlpatterns = [
path("shop/",views.shop)
]
views.py
from django.shortcuts import render
from utils.handle import fetch_data
from utils.handle import parse_products
from utils.handle import extract_product_list
def shop(request):
"""執行爬取任務"""
url = r'https://search.dangdang.com/?key=%D3%B2%C5%CC&act=input'
data = fetch_data(url)
if data:
product_list = extract_product_list(data)
data = parse_products(product_list)
else:
data = []
return render(request,'index.html', {"data":data})
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商品比價</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th {
background-color: #f4f4f4;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
</style>
</head>
<body>
<h1>硬碟比價結果</h1>
<table>
<thead>
<tr>
<th>序號</th>
<th>價格 (¥)</th>
<th>商品名稱</th>
</tr>
</thead>
<tbody>
{% if data %}
{% for item in data %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ item.0 }}</td>
<td>{{ item.1 }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="3">暫無資料</td>
</tr>
{% endif %}
</tbody>
</table>
</body>
</html>
utils/handle.py
import re
import requests
def fetch_data(url):
"""
傳送GET請求並返回響應的HTML文字
:param url: 請求的網址
:return: 網頁的HTML內容,如果請求失敗則返回None
"""
try:
req = requests.get(url)
req.raise_for_status()
req.encoding = req.apparent_encoding
return req.text
except Exception as e:
print("Error in request:", e)
return None
def extract_product_list(data):
"""
使用正規表示式從網頁資料中提取商品列表部分
:param data: 網頁HTML內容
:return: 提取出的商品列表HTML片段
"""
match = re.search(r'<ul class="bigimg cloth_shoplist".*?>(.*?)</ul>', data, re.S)
if not match:
print("未找到商品列表")
return []
return match.group(1)
def parse_products(data):
"""
解析商品資料,提取商品的名稱和價格
:param data: 商品列表的HTML片段
:return: 包含商品資訊的列表,每個商品是一個元組(price, name)
"""
items = re.findall(r'<li.*?>(.*?)</li>', data, re.S)
products = []
for item in items:
price_match = re.search(r'<span class="price_n">¥(.*?)</span>', item)
title_match = re.search(r'title="(.*?)"', item)
if price_match and title_match:
price = price_match.group(1).strip()
name = title_match.group(1).strip()
products.append((price, name))
else:
products.append((None, None)) # 若找不到價格或名稱,新增空值
return products
2.2 作業心得
-
仍然是鞏固正則,對文字處理一個過程,當然也會存在匹配模式不準確導致無法提取資料,但是經過反覆的除錯,是能解決的
-
對處理異常更加熟練了 !
作業③:
要求:爬取一個給定網頁( https://news.fzu.edu.cn/yxfd.htm)或者自選網頁的所有JPEG和JPG格式檔案
3.1程式碼和圖片
import os
import re
import requests
from bs4 import BeautifulSoup
def create_directory(dir_path):
"""建立儲存目錄
:param dir_path: 目錄路徑
"""
if not os.path.exists(dir_path):
os.makedirs(dir_path)
def clean_filename(filename):
"""清理檔名,去掉不合法字元
:param filename: 原始檔名
:return: 清理後的檔名
"""
return re.sub(r'[<>:"/\\|?*]', '', filename) # 去除不合法字元
def download_image(url, save_path):
"""下載圖片並儲存到指定路徑
:param url: 圖片的URL地址
:param save_path: 儲存路徑
"""
try:
res = requests.get(url, 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",
})
with open(save_path, mode="wb") as fj:
fj.write(res.content)
print(f"下載成功:{save_path}")
except Exception as e:
print(f"下載失敗:{url},錯誤資訊:{e}")
def fetch_images_from_page(page_num):
"""從指定頁面獲取圖片並返回圖片連結和型別
:param page_num: 頁碼
:return: 圖片連結和檔名的元組列表
"""
response = requests.get(
url=f"https://news.fzu.edu.cn/yxfd.htm?page={page_num}",
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",
}
)
soup = BeautifulSoup(response.text, "html.parser")
img_tags = soup.find_all("img") # 找到所有圖片標籤
image_info = [] # 儲存圖片資訊的列表
for img in img_tags:
img_src = img.get("src") #
if img_src and img_src.startswith("/"): # 過濾掉不合法URL
url_path = f"https://news.fzu.edu.cn{img_src}"
file_name = clean_filename(img_src.split('/')[-1])
image_info.append((url_path, file_name))
return image_info
def save_images(image_info, jpeg_dir, other_dir):
"""根據圖片型別儲存圖片到不同目錄
:param image_info: 圖片資訊列表
:param jpeg_dir: JPEG圖片儲存目錄
:param other_dir: 其他型別圖片儲存目錄
"""
for url, file_name in image_info:
if file_name.lower().endswith(('.jpeg', '.jpg')): #
save_path = os.path.join(jpeg_dir, file_name) # JPEG檔案路徑
else:
save_path = os.path.join(other_dir, file_name) # 其他檔案路徑
download_image(url, save_path)
def main():
"""主函式,控制程式流程"""
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
JPEG_DIR = os.path.join(BASE_PATH, "jpeg_images")
OTHER_DIR = os.path.join(BASE_PATH, "other_images")
create_directory(JPEG_DIR)
create_directory(OTHER_DIR)
num_pages = int(input("請輸入要爬取的頁數:"))
for page in range(1, num_pages + 1):
print(f"開始爬取第 {page} 頁...")
image_info = fetch_images_from_page(page)
save_images(image_info, JPEG_DIR, OTHER_DIR)
print("所有頁面下載完成!")
if __name__ == "__main__":
main()
3.2 作業心得
- 再次熟悉正則和BeautifulSoup進行資料提取
- 處理檔案下載時的異常情況
- 熟悉檔案操作,os模組內建方法算是真記住了!