Python爬蟲爬取博客園做業

要求

第一部分:

請分析做業頁面,爬取已提交做業信息,並生成已提交做業名單,保存爲英文逗號分隔的csv文件。文件名爲:hwlist.csv 。
文件內容範例以下形式:
學號,姓名,做業標題,做業提交時間,做業URL
20194010101,張三,羊車門做業,2018-11-13 23:47:36.8, http://www.cnblogs.com/sninius/p/12345678.html

第二部分:

在生成的 hwlist.csv 文件的同文件夾下,建立一個名爲 hwFolder 文件夾,爲每個已提交做業的同窗,新建一個以該生學號命名的文件夾,將其做業網頁爬取下來,並將該網頁文件存以學生學號爲名,「.html」爲擴展名放在該生學號文件夾中。
 

正題

  以前打過CTF比賽,完成這樣的爬蟲仍是挺簡單的。以爬取羊車門問題的做業爲例,如下是我解決這個問題的思路,歡迎你們向我提出問題,或者指出錯誤。
 
 
   咱們將須要爬取的內容在頁面中找到,他是下圖這樣的:

 

  分析一下他們的代碼,我在瀏覽器中對應位置右鍵,而後點擊檢查元素,能夠找到對應部分的代碼。可是,直接查看當前網頁的源碼發現,裏面並無對應的代碼。我猜想這裏是根據服務器上的數據動態生成的這部分代碼,因此咱們須要找到數據文件,以便向服務器申請,獲得這部分資源。css

  在剛纔查看元素的地方接着找數據文件,在Network裏面的文件中很順利的就找到了,並在報文中拿到了URL和請求方法。html

  查看一下這個文件發現是JSON文件,那樣的話難度就又下降了,由於Python中有json庫,解析json的能力很強。能夠直接將json轉換爲字典和列表類型。web

   在這裏我簡單介紹一下數據解析的過程吧。首先,我將爬取到的json文本轉換成某種數據類型,具體由數據決定,通常不是字典就是列表。查看類型發現是字典,且字典中有三個key值,而咱們須要的key在一個叫data的key中。json

  而data中的數據是一個學生信息的列表類型,列表的每一個元素都是一個字典,包括學生姓名,學號等信息。能夠利用下標獲取列表元素,並用key值獲得你想拿到的信息。好比,利用Url獲得網頁連接。瀏覽器

 

 

 

這時候咱們爬取須要的信息的準備工做能夠說是結束了,咱們拿到了數據的URL,而且知道了數據類型和數據結構。因而,咱們只須要用requests庫爬一下這個頁面,而後用json解析一下,而且篩選有用的信息就行了。服務器

(沒用到BeautifulSoup和re庫有點小失落)網絡

 接下來就是建立文件,就沒有什麼難度了。只是在爲每一個學生建立文件的時候注意一下,建立好之後及時的回到上層目錄,不然,可能會讓文件一層層的嵌套下去。數據結構

代碼

# -*- coding:utf-8 -*-

import requests
import json
import os
#抓取頁面
url = 'https://edu.cnblogs.com/Homework/GetAnswers?homeworkId=2420&_=1542959851766'
try:
    r = requests.get(url,timeout=20)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
except:
    print('網絡異常或頁面未找到,請重試')

#利用json拿到數據列表,每一個列表元素都是字典
datas = json.loads(r.text)['data']
result = ""
#數據處理
for data in datas:
    result += str(data['StudentNo'])+','+data['RealName']+','+data['DateAdded'].replace('T',' ')+','+data['Title']+','+data['Url']+'\n'
#寫入文件
with open('hwlist.csv','w') as f:
    f.write(result)
#建立文件夾hwFolder
os.mkdir('hwFolder')
os.chdir('hwFolder')
#建立每一個學生的做業文件
for data in datas:
    #建立目錄
    os.mkdir(str(data['StudentNo']))
    os.chdir(str(data['StudentNo']))
    #抓取頁面
    try:
        webmsg = requests.get(data['Url'],timeout=20)
        webmsg.raise_for_status()
        webmsg.encoding = webmsg.apparent_encoding
    except:
        print('網絡異常或頁面未找到,請重試') 
    #保存抓到的頁面
    with open(str(data['StudentNo'])+'.html','wb') as f:
        f.write(webmsg.content)
    os.chdir(os.path.pardir)

 

部分結果展現

 

 

上圖是hwlist.csv文件的部分結果(Excel下打開)app

