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比賽,完成這樣的爬蟲還是挺簡單的。以爬取羊車門問題的作業為例,以下是我解決這個問題的思路,歡迎大家向我提出問題,或者指出錯誤。
 
 
   我們將需要爬取的內容在頁面中找到,他是下圖這樣的:

 

  分析一下他們的代碼,我在瀏覽器中對應位置右鍵,然后點擊檢查元素,可以找到對應部分的代碼。但是,直接查看當前網頁的源碼發現,里面並沒有對應的代碼。我猜測這里是根據服務器上的數據動態生成的這部分代碼,所以我們需要找到數據文件,以便向服務器申請,得到這部分資源。

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

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

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

  而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下打開)

玩個稍復雜點的

  像之前那樣爬取頁面的話,其實是有點問題的。首先,我們只是爬取了頁面的內容,但是並沒有抓取到頁面的樣式,頁面顯示會不太正常,排版混亂。其次,頁面中還有圖片等元素都不會顯示出來。而且,如果遇到網絡問題代碼需要再次運行的時候還會遇到一個問題,那就是目錄已經存在了,我們在創建目錄就會失敗。除此之外還是有不少問題的,此處我先解決之前說到的幾個問題。即顯示問題和目錄問題。

  如何解決我提到的這些問題呢,目錄問題我使用了一種比較容易實現的方案,那就是先判斷當前目錄是否存在,如果不存在就創建目錄,否則就什么都不做。至於文件,暫定的方法是直接覆蓋。顯示問題也比較好解決,抓取網頁和抓取樣式或者網頁其實都一樣,就是用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代碼找一個給大家看看,箭頭指向的地方都是程序自己改過的地方:

 

 

 其實,雖然現在又和原頁面接近了不少,但是……我暫時沒有時間繼續完善了,以后還會繼續完善。給大家一個原博客的圖片,你會我先我還是少了些東西。暫時先這樣吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM