数据采集与融合技术作业一

作业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">&yen;(.*?)</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模块内置方法算是真记住了!
posted @ 2024-10-15 23:56  清风拂山岗(小高同学)  阅读(14)  评论(0编辑  收藏  举报