之前项目我们进行了简单的数据爬取,数据存储(json,excel,mysql),基本不涉及数据分析和数据处理

项目分析

本次项目,我们需完成我校学生期中期末考试成绩爬取。存储到我们本地数据库中,并对班级成绩,学生个人成绩进行分析。最终形成分析图表。

关于需要用户登录的网站的数据爬取:

网页请求是如何区分用户登录或具体哪个用户的:

  1. cookies

  2. 请求头的authorization

    本项目针对对最为常见的cookies方式

之前新发地项目的数据爬取较为简单,没有登录也没有防护。

本次爬取的数据网站为学校管理平台,需要登录,具有较好的防护。

爬虫解决登录问题的思路有两个:

  1. Selenium库 实现登录(本项目只做演示,具体操作下个项目实施)

  2. requests库 加载cookie (本项目使用)

基本框架

import requests
#网址
url = "https://xxxxxx"
#请求头
headers = {
    "User-Agent": "xxxxxxx",
    }
#cookies
cookies = {
    "ASP.NET_SessionId": "xxxxxxxxxxxxxxxxxxxx",
    }
#参数
data = {
    "key": "value",
    }  
try:
    response = requests.post(url, headers=headers, data=data, cookies=cookies)
    response.raise_for_status()
    print(response.text)  
    
except requests.HTTPError as http_err:
    print(f"网页请求出错,错误代码: {http_err}")
except Exception as err:
    print(f"出现错误: {err}")

'''
Author: xiandaidl 694060435@qq.com
Date: 2024-03-03 20:32:18
LastEditors: xiandaidl 694060435@qq.com
LastEditTime: 2024-03-03 20:58:04
FilePath: \测试\测试.py
Description: 这是网页爬虫请求的典型代码,在实际使用中,请将url,headers,cookies进行替换。
'''
import requests

'''
description: 用于向url请求数据
return {返回json,如果请求失败返回None}
'''
def fetch_data(url,headers,cookies,data):
    try:
        response = requests.post(url, headers=headers, data=data, cookies=cookies)
        response.raise_for_status()
        return response.json()
    except requests.HTTPError as http_err:
        print(f"网页请求出错,错误代码: {http_err}")
    except Exception as err:
        print(f"出现错误: {err}")
    

url = "https://xxxxxx"
headers = {
    "User-Agent": "xxxxxxx",
    }
cookies = {
    "ASP.NET_SessionId": "xxxxxxxxxxxxxxxxxxxx",
    }
data = {
    "key": "value",
    }  

res_json=fetch_data(url,headers,cookies,data)
if res_json!=None:
    print(res_json)

关于请求头

请求头(Request Headers)是在HTTP请求中包含的元数据信息,它们提供了关于请求的附加信息,用于帮助服务器理解和处理请求。请求头通常由键值对组成,每个键值对包含一个标头名称和一个与之相关联的值。

举例:

headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
        "Accept": "application/json, text/javascript, /; q=0.01",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "X-Requested-With": "XMLHttpRequest",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin"
        }
  1. User-Agent:指定了浏览器或客户端的用户代理字符串。它告诉服务器请求的客户端是什么类型的设备,以及使用了哪个浏览器版本。

  2. Accept:指定了客户端可以接受的内容类型。在这个例子中,客户端表示它可以接受JSON、JavaScript等类型的响应。

  3. Accept-Language:指定了客户端首选的自然语言。这个头部信息告诉服务器客户端喜欢接收哪些语言的响应。

  4. Content-Type:指定了请求主体的类型。在这个例子中,客户端要发送的请求主体是表单数据(x-www-form-urlencoded),并且使用UTF-8字符编码。

  5. X-Requested-With:通常用于标识Ajax请求。在这个例子中,客户端使用XMLHttpRequest对象发送请求。

  6. Sec-Fetch-DestSec-Fetch-ModeSec-Fetch-Site:这些是安全性相关的头部信息,用于指示请求的目标、模式和来源。它们通常由浏览器自动添加,以确保请求的安全性。

在这些请求头中,有几个与防爬虫相关的信息:

  1. User-Agent:用户代理字符串是最常见的用于识别爬虫的方法之一。网站可以通过检查User-Agent来确定请求是否来自于一个正常的浏览器,而不是爬虫程序。为了避免被识别为爬虫,有时需要将User-Agent设置成一个类似真实浏览器的字符串,以减少被屏蔽的风险。

  2. X-Requested-With:这个头部信息通常用于标识Ajax请求,但也可以被用来检测爬虫行为。有些网站可能会检查这个头部信息来确定请求是否来自于正常的浏览器,因为大多数爬虫不会发送这个头部信息。

  3. Sec-Fetch-DestSec-Fetch-ModeSec-Fetch-Site:这些头部信息是用于安全性的,但它们也可以被用来检测爬虫。例如,通过检查Sec-Fetch-Mode是否为"navigate",网站可以判断请求是否是用户在浏览器中手动发起的,而不是由爬虫自动发起的。

