1. 程式人生 > >Python爬蟲+ pyqt5(從零開始到爬取教務處新聞,課程表,成績)

Python爬蟲+ pyqt5(從零開始到爬取教務處新聞,課程表,成績)

前言:

剛開始以為Python爬蟲很高深,其實,當你模仿其他人的程式碼,敲了一遍之後,你8成就可以理解Python的基本爬蟲了。

無論是學習什麼事情,剛開始就要準備好利器工具,那樣我們才能開始我們的旅程。工欲善其事,必先利其器麻!

Python的安裝:點選開啟連結    我下載的是3.5 你也可以下載其他版本的

Pycharm(是IDE)安裝:點選開啟連結

另外最好用火狐瀏覽器,檢視 除錯好點。

(一)好啦,準備好了,就讓我們寫一個最基本爬蟲,爬取一個網頁的圖片。


我們就爬這個網頁的所有圖片

先按F12,再按F5檢視,如下圖


再點選對應的網頁


這樣我們獲取網頁的基本資訊已經都做完了,就可以敲程式碼了。注意:程式碼要對齊,不然pycharm會報錯。

#匯入正則模組  用於匹配
import re
#匯入請求模組  向網頁請求的
import urllib.request
 
#通過url獲取網頁     def 這是一個函式定義頭  getHtml函式名    url函式引數
def getHtml(url):
    """			
    通過url獲取html地址		#函式說明
    :param url:
    :return: html
    """
	#headers 網頁的請求頭 防止反爬蟲(騙伺服器,是瀏覽器在請求,而不是機器人在請求)  在對應的網頁 按F12除錯檢視
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0',
    }
	#請求 返回req
    req = urllib.request.Request(url, headers =headers)
    #開啟req返回一個網頁
	html = urllib.request.urlopen(req).read()
    return html

#通過正則表示式來獲取圖片地址,並下載到本地
def getImg(html):
    """
    根據reg獲取圖片,並下載到本地
    :param html:
    :return: imglist
    """
	# 定義圖片正則 規則
    reg = r'src="(.+?\.jpg)"'
	#匹配
    imgre = re.compile(reg)
	#找到圖片列表
    imglist = re.findall(imgre, html)
	#x圖片編號
    x = 1
    for imgurl in imglist:
        print(imgurl)
        #通過urlretrieve函式把資料下載到本地的D:\\images,所以你需要建立目錄
        urllib.request.urlretrieve(imgurl, './images/'+'%s.jpg'%x)
        x = x + 1
    return imglist

def get():
	
    html = getHtml("https://tieba.baidu.com/p/5680688545")
    #記得加上這句 不然會亂碼 編碼格式看網頁原始碼 大部分 utf-8 gbk json
    html = html.decode('utf-8')
    print(html)
    print(getImg(html))

if  __name__ == "__main__":
    get()

裡面加了好多註釋,程式碼是很短的,實際程式碼也只有十幾行。另外我忘了加入Sleep函數了,加上的話會變慢,不過這樣能夠保證持續爬取,如果過快爬取的話,伺服器會認為你頻繁訪問而暫停你的請求。

會爬一個網頁的圖片就可以爬多個網頁了。

其實很簡單,只需要加工for迴圈 不斷 改變gethtml裡面的url的地址,不同的url對應不同的網頁,就可以實現爬取不同網頁的圖片了。

下面我舉個栗子,你們自己再試著去模仿下哈:

import re
import urllib.request
import urllib.error
from urllib.request import urlopen
import os
import operator
import time
#通過url獲取網頁
def getHtml(url):
    """
    通過url獲取html地址
    :param url:
    :return: html
    """
    # 用於模擬http頭的User-agent
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0',
    }

    req = urllib.request.Request(url, headers =headers)
    try:
        page = urlopen(req)
    except urllib.error as e:
        print(e)
    print(222)
    html = page.read().decode('utf-8')
    return html


