Python每日一練(11)-爬取在線課程

1. 爬取在線課程Excel版

昨天呢筆者外出去吃了一頓火鍋,疫情期間在家饞了好久勒,所以就沒有更新Python的每日一練,今天中午吃了飯之後,趕緊打開電腦給補上,嘿嘿。今天的案例主要是去爬取網易雲課堂上與Python相關的1200多門課程。如圖所示。
在這裏插入圖片描述如果我們要在網易雲上發佈一門Python課程,那麼就要對競品進行分析。而要對競品分析,首先要獲取競品數據,那麼使用爬蟲技術再合適不過了。使用Python爬蟲技術爬取網易雲課堂全部Python課程數據,並將爬取到的課程信息數據寫入到Excel表格中。這個案例的技術要點就是將數據輸入存入Excel,我們使用xlsxwriter模塊實現該功能。使用前一定要安裝該模塊:

pip install --user  -i http://pypi.douban.com/simple --trusted-host pypi.douban.com xlsxwriter

效果如圖所示:
在這裏插入圖片描述
使用xlsxwriter模塊基本步驟及流程如下所示:

import xlsxwriter  # 1.導入

# 2.創建Excel文件 參數爲: Excel名稱
work_book = xlsxwriter.Workbook("網易雲課堂Python課程數據.xlsx")
# 3.創建sheet
work_sheet = work_book.add_worksheet("Sheet1")
# 4.寫入數據
# 第一個參數表示行(從0開始) 第二個參數表示列(從0開始) 第三個參數是該表格的內容
work_sheet.write(0, 0, "商品ID")
work_sheet.write(0, 1, "課程ID")
work_sheet.write(0, 2, "商品名稱")
work_sheet.write(0, 3, "商品類型")
work_sheet.write(0, 4, "機構名稱")
work_sheet.write(0, 5, "評分")
# 5.關閉excel寫入
work_book.close()

運行程序,使用xlsxwriter模塊生成的Excel表格如圖所示。
在這裏插入圖片描述
先來分析一下網易雲課堂Python課程頁面。在百度搜索框中輸入網易雲課堂訪問網易雲課堂首頁,在首頁搜索欄中輸入Python關鍵字,進入Python課程頁面,然後單擊全部選項,顯示全部Python課程,如下圖所示。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
進入到上面的頁面之後,在網頁上任意一個空白地方單擊鼠標右鍵,選擇檢查。調出了控制檯之後,然後點擊Network,在按F5或者是瀏覽器左上角刷新頁面。如圖所示。
在這裏插入圖片描述
在這裏插入圖片描述
我們發現課程信息沒有直接顯示頁面中,而是保存在studycourse.json文件中,如圖所示。
在這裏插入圖片描述
通過以上分析,下面我們就可以使用requests模塊來獲取課程數據信息,使用xlsxwriter模塊將課程信息寫入到Excel表格。具體步驟如下:
1.由於使用了第三方模塊requestsxlsxwriter,所以需要先安裝該模塊。已經安裝過的可以不用再安裝。使用pip命令如下:

pip install --user  -i http://pypi.douban.com/simple --trusted-host pypi.douban.com requests
pip install --user  -i http://pypi.douban.com/simple --trusted-host pypi.douban.com xlsxwriter

2.導入程序中需要使用的模塊,具體代碼如下:

import requests
import xlsxwriter

3.首先使用requests模塊發送POST請求, 獲取到當前頁數的課程信息,然後使用json()方法獲取到Json格式數據,接下來使用xlsxwriter模塊將獲取到的當前頁數的信息寫入到Excel。最後在依次遍歷每一頁的課程信息。代碼如下:

import requests
import xlsxwriter  # 導入


def get_json(index):
    """
    爬取課程的json數據
    :param index: 頁碼 pageIndex 從1開始
    :return: json數據
    """
    url = "https://study.163.com/p/search/studycourse.json"
    payload = {
        "pageSize": 50,
        "pageIndex": index,
        "relativeOffset": 0,
        "searchTimeType": -1,
        "orderType": 5,
        "priceType": -1,
        "activityId": 0,
        "qualityType": 0,
        "keyword": "python"
    }  # 請求體
    headers = {
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
                      " (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36",
        "accept": "application/json",
        "content-type": "application/json",
        "origin": "https://study.163.com"
    }  # 請求頭

    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:  # 根據狀態碼判斷是否請求成功
        content_json = response.json()  # 獲取json數據
        if content_json and content_json["message"] == "ok":  # 判斷數據是否存在
            return content_json
    else:
        print("請求數據失敗~")
        return None