通常情况下,建议:请求头的内容和实际请求完全一致
通常使用浏览器的F12复制fetch来实现

关于Cookies

Cookies是存储在用户计算机上的小型文本文件,它们包含着网站对用户的一些信息或状态。这些信息通常用于跟踪用户的活动、存储用户的偏好设置、实现用户认证等。当用户访问一个网站时,服务器可以通过HTTP响应头将一个或多个Cookie发送给用户的浏览器,浏览器会将这些Cookie保存起来。然后,在用户后续的访问中,浏览器会将这些Cookie通过HTTP请求头发送回服务器。

举例:

 cookies = {
        "ASP.NET_SessionId": "2aqltgjqdwm5fjh0xd1hfz1q",
        "JSESSIONID": "C2F1FDFE83E167D67BFAC4040655FB08",
        }
  1. ASP.NET_SessionId:这是一个ASP.NET应用程序使用的Session ID。Session ID是用来标识用户会话的唯一标识符,它通常用于跟踪用户的会话状态。在这个例子中,ASP.NET应用程序将使用这个Session ID来识别特定用户的会话,并在会话之间共享状态信息。

  2. JSESSIONID:这是一个Java应用程序使用的Session ID。类似于ASP.NET的Session ID,JSESSIONID也是用来唯一标识用户会话的标识符。在这个例子中,Java应用程序将使用这个Session ID来管理用户的会话状态。

异常处理机制

Python的异常处理机制允许程序员在代码中处理和管理可能出现的异常情况,从而增加程序的稳定性和健壮性。

举例:

实际使用中不用写那么多

try:
    # 可能会引发异常的代码块
    # ...
except ValueError:
    # 处理ValueError异常的代码块,类型错误
    # ...
except TypeError:
    # 处理TypeError异常的代码块,数值错误
    # ...
except Exception as e:
    # 处理其他类型异常的代码块
    # ...
finally:
    # 最终执行的清理代码块
    # ...
try:
    # 可能会引发异常的代码块
    # ...
except Exception as e:
    # 异常处理代码块
    # ...

以下是Python异常处理机制的基本要点:

  1. 异常抛出:在程序执行过程中,如果发生了错误或异常情况,Python会抛出一个异常对象。异常可以是Python内置的异常类,也可以是自定义的异常类。常见的异常包括TypeErrorValueError等。

  2. try语句:程序员可以使用try语句来捕获可能会引发异常的代码块。try语句包含了可能会引发异常的代码,以及一个或多个except子句用于处理不同类型的异常。

  3. except子句except子句用于捕获和处理try代码块中引发的异常。每个except子句可以指定要捕获的异常类型,以及处理该异常的代码块。

  4. finally子句finally子句可选,用于指定无论是否引发异常都要执行的代码。通常用于执行清理操作,比如关闭文件或释放资源。

典型使用场景

  1. 访问网页时

为了避免请求失败导致的程序崩溃,我们可以使用以下代码

import requests

url = "http://abc.baidu.com/asdasd"

try:
    response = requests.get(url)
    response.raise_for_status()
    print(response.text)

except requests.HTTPError as http_err:
    print(f"网页请求出错,错误代码: {http_err}")
except Exception as err:
    print(f"出现错误: {err}")

print("程序继续执行")
import requests

url = "http://abc.baidu.com/asdasd"
response = requests.get(url)
print(response.text)

print("程序继续执行")

其中的response.raise_for_status()requests库中的一个方法,用于检查发送的HTTP请求的响应是否包含了一个错误状态码。如果响应的状态码表示请求失败(即不在200-299范围内),这个方法会抛出一个HTTPError异常,其中包含了响应的详细信息,如错误码、错误原因等。

  1. 访问数据库时

为了避免数据库连接失败,或sql语句错误,或sql语句执行失败,导致的程序崩溃

我们可以使用以下代码

import pymysql
from pymysql import MySQLError

try:
    # 连接数据库
    connection = pymysql.connect(host='localhost',
                                 user='username',
                                 password='password',
                                 database='database_name',
                                 charset='utf8mb4',
                                 cursorclass=pymysql.cursors.DictCursor)
    # 创建游标对象
    cursor = connection.cursor()
    # 执行数据库操作
    cursor.execute("SELECT * FROM table_name")
    # 获取查询结果
    result = cursor.fetchall()
    # 输出结果
    print(result)

except MySQLError as e:
    # 处理MySQL数据库相关的异常
    print(f"数据库相关出现报错: {e}")

except Exception as e:
    # 处理其他类型的异常
    print(f"出现错误: {e}")

finally:
    # 关闭数据库连接
    # 检查当前程序变量中是否包含connection,如果有关掉它
    if 'connection' in locals():
        connection.close()

爬虫使用cookies练习

本次练习,我们使用网站为学校网站https://xdzx.chneic.sh.cn/