#通過正則表示式來獲取圖片地址,並下載到本地
def getImg(html):
    """
    根據reg獲取圖片,並下載到本地
    :param html:
    :return: imglist
    """
    reg = r'src="(.+?\.jpg)"'
    imgre = re.compile(reg)
    imglist = re.findall(imgre, html)
    x=1        #圖片編號
    for imgurl in imglist:
        print(imgurl)
        #通過urlretrieve函式把資料下載到本地的D:\\images,所以你需要建立目錄
        while os.path.exists('./images/'+'%s.jpg'%x):
            x = x + 1
        if (operator.eq(imgurl,'https://ws1.sinaimg.cn/large/7e8b4ac8ly1fqdb6j1r9jj208202ijs9.jpg') !=1 ) & (operator.eq(imgurl,'https://ws1.sinaimg.cn/large/7e8b4ac8ly1fqdb6j9jfij208202ijs8.jpg')!=1):
            urllib.request.urlretrieve(imgurl, './images/'+'%s.jpg'%x)
        x = x + 1
    return imglist
def geturl(dir):

    y = 1
    while y >= 1:
        if y == 1:
            try:
                html = getHtml("http://aladd.net/archives/" + str(dir) + ".html")
            except:
                y = y + 1
                continue
            imglist = getImg(html)
            print("http://aladd.net/archives/" + str(dir) + ".html")
        else:
            try:
                html = getHtml("http://aladd.net/archives/" + str(dir + y-1) + ".html")
            except:
                y = y + 1
                continue
            imglist = getImg(html)
            print("http://aladd.net/archives/" + str(dir + y-1) + ".html")
        y = y + 1

def get():

    global x
    dir =32400        #第幾個網頁號
    while dir>=1:
        aaa=geturl(dir)
        print(aaa)
        dir = dir +1

if  __name__ == "__main__":
    get()

程式碼寫得很亂,程式碼就沒註釋了,第一個栗子基本都有解釋了。

這兩個簡單的例子就讓我對python產生了濃厚的興趣,於是我就想到一件有趣的事情,就是爬取我們學校教務處的資訊。

(二)爬取教務處資訊

(1)爬取教務處的新聞

1.到教務處頁面上

先按F12在按F5,點選一個類別的新聞。

檢視訊息頭,看到對應的請求網址。


緊接著,我們滑鼠右擊,檢視網頁原始碼去檢視你要爬取的資料在那些標籤中。


接著我們按下一頁,觀察請求網址,我可以發現是有規律變化的。


然後我們就可以開始爬取新聞了。

   sessions = requests.session()    #這個設為全域性的sessioins,下面我們講的模擬登入也用的是同一個sessions
    i = 1       #對應第1頁資訊
    page = '_' + str(i)
    if i == 1:
        newsMainUrl = 'http://jwc.dgut.edu.cn/dglgjwc/jwtz/list2_wh.shtml'
    elif i != 1:
        newsMainUrl = 'http://jwc.dgut.edu.cn/dglgjwc/jwtz/list2_wh' + page + '.shtml'

    newsMainHeader = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Encoding': 'gzip,deflate',
        'Cache-Control': 'max-age=0',
        'Content-Type': 'application/x-www-form-urlencoded',
        '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',
        'Host': 'cas.dgut.edu.cn',
        'Referer': 'http://jwc.dgut.edu.cn/',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0'
    }
    #get 請求
    rsp = sessions.get(newsMainUrl, headers=newsMainHeader)
    #獲取網頁
    content = rsp.content.decode('utf-8')
    #soup用來解析網頁的
    soup = BeautifulSoup(content, 'html.parser')
    #獲取所有dvi 並且calss為con-right fr"的標籤
    dvs = soup.find_all('div', class_="con-right fr")

    for dv in dvs:
        #獲取div中所有的a標籤
        items = dv.find_all('a')
        tds = dv.find_all('td', width="22%")
        for item in items:
            print(item.contents)
        for td in tds:
            print(td.text)

上面我們獲取了一個類別的所有新聞之後,就可以爬取不同類別的新聞了,這跟爬取一個網址的所有圖片和爬取多個網址的圖片是相同的道理的。

發現沒,每個類別的網址只有一處地方不同。

我們可以用一個列表來儲存不同之處