def get_content(content_json):
    """
    獲取課程信息列表
    :param content_json: 通過get_json函數獲取的json格式數據
    :return: 課程數據
    """
    if "result" in content_json:
        return content_json["result"]["list"]  # 返回課程數據列表


def save_excel(content, index):
    """
    存儲到Excel
    :param content: 課程內容
    :param index: 索引值,從0開始
    :return: None
    """
    for num, item in enumerate(content):
        # 因爲Excel第一行要寫入標題 所以row從1開始
        row = 50 * (index - 1) + (num + 1)
        # 行內容
        work_sheet.write(row, 0, str(item["productId"]))
        work_sheet.write(row, 1, str(item["courseId"]))  # 以文本的形式在Excel單元格中顯示
        work_sheet.write(row, 2, item["productName"])
        work_sheet.write(row, 3, item["productType"])
        work_sheet.write(row, 4, item["provider"])
        work_sheet.write(row, 5, item["score"])
        work_sheet.write(row, 6, item["scoreLevel"])
        work_sheet.write(row, 7, item["learnerCount"])
        work_sheet.write(row, 8, item["lessonCount"])
        work_sheet.write(row, 9, item["lectorName"])
        work_sheet.write(row, 10, item["originalPrice"])
        work_sheet.write(row, 11, item["discountPrice"])
        work_sheet.write(row, 12, item["discountRate"])
        work_sheet.write(row, 13, item["imgUrl"])
        work_sheet.write(row, 14, item["bigImgUrl"])
        work_sheet.write(row, 15, item["description"])


def main(index):
    """
    主函數
    :param index: 索引值,從1開始
    :return:
    """
    content_json = get_json(index)  # 取出json數據
    content = get_content(content_json)  # 取出課程信息
    save_excel(content, index)  # 存入Excel


if __name__ == '__main__':  # 程序入口
    print("*******************開始執行*******************")
    work_book = xlsxwriter.Workbook("網易雲課堂Python課程數據.xlsx")  # 創建excel文件
    work_sheet = work_book.add_worksheet("first_sheet")  # 創建sheet
    # 行首標題
    work_sheet.write(0, 0, "商品id")
    work_sheet.write(0, 1, "課程id")
    work_sheet.write(0, 2, "課程名稱")
    work_sheet.write(0, 3, "課程類型")
    work_sheet.write(0, 4, "機構名稱")
    work_sheet.write(0, 5, "評分")
    work_sheet.write(0, 6, "評分等級")
    work_sheet.write(0, 7, "學習人數")
    work_sheet.write(0, 8, "課程節數")
    work_sheet.write(0, 9, "講師名稱")
    work_sheet.write(0, 10, "原價")
    work_sheet.write(0, 11, "折扣價")
    work_sheet.write(0, 12, "折扣率")
    work_sheet.write(0, 13, "課程小圖url")
    work_sheet.write(0, 14, "課程大圖url")
    work_sheet.write(0, 15, "課程描述")
    total_page_count = get_json(1)["result"]["query"]["totlePageCount"]
    for i in range(total_page_count):
        main(i + 1)  # i+1即爲頁碼
    work_book.close()  # 關閉excel寫入
    print("*******************執行結束*******************")

運行程序會生成一個Excel表格,如圖所示。
在這裏插入圖片描述

2. 爬取在線課程MySQL版

對於使用爬蟲爬取到的數據,通常將其保存到文件或是數據庫中。這次將使用requests模塊爬取網易雲課程,並使用pymysql模塊將爬取到的數據存入到MySQL數據庫中。爬取後效果如圖所示。
在這裏插入圖片描述
Python中操作mysql步驟:
在這裏插入圖片描述
(1) 安裝第三方模塊pymysql,命令如下:

 pip install pymysql

(2) 在py文件中引入pymysql模塊

from pymysql import *

(3) 建立連接

# 1.建立數據庫鏈接
# host: 連接的mysql主機 本機寫'localhost'
# port: 連接的mysql主機端口 默認是3306
# database: 數據庫的名稱
# user: 連接的用戶名
# password: 連接的密碼
# charset: 通信採用的編碼方式,推薦使用utf8
conn = connect(host="localhost", port=3306, database="wyy_spider", user="root", password="mysql", charset="utf8")
# 對象的方法如下:
# close()關閉連接
# commit()提交
# cursor() 返回Cursor對象,用於執行sql語句並獲得結果

(4) 創建cursor對象