玩個稍複雜點的

  像以前那樣爬取頁面的話,實際上是有點問題的。首先,咱們只是爬取了頁面的內容,可是並無抓取到頁面的樣式,頁面顯示會不太正常,排版混亂。其次,頁面中還有圖片等元素都不會顯示出來。並且,若是遇到網絡問題代碼須要再次運行的時候還會遇到一個問題,那就是目錄已經存在了,咱們在建立目錄就會失敗。除此以外仍是有很多問題的,此處我先解決以前說到的幾個問題。即顯示問題和目錄問題。編輯器

  如何解決我提到的這些問題呢,目錄問題我使用了一種比較容易實現的方案,那就是先判斷當前目錄是否存在,若是不存在就建立目錄,不然就什麼都不作。至於文件,暫定的方法是直接覆蓋。顯示問題也比較好解決,抓取網頁和抓取樣式或者網頁其實都同樣,就是用URL發送一個請求,來得到這個資源,其實和抓取HTML相比,就是文件格式不太一致。

  以抓取樣式表(CSS)爲例,樣式的URL怎麼獲取呢?有一些樣式是在一個叫作Link的標籤的href屬性裏,這裏面就是外聯樣式存儲的位置。把它提取出來,請求這個樣式,而且修改原來的href屬性爲抓到的文件在本身電腦上的保存位置便可。這樣的話便可保證抓到的CSS能夠正常使用,確保排版正確。

  固然了,即便這樣,和本來的網頁也是有差異的,由於抓取到的資源仍是不夠,和瀏覽器中得到的元素對比一下就會發現還差很多。鑑於本人能力有限,這裏就補充一下爬取外聯CSS和圖片的內容,感興趣的能夠看一看。

  Tips:這裏解析HTML頁面藉助了強大的BeautifulSoup4庫(解析標籤和玩同樣)和re庫,使工做量減小了很多。(安裝bs4庫: pip install BeautifulSoup4)

 

# -*- coding:utf-8 -*-

import requests
import json
import os
import re
from bs4 import BeautifulSoup

def getHtml(url,timeout=110):
    try:
        res = requests.get(url,timeout)
        res.raise_for_status()
        res.encoding = res.apparent_encoding
        return res
    except:
        print('網絡異常,'+url+"爬取失敗")
    
def saveFile(name,content,mode='w'):
    try:
        with open(name,mode) as f:
            f.write(content)
    except:
        print("文件"+name+"建立失敗")
def getSource(text):
    #抓取樣式
    root_url = 'https://www.cnblogs.com'
    soup = BeautifulSoup(text,'html.parser')
    for i in soup('link'):
        css_list = [css for css in i['href'].split('/') if 'css' in css]
        if css_list!=[]:
            filename = re.search(r'.*css',css_list[0]).group(0)
            r = requests.get(root_url+i['href'])
            saveFile(filename,r.content,'wb')
            text = text.replace(i['href'],'Source/'+filename)
    #抓取圖片  用戶本身插入的圖片和網站本身生成的圖片都抓
    #用戶本身插的那些格式很亂……用戶本身搞的東西就是個坑
    for i in soup('img'):
        try:
            img_list = [img for img in i['src'].split('/') if 'gif' in img or 'png' in img or 'jpeg' in img]
        except KeyError :#某用戶本身改了HTML代碼 得讓我單獨判斷一下
            img_list = []
        if img_list!=[]:
            filename = img_list[0]
            try:
                r = requests.get(root_url+i['src'])
                r.raise_for_status()
            except:
                if not 'http' in i['src']:
                    r = requests.get("https:"+i['src'])
                else:#又是某用戶寫博客用了HTML編輯器,寫的還不對
                    r = requests.get(i['src'])
            saveFile(filename,r.content,'wb')
            text = text.replace(i['src'],'Source/'+filename)
    #text用於修改原始的頁面連接,保證本地能夠正常查看頁面
    return text
#############################主程序############################
#抓取頁面 並獲得數據
r = getHtml('https://edu.cnblogs.com/Homework/GetAnswers?homeworkId=2420&_=1542959851766')
datas = json.loads(r.text)['data']
#處理數據並將數據寫入文件
result = ""
for data in datas:
    result += str(data['StudentNo'])+','+data['RealName']+','+data['DateAdded'].replace('T',' ')+','+data['Title']+','+data['Url']+'\n'
saveFile('hwlist.csv',result,'w')
#建立文件夾hwFolder
if not os.path.exists('hwFolder'):
    os.mkdir('hwFolder')
os.chdir('hwFolder')
#建立每一個學生的做業文件
for data in datas:
    #建立目錄
    if not os.path.exists(str(data['StudentNo'])):
        os.mkdir(str(data['StudentNo']))
    os.chdir(str(data['StudentNo']))
    #抓取頁面
    webmsg = requests.get(data['Url'])
    print('當前的URL:'+data['Url'])#等待的過程有字出來不會無聊
    #頁面的一些資源
    if not os.path.exists('Source'):
        os.mkdir('Source')
    os.chdir('Source')
    webtext = getSource(webmsg.text)
    os.chdir(os.path.pardir)
    saveFile(str(data['StudentNo'])+'.html',webtext.encode(),'wb')
    
    os.chdir(os.path.pardir)

   若是你的網絡沒問題,講道理,應該不會拋異常。接下來找個頁面看看效果吧:

排版抓出來了,挺炫的,固然,圖片也抓了。

 

 

考慮到有人會HTML,我把被程序調整後的HTML代碼找一個給你們看看,箭頭指向的地方都是程序本身改過的地方:

 

 

 其實,雖然如今又和原頁面接近了很多,可是……我暫時沒有時間繼續完善了,之後還會繼續完善。給你們一個原博客的圖片,你會我先我仍是少了些東西。暫時先這樣吧。

相關文章
相關標籤/搜索