type = ['jwtz', 'kwtz', 'xjtz', 'jytz', 'sjtz']
每個類別的第1頁是這樣的 ,newstype:0對應就是教務通知(jwtz),1就是考務通知(kwtz)
newsMainUrl = 'http://jwc.dgut.edu.cn/dglgjwc/' + str(type[newsType]) + '/list2_wh.shtml'

第2頁起是這樣的

newsMainUrl = 'http://jwc.dgut.edu.cn/dglgjwc/' + str(type[newsType]) + '/list2_wh' + page + '.shtml'
具體實現你們可以自己去動手試試,我就不展現程式碼了。


(2)登入教務處,爬取課表,成績表

1.用request模擬登入教務處

我們學校有兩個登入頁面,第一個登入頁面是真正的提交表單的介面,第二登入介面就把表單提交到第一個登入頁面。

先開啟第一個登入頁面


這是第二個登入頁面


要先按F12,再按F5進入除錯模式,再把學號,密碼填入,點選登入

查詢Post的網頁,不過我這裡找不到Post的頁面,因為它提交的真正的登入頁之後就不見了。

我找了好幾天,偶然在網速慢的時候,看到了那個post網頁,才發現那是第一個登入網頁的網址,額,找了那麼久,竟然就在眼前。

這個找到的真正的登入頁面


這是登入頭,和請求頭


def loginHtml(USER, PWD):
    #登入真正的網址  這個要按登入按鈕後  查看錶單提交到哪  (這個我是找了好久的,原因是表單不是提交到當前的登入頁面)
    loginUrl = 'https://cas.dgut.edu.cn/home/Oauth/getToken/appid/jwxt.html'
    #登入頭
    loginHeader = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Encoding': 'gzip,deflate,br',
        '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',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Host': 'cas.dgut.edu.cn',
        'Referer': 'https://jwxt.dgut.edu.cn/login?appid=jwxt',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0'
    }
    #表單
    dat = {'username': USER, 'password': PWD, '_token_': '3c47f66f3bfba8c07b39773094c98c01'}
    #提交資料的 用post
    rsp = sessions.post(loginUrl, headers=loginHeader, data=dat)
    print(rsp.json()['code'])  #用這個判斷是否登入成功  返回1成功  返回4失敗

這樣就模擬登入成功了,其實就是將資料提交真正的url,這就要你學會去查詢。

2.登入了之後,我們就可以爬取成績表了。

我們上面用的是request的sessions,這個攜帶了登入的資訊(以後我不論是get還是post都用同一個sessions)。

點選對應的成績頁面。


在點選訊息頭


接著點選引數


好了至此,我們已經把資料都準備好了。

開始我們的爬蟲時間。

import requests
from bs4 import BeautifulSoup

def getScore():
    
    #成績url
    scoreUrl = 'http://jwxt.dgut.edu.cn/dglgjw/student/xscj.stuckcj_data.jsp'
    #成績請求頭
    scoreHeader = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Encoding': 'gzip,deflate',
        'Connection': 'keep-alive',
        'Content-Length': '158',
        'Content-Type': 'application/x-www-form-urlencoded',
        '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',
        'Cookie': 't_jwc_session=2|1:0|10:1526973050|13:t_jwc_session|16:MjAxNjQxNDA0MTA2|941e8dcc1b909a4634a529d225f016e7adc134714b50997332dad4e4c8420a22; _xsrf=2|131f13d9|35f809a60aa929ed7069f856a1854f99|1526973050; JWC_SERVERID=jwc1; JSESSIONID=60B287A3AD7A501C71462CD64F1F7392; JWXT_HA=ha15',
        'Host': 'cas.dgut.edu.cn',
        'Referer': 'http://jwxt.dgut.edu.cn/dglgjw/student/xscj.stuckcj.jsp?menucode=JW130706',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0'
    }
    #成績資料表單
    scoreData = {
        'fx': '1',
        'fxC': 'on',
        'menucode_current': '',
        'pjwchckcjklpbcj': '0',
        'sjxz': 'sjxz3',
        'sjxzS': 'on',
        'xn': str(year1.value()),
        'xn1': str(year1.value()+1),
        'xq': str(term.currentIndex()),
        'xypjwchcnckcj': '0',
        'ysyx': 'yscj',
        'ysyxS': 'on',
        'zx': '1',
        'zxC': 'on'
    }
    #提交表單
    rsp = sessions.post(scoreUrl, headers=scoreHeader, data=scoreData)
    #得到成績網址得內容
    content = rsp.content.decode('gbk')
    #用soup解析html,相當於篩選你要的資料
    soup = BeautifulSoup(content, 'html.parser')    
        #按列 獲取成績表格的內容
    for tr in soup.findAll('tr'):        #成績在tr td 標籤中
        tds = tr.findAll('td')
        print(tds)

