记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)

文章目录

  • 前言
  • 爬取流程
    • 敲代码前的试探
    • 通过试探,总结一下编程思路。
    • 思路有了,上代码!
  • 后记

前言

  • 开门见山,直接切入正题,先看最终效果
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第1张图片记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第2张图片
  • 最终效果很理想,但这个过程是非常残酷的。

爬取流程

敲代码前的试探

  • 先实验能否直接得到题目。
import requests
courseId = 208420018
mHeaders = {
    'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3'
}
url = "https://mooc1.chaoxing.com/nodedetailcontroller/visitnodedetail?courseId=208255733&knowledgeId=263215264"

mHtml = requests.get(url,headers = mHeaders).content.decode('utf-8')
if '人类进入21世纪' in mHtml:
    print('甩了')
else:
    print('不妥')
  • 输出结果——不妥,说明并不能够通过静态页面直接爬取题目。一般经验肯定是认为是接口传输的,于是开始找接口。。。。
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第3张图片
  • 但是,网络传输的数据除了图片就剩html,css和js了,根本找不到传输题目的接口。
  • 当我把指针选中所有题目时,我发现了一个以我的前端知识理解不了的东西。
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第4张图片
  • Html中又镶嵌了一个Html,emmm…这就触及到我的知识盲区了。而且,点击下面的iframe标签里的src会跳转到一个新的页面,而这个页面正是所有的测验题目!!!
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第5张图片
  • 用postman简单Get一下,发现能够得到所有的题目!
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第6张图片
  • 接下来就改分析url了,总共有7个参数,其中下图打√的是必须的,其他几个可以不加。方法:用postman挨个请求就行。对于这三个参数,courseId是本来就知道的,那么我们的目标就是找到workId和knowledgeid即可。
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第7张图片
  • 一番寻找后,发现knowledgeid通过静态页面就能获取。
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第8张图片
  • 而workId在刚才找到的iframe标签的测验地址里,有人可能会问,那为啥不直接用测验地址呢,这我也试过,但是postman请求后的结果发现,得到的iframe标签里并没有地址,但是有个data属性,其中基本全是html转义符。
    在这里插入图片描述
  • 用Python的html库反转义后可得到workId。(后来发现,反转义与否不影响获取,直接用正则表达式就可以得到)
    记一次用Python爬取超星学习通课后小测验并写入Word文档(完整思路)_第9张图片

通过试探,总结一下编程思路。

  1. 首先得到课程ID(courseId),并组成url供后续访问得到章节ID(knowledgeId);
  2. 其次,通过访问主页面得到每一个knowledgeId,将knowledgeId和courseId组成url供后续获取workId;
  3. 然后,访问用courseId和knowledgeId组成的url,得到章节的workId,并将courseId,knowledgeId和workId组成最终的小测验地址;
  4. 对小测验地址进行访问,挨个爬取题目、选项并写入Word文档。

思路有了,上代码!

  1. 课程ID直接给,组成url并访问,得到页面。
def getHtml():  #得到页面
    mUrl = url.replace('{{courseId}}',courseId)
    response = requests.get(mUrl,headers=mHeaders)
    response.encoding = 'utf-8'
    mHtml = html.unescape(response.text)        #Html反转义
    #response.encoding = 'utf-8'
    print(mHtml)
    return mHtml
  1. 得到每一个小节的knowledgeId并和courseId组成url,并对爬取的地址进行试探是否是章节的地址。
def getCourseUrlList(zhtml):  #得到可以用的每一个课时的Url
    divList = []
    re_rule = 'courseId=' + courseId + '&knowledgeId=(.*?)">'
    # for i in re.findall(re_rule,html):
    #     divList.append(i)
    divList = re.findall(re_rule,zhtml)
    urlList = []
    for i in divList:
        mUrl = 'https://mooc1.chaoxing.com/nodedetailcontroller/visitnodedetail?courseId='+courseId+'&knowledgeId='+i
        print(mUrl)
        try:
            response = requests.get(mUrl,headers=mHeaders,timeout=1)
            if response.status_code == 200:
                if courseId in response.text:
                     urlList.append(mUrl)
                     print('访问成功')
                else:
                    print('非课程网页')
        except Exception as e:
            print('访问失败')

    return urlList
  1. 得到workId并和courseId、knowledgeId组成小测验的url。