# 用於執行sql語句 使用頻度最高的語句爲select、insert、update、delete
# 獲取Cursor對象: 調用Connection對象的cursor()方法
cs1 = conn.cursor()
# close()關閉
# execute(operation [, parameters ])執行語句,返回受影響的行數,主要用於執行insert、update、delete語句,也可以執行create、alter、drop等語句
# fetchone()執行查詢語句時,獲取查詢結果集的第一個行數據,返回一個元組
# fetchall()執行查詢時,獲取結果集的所有行,一行構成一個元組,再將這些元組裝入一個元組返回

2.1 增刪改查

向數據庫中添加數據,示例代碼如下:

from pymysql import *  # 導入

if __name__ == '__main__':
    # 1.建立連接
    conn = connect(host="localhost", port=3306, database="wyy_spider",
                   user="root", password="mysql", charset="utf8")

    # 2.創建cursor對象
    cs1 = conn.cursor()
    # 3.操作數據
    # 3.1 插入一條數據
    count = cs1.execute("insert into teacher(name,age) values('Amo',18)")
    print(count)  # 打印受影響的行數
    count = cs1.execute("insert into teacher(name,age) values('zy',16)")
    print(count)  # 打印受影響的行數
    sql = "insert into teacher(name,age) values(%s, %s)"
    # 第一個參數: 固定的sql語法
    # 第二個參數: 列表 列表裏面的元素是由一個一個的元組組成
    cs1.executemany(sql, [("Jerry", 35), ("Paul", 21), ("Jason", 26)])
    # 3.2 插入多條數據
    # 提交之前的操作
    conn.commit()
    # 關閉Cursor對象和Connection對象
    cs1.close()
    conn.close()

在這裏插入圖片描述
從數據庫中刪除數據,示例代碼如下:

from pymysql import *  # 導入

if __name__ == '__main__':
    # 1.建立連接
    conn = connect(host="localhost", port=3306, database="wyy_spider",
                   user="root", password="mysql", charset="utf8")

    # 2.創建cursor對象
    cs1 = conn.cursor()
    # 3.操作數據
    count = cs1.execute("delete from teacher where name='Jerry'")
    print(count)
    # 提交之前的操作
    conn.commit()
    # 關閉Cursor對象和Connection對象
    cs1.close()
    conn.close()

在這裏插入圖片描述
更改數據庫中的部分數據,示例代碼如下:

from pymysql import *  # 導入

if __name__ == '__main__':
    # 1.建立連接
    conn = connect(host="localhost", port=3306, database="wyy_spider",
                   user="root", password="mysql", charset="utf8")

    # 2.創建cursor對象
    cs1 = conn.cursor()
    # 3.操作數據
    count = cs1.execute("update teacher set age=18 where name='zy'")
    print(count)
    # 提交之前的操作
    conn.commit()
    # 關閉Cursor對象和Connection對象
    cs1.close()
    conn.close()

在這裏插入圖片描述
查詢數據庫中的數據,示例代碼如下:

from pymysql import *  # 導入

if __name__ == '__main__':
    # 1.建立連接
    conn = connect(host="localhost", port=3306, database="wyy_spider",
                   user="root", password="mysql", charset="utf8")

    # 2.創建cursor對象
    cs1 = conn.cursor()
    # 3.操作數據
    count = cs1.execute("select * from teacher where age > 18")
    print(count)
    # for i in range(count):
    #     # 獲取查詢的結果
    #     result = cs1.fetchone()
    #     print(result)  # 打印結果 返回結果爲一個元組 ('Paul', 21)
    result = cs1.fetchall()
    # (('Paul', 21), ('Jason', 26))
    print(result)  # 返回的結果是一個元組 元組的元素由元組組成
    # 提交之前的操作
    conn.commit()
    # 關閉Cursor對象和Connection對象
    cs1.close()
    conn.close()

2.2 代碼實現

實現將爬取信息寫入數據庫的功能,需要先創建數據庫,創建數據表,然後編寫代碼實現該功能。具體步驟如下:
(1) 創建數據庫。可以使用控制檯或者MySQL可視化工具創建一個名爲wyy_spider的數據庫。如圖所示:
在這裏插入圖片描述
(2) 創建數據表。通過如下SQL語句來創建course表:

create table course(
	course_id bigint(20) not null,
    product_id bigint(20) not null,
    product_type int(11) not null,
    product_name varchar(125) not null,
    provider varchar(125) not null,
    score float default null,
    score_level int(11) default null,
    learner_count int(11) default null,
    lesson_count int(11) default null,
    lector_name varchar(125) default null,
    original_price float default null,
    discount_price float default null,
    discount_rate float default null,
    img_url varchar(125) default null,
    big_img_url varchar(125) default null,
    description text,
    primary key(course_id)) engine=InnoDB default charset=utf8;