好了,這樣成績表的基本資訊已經實現了。

3.下面我們可以模仿上面爬取成績表的方式爬取課程表。

不過獲取課程表的方式是get,不用提交資料, 是查詢字串的方式

點選課程表的網頁看它的訊息頭


再看它的查詢字串


在點選17-18第一學期


16-17第二學期



從上面我們可以看出規律

17-18學年  用y代表  w為上學期(即第一學期)  x為下學期  

16-17學年  用i代表    w為上學期(即第一學期)  x為下學期  

緊接著我們檢視它的響應原始碼(即課程表的原始碼)。找到你要爬取的資訊的標籤。


那麼,準備工作已經做完了,我們就開始我們的表演吧。

    #yearTmp是  上面對應 'y' 或 'i' ,或者其他(具體看網頁的原始碼)     
  #termTmp是學期   值為'w' 或者 'x'
  
    courseUrl = 'http://jwxt.dgut.edu.cn/dglgjw/student/wsxk.xskcb10319.jsp?params=eG49MjAxN' + yearTmp + 'Z4cT0' + termTmp + 'JnhoPTIwMTYwMDAwMzk4MQ=='

    courseHeader = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Encoding': 'gzip,deflate',
        'Content-Type': 'application/x-www-form-urlencoded',
        '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',
        'Cookie': 't_jwc_session=2|1:0|10:1527066371|13:t_jwc_session|16:MjAxNjQxNDA0MTA2|63f0b2477c712ec247910d86933fae49a2413b6609d7863d75473268b6b94d43; _xsrf=2|cd61f852|b2a45a7624b0f21a67f08c3b347d6550|1527066371; JWC_SERVERID=jwc1; JSESSIONID=F147E34F9039F4F1920D8406C5973E0F; JWXT_HA=ha14',
        'Host': 'cas.dgut.edu.cn',
        'Referer': 'http://jwxt.dgut.edu.cn/dglgjw/student/xkjg.wdkb.jsp?menucode=JW130501',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0'
    }

    rsp = sessions.get(courseUrl, headers=courseHeader)
    content = rsp.content.decode('gbk')

    soup = BeautifulSoup(content, 'html.parser')    #獲取課程名
    labels = soup.findAll('font')

    for i in range(len(labels)):  # lables儲存所有font裡面的內容       
        print(labels[i].contents[0])
    labels = soup.findAll('div', class_='div_nokb')
    for j in range(len(labels)):
        print(labels[j].get('id'))

    ct = soup.find_all('div', style='padding-bottom:5px;clear:both;')
    for k in range(len(ct)):
        print(ct[k].contents[1])

另外我先說下re爬取正則的的簡單使用。

用(.*?)替換你要爬取的資訊

比如


pa = re.compile(r'font-weight: bolder\'>(.*?)</font')  #注意'用改為 \' 這是轉義字元
courseName = re.findall(pa, content)     #content是對應的html

這樣就可以獲取課程名稱了

(三)用pyqt5將上面爬取的資料展現出來

爬取了資料,想著pyqt5還可以做成介面(當然之前我是學了點基本的qt知識),於是我就開始用pyqt5了

由於程式碼比較亂,所以這裡只能給出效果圖了:



以上就是我學了10幾天Python的一些收穫,歡迎大家一起學習。(V●ᴥ●V)

目前爬取成績和課表的存在一個問題,那就是每次都要在headers中新增Cookie的值,這就要去網頁上手動複製Cookie到程式碼中(如果沒新增就會出錯)。我不知道為什麼會這樣,因為我已經用了登入的sessions了,為什麼還是獲取不了成績!?

希望有大神能夠指點指點。