def getZuoYeUrl(urlList):       #得到测验的Url
    tUrlList = []
    for i in urlList:
        response = requests.get(i,headers=mHeaders).content.decode('utf-8')
        res = re.findall('workid":"(.*?)",', response)
        if len(res):
            for i in res:
                tUrl = "https://mooc1.chaoxing.com/api/selectWorkQuestion?workId="+i+"&ut=null&classId=0&courseId="+courseId
                tUrlList.append(tUrl)
    return tUrlList
  1. 访问小测验的地址并爬取标题、题干、选项写入Word文档。
def writeDocx(urlList):          #从测验Url中读取题目并写入Word文档
    for url in urlList:
        mHtml = requests.get(url, headers=mHeaders).content.decode("utf-8")
        file = docx.Document()
        h3 = re.findall('

(.*?)

'
, mHtml) Title = "" for i in h3: Title = html.unescape(i) file.add_heading(Title) text = html.unescape(mHtml) mHtml = etree.HTML(text) # 将html转换为xml timuList = mHtml.xpath('//div[@class="TiMu"]') # 找到每一个题目及其所有选项 for i in timuList: time.sleep(0.05) mStr = etree.tostring(i).decode('utf-8') # 将xml树结点读出并转换为utf-8格式 res = html.unescape(mStr) # 解码xml tType = re.findall('(【.*?】)', res) tRType = [] for a in tType: p_rule = '<.*?>' tRType.append(re.sub(p_rule,'',str(a))) #删除所有的html标签 tGan = re.findall('】?(.*?)

'
, res) if not len(tGan): tGan = re.findall('
(.*?)
'
, res) if not len(tGan): tGan = re.findall('】(.*?)
',res) tRGan = [] for a in tGan: p_rule = '<.*?>' tRGan.append(re.sub(p_rule,'',str(a))) file.add_paragraph(tRType + tRGan) ''' for j in tType: print(j) file.add_paragraph(j) for j in tGan: print(j) file.add_paragraph(j) ''' XuanXiang = etree.HTML(res) tAny = XuanXiang.xpath('//li[@class="clearfix"]') for j in tAny: tStr = etree.tostring(j).decode('utf-8') tRes = html.unescape(tStr) tXuan = re.findall('(.*?).*?none;">?(.*?)?', tRes) tRXuan = [] for a in tXuan: tRRXuan = "" for b in a: p_rule = '<.*?>' tRRXuan = tRRXuan + re.sub(p_rule, '', str(b)) tRXuan.append(tRRXuan) for k in tRXuan: file.add_paragraph(k) file.save("D:\\"+Title+".docx") file.close() print(Title+'爬取完成') time.sleep(0.3)
  • 其余代码
  1. 全局变量及导库(放在开头)
# coding=utf-8
from lxml import etree
import docx
import requests
import re
import html
import time

url = "https://mooc1.chaoxing.com/course/{{courseId}}.html"
mHeaders = {
    'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3'
}
  1. 主函数
if __name__ == "__main__":
    courseId = "208255733"	#通过改变courseId可以实现爬取不同的课程,也可以课程号自加循环爬取,但课程量太大,就不一一编写。
    zHtml = getHtml()
    canUseUrl = getCourseUrlList(zHtml)
    zuoYeUrl = getZuoYeUrl(canUseUrl)
    writeDocx(zuoYeUrl)

后记

  • 经实验,本程序适用于大部分超星学习通课程,课程号可以在访问该课程时的地址栏url中获取。
  • 不要脸的推荐本人的其余爬虫文章:记一次用Python统计全国女性Size
  • 如复制出错,可去博主Github取完整代码点击直达,记得点个star哦,感激不尽。
  • 如有大佬发现文章错误,或代码错误,烦请指正,感激不尽。
  • 创作不易,转载请注明出处,感谢。

你可能感兴趣的:(笔记)