注意!
学校网站具有较好的防护机制!通过防火墙WAF实现的。
外部“可疑”访问基本都能防御!(也能解决,但不是本项目内容,后续讲Selenium项目时再实施)
所以,我们要从内部进行访问,目的是绕过防火墙
外部访问时,https://xdzx.chneic.sh.cn/对应地址为222.66.36.123。
服务器在内网真实地址为10.86.215.231

以下步骤每次必做:

步骤:修改hosts文件

路径为:C:\Windows\System32\drivers\etc

用管理员模式打开notepad++,再打开路径下的hosts文件

在文件末尾添加

10.86.215.231 xdzx.chneic.sh.cn

表示将xdzx.chneic.sh.cn解析为内网地址10.86.215.231

测试,在命令行执行,ping xdzx.chneic.sh.cn 查看返回结果

步骤:获取cookies

不能给你们密码,只提供cookies

cookie添加方式见“框架”

步骤:访问测试页(获取22306班期末考试成绩)

步骤:代码规范化,模块化

随着代码功能的丰富,代码越来越长,可读性越来越差。

我们将逐步实现代码模块化封装。

需要使用扩展“koroFileHeader”

'''
Author: xiandaidl 694060435@qq.com
Date: 2024-03-03 20:32:18
LastEditors: xiandaidl 694060435@qq.com
LastEditTime: 2024-03-03 20:58:04
FilePath: \测试\测试.py
Description: 这是网页爬虫请求的典型代码,在实际使用中,请将url,headers,cookies进行替换。
'''
import requests

'''
description: 用于向url请求数据
return {返回json,如果请求失败返回None}
'''
def fetch_data(url,headers,cookies,data):
    try:
        response = requests.post(url, headers=headers, data=data, cookies=cookies)
        response.raise_for_status()
        return response.json()
    except requests.HTTPError as http_err:
        print(f"网页请求出错,错误代码: {http_err}")
    except Exception as err:
        print(f"出现错误: {err}")
    

url =  "https://xdzx.chneic.sh.cn/XDEAM/Svl_Zbcjcx"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
    "Accept": "application/json, text/javascript, /; q=0.01",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "X-Requested-With": "XMLHttpRequest",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin"
    }
cookies = {
    "ASP.NET_SessionId": "xxxxxxx",
    "JSESSIONID": "xxxxxxx"
    }
data = {
    "active": "initStuScoreInfo",
    "sAuth": "@05@O@09@O@82@",
    "EXAMTYPE": "qz",
    "XNXQBM": "2023101",
    "BJBH": "22306"
    }  

res_json=fetch_data(url,headers,cookies,data)
if res_json!=None:
    print(res_json)

步骤:成绩数据规范化处理(只取语数英)

'''
Date: 2024-03-04 12:14:17
astEditTime: 2024-03-04 14:47:21
FilePath: \22306班大数据课程\第三周\5.1 项目:学生考试成绩 数据爬取.py
'''
import requests

'''
description: 这个函数是用于网页请求
return {*} 访问正常返回json,失败返回空
'''
def fetch_data(url,headers,cookies,data):
    try:
        response = requests.post(url, headers=headers, data=data, cookies=cookies)
        response.raise_for_status()
        return response.json()
    except requests.HTTPError as http_err:
        print(f"网页请求出错,错误代码: {http_err}")
    except Exception as err:
        print(f"出现错误: {err}")
'''
description: 这个是对获取的数据进行处理
param {*} res_json 请求的网页的json数据
return {*} 学生姓名,语文,数学,英语
'''
def Processing_data(res_json):
    subject_codes={
        "语文":None,
        "数学":None,
        "英语":None
    }
    stu_list=[]
    for item in res_json[0]["examSubData"]:
        if item['subName'] in subject_codes:
            subject_codes[item['subName']]=item['code']
    # print(subject_codes)
    for stu in res_json[0]["listData"]:
        row_data=[stu['XM']]
        for xueke in subject_codes.values():
            row_data.append(stu[xueke])
        stu_list.append(row_data)
    return stu_list

#网址
url = "https://xdzx.chneic.sh.cn/XDEAM/Svl_Zbcjcx"
#请求头
headers={
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    "sec-ch-ua": "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Microsoft Edge\";v=\"122\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-requested-with": "XMLHttpRequest"
    }
#cookies
cookies = {
    "ASP.NET_SessionId": "sa2s30vylufeoyhgzxreeg2n",
    "JSESSIONID":"262E738992B126248B1C6EC25A731C92"
    }
#参数
data = {
    "active": "initStuScoreInfo",
    "sAuth": "@05@O@09@O@82@",
    "EXAMTYPE": "qm",
    "XNXQBM": "2023101",
    "BJBH": "22306"
    }  

res_json=fetch_data(url,headers,cookies,data)
if res_json!=None:
    print(Processing_data(res_json))


阶段效果