1. 程式人生 > >OJ系統爬蟲總結

OJ系統爬蟲總結

背景

最近導師讓我幫他把OJ系統上的學生程式碼匯出來,怎知系統並沒有一鍵匯出的功能,無奈只能對著百度眾多繁雜的教程咬咬牙爬蟲,折騰了1天半總算搞出來交差了。

需求

1.提取驗證碼
2.模擬登陸
3.提取學生賬號(學號)stuID、執行編號runID、題目編號pID.,構成學生程式碼提交頁面的連結link
4.根據連結link提取學生提交的程式碼
5.匹配merge學號和學生姓名
6.儲存每位學生的程式碼到本地,並且以學生姓名命名資料夾

總結

總結出以下幾點:
1. opener是一個好東西,一開始將cookie繫結到opener裡面,後面都無需麻煩了
2. 構造request請求,post方法程式碼如下

response=opener.open(postURL,postdata) #如果是get方法的話就無需第二個引數
content=response.read().decode()
print(content) #檢視post登陸結果

3.讀取excel檔案:

pd.read_excel('d:/stulist.xlsx', sheetname='stulist',dtype=str)

4.抓包遇到有驗證碼,順藤摸瓜找出驗證碼的網址,用opener訪問驗證碼地址,獲取cookie,將驗證碼儲存到本地再讀進來顯示出影象來。
5.正則表示式問號需要加斜杆轉義
6.如何轉到下一頁:抓取下頁網址資訊,繼續往下爬,直到末頁跳出while迴圈
7.用try-except可以在報錯之後繼續執行下去
8.一開始不懂,看了很多教程後,拼湊程式碼,一會兒用opener,一會兒用requests,結果浪費很多時間,爬蟲框架很多,建議用的話只用一個就行,既然opener這麼容易,以後我就專門用它好了。

程式碼:

# -*- coding: utf-8 -*-
"""
Created on Tue Jul 10 15:01:36 2018

@author: Administrator
"""


from http import cookiejar
import urllib.request
import re  
import pandas as pd
import os
import urllib.parse
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

stulist=pd.read_excel('d:/stulist.xlsx'
, sheetname='stulist',dtype=str) orginURL="http://116.56.140.75:8000/JudgeOnline/" cid=1117 vcodeURL=orginURL+"vcode.php" postURL=orginURL+"login.php" statusURL=orginURL+'status.php' page_URL= statusURL+'?problem_id=&user_id=&cid='+str(cid)+'&language=-1&jresult=-1&showsim=0' #將cookie繫結到一個opener裡,cookie由cookielib自動管理 cookie=cookiejar.CookieJar() handler=urllib.request.HTTPCookieProcessor(cookie) opener=urllib.request.build_opener(handler) #根據抓包資訊,構造標頭檔案headers opener.addheaders=[("User-Agent","'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'")] #用opener訪問驗證碼地址,獲取cookie pic=opener.open(vcodeURL).read() #儲存驗證碼到本地(以二進位制格式開啟一個檔案只用於寫入) local=open('vcode.jpg','wb') local.write(pic) local.close() #讀取並顯示驗證碼(以二進位制格式開啟一個檔案用於只讀) f = open('vcode.jpg', mode='rb') plt.imshow(mpimg.imread('vcode.jpg')) plt.show() vcode=input('請輸入驗證碼:') #根據抓包資訊構造表單 postData={'password':'xxxxxx', 'submit':'Submit', 'user_id':'xxxxxx', 'vcode':vcode } #生成post資料(key1=value1&key2=value2的形式) data=urllib.parse.urlencode(postData).encode('utf-8') #構造request請求 response=opener.open(postURL,data) content=response.read().decode() print(content) #返回登入結果 runID=[] stuID=[] pID=[] while 1: response = opener.open(page_URL) content=response.read().decode() """ pattern0 = re.compile(r'<tr><td>(.*?)</td><td>',re.S) match_ct0 = re.findall(pattern0,content) for i in range(len(match_ct0)): runID.append(match_ct0[i].lstrip()) #去除左空格 """ pattern1 = re.compile(r'#(.*?)\'',re.S) match_ct1 = re.findall(pattern1,content) for i in range(len(match_ct1)-1): stuID.append(match_ct1[i+1]) pattern2 = re.compile(r'href="submitpage.php\?cid='+str(cid)+'&pid=(.*?)&sid=(.*?)">Edit</a>',re.S) #正則表示式中問號需要轉義 match_ct2 = re.findall(pattern2,content) for i in range(len(match_ct2)): runID.append(match_ct2[i][1]) pID.append(match_ct2[i][0]) # pID.append(match_ct2[i].replace('problem','submitpage')) #字串替換 pattern3 = re.compile(r'Previous Page</a>]&nbsp;&nbsp;\[<a href=status.php?(.*?)>Next Page',re.S) match_ct3 = re.findall(pattern3,content) next_page_URL=statusURL+match_ct3[0] pre_page_URL=page_URL page_URL=next_page_URL #轉到下一頁 if page_URL==pre_page_URL: #末頁跳出while迴圈 break #配對錶 pair_table=pd.DataFrame() pair_table['runID']=pd.Series(runID) pair_table['stuID']=pd.Series(stuID) pair_table['pID']=pd.Series(pID) pair_table['link']=orginURL+'submitpage.php?cid=1117&pid='+pair_table ['pID']+"&sid="+pair_table['runID'] pair_table = pd.merge(pair_table, stulist, on='stuID',how='left') #左連線 pair_table=pair_table.sort_values(by = ['name'],axis = 0,ascending = True) #按name升序排序 pair_table = pair_table.reset_index(drop=True) #重置index #讀取學生程式碼,寫到本地,每個學生一個資料夾 for i in range(len(pair_table)): folder = 'd:\OJ\\'+str(pair_table.ix[i,'name']) #路徑 fileName=str(pair_table.ix[i,'pID'])+'.txt' #檔名 if not os.path.exists(folder): #判斷資料夾是否存在 os.makedirs(folder) #若否新建資料夾 submitpageURL=pair_table.ix[i,'link'] response = opener.open(submitpageURL) submitpage_content=response.read().decode() pattern = re.compile(r'<textarea style="width:80%" cols=180 rows=20 id="source" name="source">(.*?)</textarea>',re.S) match_ct = re.findall(pattern,submitpage_content) grab_content =match_ct[0] #用try-except可以在報錯之後繼續執行下去 try: with open(folder+"\\"+fileName,"w") as f: f.write(grab_content) except: print(str(pair_table.ix[i,'name'])+"\terror\t"+submitpageURL)