(3) 最後代碼如下:

# -*- coding: utf-8 -*-
# @Time    : 2020/4/11 16:44
# @Author  : 我就是任性-Amo
# @FileName: 66.爬取在線課程.py
# @Software: PyCharm
# @Blog    :https://blog.csdn.net/xw1680

from pymysql import *
import requests
import time  # 導入


def get_json(index):
    """
    爬取課程的json數據
    :param index: 頁碼 pageIndex 從1開始
    :return: json數據
    """
    url = "https://study.163.com/p/search/studycourse.json"
    payload = {
        "pageSize": 50,
        "pageIndex": index,
        "relativeOffset": 0,
        "searchTimeType": -1,
        "orderType": 5,
        "priceType": -1,
        "activityId": 0,
        "qualityType": 0,
        "keyword": "python"
    }  # 請求體
    headers = {
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/"
                      "537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36",
        "accept": "application/json",
        "content-type": "application/json",
        "origin": "https://study.163.com"
    }  # 請求頭

    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:  # 根據狀態碼判斷是否請求成功
        content_json = response.json()  # 獲取json數據
        if content_json and content_json["message"] == "ok":  # 判斷數據是否存在
            return content_json
    else:
        print("請求數據失敗~")
        return None


def get_content(content_json):
    """
    獲取課程信息列表
    :param content_json: 通過get_json函數獲取的json格式數據
    :return: 課程數據
    """
    if "result" in content_json:
        return content_json["result"]["list"]  # 返回課程數據列表


def check_course_exit(course_id):
    """
    檢查課程是否存在
    :param course_id: 課程id
    :return: 課程存在返回True 否則返回False
    """
    sql = f'select course_id from course where course_id = {course_id}'
    cs1.execute(sql)
    course = cs1.fetchone()
    if course:
        return True
    else:
        return False


def save_to_course(course_data):
    sql_course = """insert into course
    values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
    """
    cs1.executemany(sql_course, course_data)


def save_mysql(content):
    course_data = []
    for item in content:
        if not check_course_exit(item['courseId']):
            course_value = (item['courseId'], item['productId'], item['productType'],
                            item['productName'], item['provider'], item['score'],
                            item['scoreLevel'], item['learnerCount'],
                            item['lessonCount'], item['lectorName'],
                            item['originalPrice'], item['discountPrice'],
                            item['discountRate'], item['imgUrl'], item['bigImgUrl'],
                            item['description'],)
            course_data.append(course_value)
    save_to_course(course_data)


def main(index):
    """
    主函數
    :param index: 索引值,從1開始
    :return:
    """
    content_json = get_json(index)  # 取出json數據
    content = get_content(content_json)  # 取出課程信息
    save_mysql(content)  # 存入數據庫


if __name__ == '__main__':  # 程序入口
    # 1.建立數據庫鏈接
    # host: 連接的mysql主機 本機寫'localhost'
    # port: 連接的mysql主機端口 默認是3306
    # database: 數據庫的名稱
    # user: 連接的用戶名
    # password: 連接的密碼
    # charset: 通信採用的編碼方式,推薦使用utf8
    conn = connect(host="localhost", port=3306, database="wyy_spider",
                   user="root", password="mysql", charset="utf8")

    # 2.創建cursor對象
    # 用於執行sql語句 使用頻度最高的語句爲select、insert、update、delete
    # 獲取Cursor對象: 調用Connection對象的cursor()方法
    cs1 = conn.cursor()
    print("*******************開始執行*******************")
    start = time.time()
    total_page_count = get_json(1)['result']["query"]["totlePageCount"]  # 獲取總頁數
    for i in range(total_page_count):
        main(i + 1)
    conn.commit()
    cs1.close()
    conn.close()
    print("執行結束")
    end = time.time()
    print(f"程序執行時間是{end - start}秒")
    print("*******************執行結束*******************")

程序運行結果如圖所示,並且數據已經插入到mysql數據庫中:
在這裏插入圖片描述

3. 使用多進程爬取在線課程MySQL版

如果爬取的數據內容較多,存在多個分頁,使用普通方式用時會較長。針對這種情況,可以使用多進程的方式來提高爬取數據和存入數據庫的效率。如果大家對多任務不熟悉的話,可以去觀看博主的另外一篇博客 Python進程和線程。運行效果如圖所示。
在這裏插入圖片描述
使用多任務爬取數據的時候,建議大家先將建表中的主鍵給去掉,先將數據爬取下來,然後再去數據庫中去重。否則可能會出錯,如下圖所示。
在這裏插入圖片描述
示例代碼如下:

from pymysql import *
import requests
import time  # 導入
from multiprocessing import Pool


def get_json(index):
    """
    爬取課程的json數據
    :param index: 頁碼 pageIndex 從1開始
    :return: json數據
    """
    url = "https://study.163.com/p/search/studycourse.json"
    payload = {
        "pageSize": 50,
        "pageIndex": index,
        "relativeOffset": 0,
        "searchTimeType": -1,
        "orderType": 5,
        "priceType": -1,
        "activityId": 0,
        "qualityType": 0,
        "keyword": "python"
    }  # 請求體
    headers = {
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/"
                      "537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36",
        "accept": "application/json",
        "content-type": "application/json",
        "origin": "https://study.163.com"
    }  # 請求頭

    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:  # 根據狀態碼判斷是否請求成功
        content_json = response.json()  # 獲取json數據
        if content_json and content_json["message"] == "ok":  # 判斷數據是否存在
            return content_json
    else:
        print("請求數據失敗~")
        return None


def get_content(content_json):
    """
    獲取課程信息列表
    :param content_json: 通過get_json函數獲取的json格式數據
    :return: 課程數據
    """
    if "result" in content_json:
        return content_json["result"]["list"]  # 返回課程數據列表


def check_course_exit(course_id):
    """
    檢查課程是否存在
    :param course_id: 課程id
    :return: 課程存在返回True 否則返回False
    """
    sql = f'select course_id from course where course_id = {course_id}'
    cs1.execute(sql)
    course = cs1.fetchone()
    if course:
        return True
    else:
        return False


def save_to_course(course_data):
    sql_course = """insert into course
    values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
    """
    cs1.executemany(sql_course, course_data)


def save_mysql(content):
    course_data = []
    for item in content:
        if not check_course_exit(item['courseId']):
            course_value = (item['courseId'], item['productId'], item['productType'],
                            item['productName'], item['provider'], item['score'],
                            item['scoreLevel'], item['learnerCount'],
                            item['lessonCount'], item['lectorName'],
                            item['originalPrice'], item['discountPrice'],
                            item['discountRate'], item['imgUrl'], item['bigImgUrl'],
                            item['description'],)
            course_data.append(course_value)
    save_to_course(course_data)


def main(index):
    """
    主函數
    :param index: 索引值,從1開始
    :return:
    """
    content_json = get_json(index)  # 取出json數據
    content = get_content(content_json)  # 取出課程信息
    save_mysql(content)  # 存入數據庫


if __name__ == '__main__':  # 程序入口
    # 1.建立數據庫鏈接
    # host: 連接的mysql主機 本機寫'localhost'
    # port: 連接的mysql主機端口 默認是3306
    # database: 數據庫的名稱
    # user: 連接的用戶名
    # password: 連接的密碼
    # charset: 通信採用的編碼方式,推薦使用utf8
    conn = connect(host="localhost", port=3306, database="wyy_spider",
                   user="root", password="mysql", charset="utf8")

    # 2.創建cursor對象
    # 用於執行sql語句 使用頻度最高的語句爲select、insert、update、delete
    # 獲取Cursor對象: 調用Connection對象的cursor()方法
    cs1 = conn.cursor()
    print("*******************開始執行*******************")
    start = time.time()
    total_page_count = get_json(1)['result']["query"]["totlePageCount"]  # 獲取總頁數
    # for i in range(total_page_count):
    #     main(i + 1)  不使用多任務的方式
    pool = Pool()
    index_list = [i for i in range(total_page_count)]
    # map(self, func, iterable, chunksize=None):
    pool.map(main, index_list)  # 執行多任務
    pool.close()  # 關閉進程池
    pool.join()  # 等待子進程結束
    conn.commit()
    cs1.close()
    conn.close()
    print("執行結束")
    end = time.time()
    print(f"程序執行時間是{end - start}秒")
    print("*******************執行結束*******************")

運行結果如圖所示:
在這裏插入圖片描述
從運行結果可以看出,比之前的執行時間短了很多。至此今天的案例就到此結束了,另外筆者在這裏聲明,筆者寫文章只是爲了學習交流,以及讓更多學習Python基礎的讀者少走一些彎路,節省時間,並不用做其他用途,如有侵權,聯繫博主刪除即可。編寫不易,請大家手留餘香~。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章