網絡上大部分關於python爬蟲的介紹以及代碼講解,都用的是python2.7或如下版本,用python3.x版本的甚少。html
在python3.3.2版本中,沒有urllib2這個庫,也沒有cookiejar這個庫。對應的庫分別是http.cookiejar以及urllib這倆。java
關於url以及python2.7爬蟲寫法的介紹,能夠參考[Python]網絡爬蟲(一):抓取網頁的含義和URL基本構成這系列的文章。我這裏主要介紹的是python3.3.2的urllib2的實現。python
首先,下面是一個簡單的抓取網頁信息程序,主要是介紹下urllib的使用。c++
# 簡單的抓取HOJ主頁的數據 import urllib.request response = urllib.request.urlopen('http://acm.hit.edu.cn') html = response.read() print(html) # 注意,因爲網頁抓取下來的是bytes格式的數據,因此寫入文件時須要以二進制的方式寫入 fout = open('txt.txt','wb') fout.write(html) fout.close() fout = open('html.html','wb') # 寫入到文件html.html fout.write(html) fout.close()
運行結果應該是txt.txt,html.html兩個文件。咱們能夠打開看看txt.txt、html.html看看,發現txt.txt的數據跟瀏覽器查看的網頁源代碼一致。若是用瀏覽器打開html.html,會發現這個跟咱們實際上考看到的網頁基本同樣。正則表達式
模擬HOJ登錄並抓取已經AC的代碼算法
咱們須要進行登錄,須要設置一個cookie處理器,它負責從服務器下載cookie到本地,而且在發送請求時帶上本地的cookie。python3.x
# 設置一個cookie處理器,它負責從服務器下載cookie到本地,而且在發送請求時帶上本地的cookie cj = http.cookiejar.LWPCookieJar() cookie_support = urllib.request.HTTPCookieProcessor(cj) opener = urllib.request.build_opener(cookie_support, urllib.request.HTTPHandler) urllib.request.install_opener(opener)
登錄時須要對HOJ服務器發送數據請求,對於發送data表單數據,咱們怎麼查看須要發送的數據?在HTTP中,這個常常使用熟知的POST請求發送。瀏覽器
咱們能夠先查看網頁源代碼,看到服務器
看到三個name,因此發送的求情數據中須要包含這三個,cookie
# 構造Post數據,從抓大的包裏分析得出的或者經過查看網頁源代碼能夠獲得 data = { 'user' : user, # 你的用戶名 'password' : password, # 你的密碼,密碼多是明文傳輸也多是密文,若是是密文須要調用相應的加密算法加密 'submit' : 'Login' # 特有數據,不一樣網站可能不一樣 }
另外,通常的HTML表單,data須要編碼成標準形式。而後作爲data參數傳到Request對象。因此咱們須要對data進行編碼。
data = urllib.parse.urlencode(data).encode('utf-8')
發送請求,獲得服務器給咱們的響應,完成登陸功能。header最好加上,否則因爲內部信息默認顯示爲機器代理,可能被服務器403 Forbidden拒絕訪問。
# 發送請求,獲得服務器給咱們的響應 response = urllib.request.Request(url, data,header) # 經過urllib提供的request方法來向指定Url發送咱們構造的數據,並完成登陸過程 urllib.request.urlopen(response)
登陸函數以下:
def login(): # 登錄函數 print('請輸入你的帳號') user = input() print('請輸入你的密碼') password = input() # 設置一個cookie處理器,它負責從服務器下載cookie到本地,而且在發送請求時帶上本地的cookie cj = http.cookiejar.LWPCookieJar() cookie_support = urllib.request.HTTPCookieProcessor(cj) opener = urllib.request.build_opener(cookie_support, urllib.request.HTTPHandler) urllib.request.install_opener(opener) url = 'http://acm.hit.edu.cn/hoj/system/login' # 登錄的界面 # 這個最好加上,否則因爲內部信息默認顯示爲機器代理,可能被服務器403 Forbidden拒絕訪問 header={'User-Agent':'Magic Browser'} # 構造Post數據,從抓大的包裏分析得出的或者經過查看網頁源代碼能夠獲得 data = { 'user' : user, # 你的用戶名 'password' : password, # 你的密碼,密碼多是明文傳輸也多是密文,若是是密文須要調用相應的加密算法加密 'submit' : 'Login' # 特有數據,不一樣網站可能不一樣 } data = urllib.parse.urlencode(data).encode('utf-8') # 發送請求,獲得服務器給咱們的響應 response = urllib.request.Request(url, data,header) # 經過urllib提供的request方法來向指定Url發送咱們構造的數據,並完成登陸過程 urllib.request.urlopen(response) return
登陸進去後,咱們須要對已經AC的代碼進行抓取。抓取的連接爲:
http://acm.hit.edu.cn/hoj/problem/solution/?problem=題目編號
url = 'http://acm.hit.edu.cn/hoj/problem/solution/?problem='+str(i) response = urllib.request.urlopen(url) html = response.read()
咱們能夠把html輸出看看具體內容是什麼。
經過查看網頁代碼,發現提交的代碼都是在<span></span>標籤中。
所以咱們能夠對這部分數據進行處理,處理過程當中咱們能夠用到python的正則表達式進行搜索。而且對於特殊的編碼如<須要進行解碼。
固然,咱們須要注意保存的文件究竟是java仍是c++。
最後,詳細的代碼以下:
import urllib import http.cookiejar #import time hashTable = { # 網頁特殊編碼轉化 '<':'<', '>': '>', '{':'{', '}':'}', '(':'(', ')':')', ' ':' ', '&':'&', '[':'[', ']':']', '"':'"' } def login(): # 登錄函數 print('請輸入你的帳號') user = input() print('請輸入你的密碼') password = input() # 設置一個cookie處理器,它負責從服務器下載cookie到本地,而且在發送請求時帶上本地的cookie cj = http.cookiejar.LWPCookieJar() cookie_support = urllib.request.HTTPCookieProcessor(cj) opener = urllib.request.build_opener(cookie_support, urllib.request.HTTPHandler) urllib.request.install_opener(opener) url = 'http://acm.hit.edu.cn/hoj/system/login' # 登錄的界面 # 這個最好加上,否則因爲內部信息默認顯示爲機器代理,可能被服務器403 Forbidden拒絕訪問 header={'User-Agent':'Magic Browser'} # 構造Post數據,從抓大的包裏分析得出的或者經過查看網頁源代碼能夠獲得 data = { 'user' : user, # 你的用戶名 'password' : password, # 你的密碼,密碼多是明文傳輸也多是密文,若是是密文須要調用相應的加密算法加密 'submit' : 'Login' # 特有數據,不一樣網站可能不一樣 } data = urllib.parse.urlencode(data).encode('utf-8') # 發送請求,獲得服務器給咱們的響應 response = urllib.request.Request(url, data,header) # 經過urllib提供的request方法來向指定Url發送咱們構造的數據,並完成登陸過程 urllib.request.urlopen(response) return def solve(html,i): txt = html.decode('gbk','ignore') start = txt.find('<span') if start==-1: # 沒有span,表示此題沒AC return p = '.java' if txt.find('import')==-1: p = '.cpp' fout = open('txt_'+str(i)+p,'w') while True: end = txt.find('<span',start+5) if end==-1: end = txt.find('</span>',start) x = txt.find('>',start)+1 w = '' ok = True while x<end: if txt[x]=='<': ok = False elif txt[x]=='>': ok = True if not ok or txt[x]=='>': x += 1 continue if txt[x]=='&': # 進行特殊的解碼 t4 = txt[x:x+4] t5 = txt[x:x+5] t6 = txt[x:x+6] if t4 in hashTable: w += hashTable[t4] x += 4 elif t5 in hashTable: w += hashTable[t5] x += 5 elif t6 in hashTable: w += hashTable[t6] x += 6 else: w += txt[x] x += 1 else: w += txt[x] x += 1 fout.write(w) if end==start: break start = end fout.close() return def run(): # 抓取全部AC代碼 for i in range(1001,3169): url = 'http://acm.hit.edu.cn/hoj/problem/solution/?problem='+str(i) response = urllib.request.urlopen(url) html = response.read() solve(html,i) #time.sleep(0.5) # 時間間隔爲0.5s發送一次抓取請求,減輕hoj服務器壓力 return login() run()
我全部的HOJ AC代碼,在http://pan.baidu.com/s/1gdsX9DL
提交AC代碼:
這個其實跟登錄並抓取AC代碼原理基本同樣。
# 構造Post數據,從網頁數據分析得出 data = { 'Proid' : num, 'Language' : language, 'Source' : txt # 能夠經過查看網頁源代碼看出 }
另外,咱們須要用到listdir()函數,以此來得到當前目錄下全部的文件。
這裏咱們須要設置time.sleep(3) ,間隔三秒遞交一次代碼,減輕服務器壓力,以避免使得HOJ服務器崩了。
總的代碼以下:
import urllib import http.cookiejar import time from os import listdir def login(): print('請輸入你的帳號') user = input() print('請輸入你的密碼') password = input() cj = http.cookiejar.LWPCookieJar() cookie_support = urllib.request.HTTPCookieProcessor(cj) opener = urllib.request.build_opener(cookie_support, urllib.request.HTTPHandler) urllib.request.install_opener(opener) url = 'http://acm.hit.edu.cn/hoj/system/login' header = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:14.0) Gecko/20100101 Firefox/14.0.1', 'Referer' : '******'} # 構造Post數據,他也是從抓大的包裏分析得出的。 data = {'op' : 'dmlogin', 'f' : 'st', 'user' : user, # 用戶名 'password' : password, # 密碼,密碼多是明文傳輸也多是密文,若是是密文須要調用相應的加密算法加密 'submit' : 'Login' # 特有數據,不一樣網站可能不一樣 } data = urllib.parse.urlencode(data).encode('utf-8') request = urllib.request.Request(url, data,header) # 經過urllib2提供的request方法來向指定Url發送咱們構造的數據,並完成登陸過程 urllib.request.urlopen(request) return def solve(file): # file:爲文件名,格式是 txt_problemNumber.cpp或者 txt_problemNumber.java ed = file.find('.') language = file[ed+1:len(file)] if language!='cpp' and language!='java': return if language=='cpp': language = 'C++' else: language = 'Java' st = file.find('_') num = file[st+1:ed] url = 'http://acm.hit.edu.cn/hoj/problem/submit/?id='+num header = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:14.0) Gecko/20100101 Firefox/14.0.1', 'Referer' : '******'} fin = open(file,'rb') txt = fin.read().decode('gbk','ignore') fin.close() # 構造Post數據,從網頁數據分析得出 data = { 'Proid' : num, 'Language' : language, 'Source' : txt # 能夠經過查看網頁源代碼看出 } data = urllib.parse.urlencode(data).encode('utf-8') # 使用UTF-8編碼方式進行編碼 request = urllib.request.Request(url, data,header) # 添加消息頭 # 經過urllib2提供的request方法來向指定Url發送咱們構造的數據,並完成登陸過程 urllib.request.urlopen(request) time.sleep(3) # 間隔三秒遞交一次代碼,減輕服務器壓力 return def run(): allFile = listdir() # 得到當前目錄下全部的文件 for file in allFile: solve(file) return login() run()
codeforces爬AC代碼(有些網頁格式不太同樣,最後直接把那些題目忽略了,囧)(CF python代碼還沒有正確,有待改進。。。)
import urllib user = 'yejinru' # 用戶 page = 21 # 提交的頁數 def solve(html,name): txt = html.decode('utf-8','ignore') cpp = '.cpp' start = txt.find('include')-1 if start==-2: start = txt.find('public static void Main') if start==-1: cpp = '.py' else: cpp = '.java' end = txt.find('</pre>',start+100) fout = open('cf/'+name+cpp,'wb') pro = txt[start:end] pro = pro.replace(u'<',u'<') pro = pro.replace(u'>',u'>') pro = pro.replace(u'"',u'"') pro = pro.replace(u'&',u'&') fout.write(pro.encode('gbk','ignore')) fout.close() return def fun(html): txt = html.decode('utf-8','ignore') pre = 0 while True: cur = txt.find('<span class=\'verdict-accepted\'>',pre) if cur==-1: return pre = cur+100 p = txt.find( '"',txt.find('submissionId="',cur-50) )+1 pid = '' while txt[p]!='"': pid += txt[p] p += 1 p = txt.find('data-problemId="',cur-300) if p==-1: return p = txt.find('>',txt.find('<a href="/',p))+19 if p==18: return tid = '' while txt[p]>='0' and txt[p]<='9': tid += txt[p] p += 1 if len(tid)>3: continue url = 'http://codeforces.com/contest/'+tid+'/submission/'+pid print(url) response = urllib.request.urlopen(url) html = response.read() solve(html,tid+txt[p]) fout = open('cf/'+pid+'.html','wb') fout.write(html) fout.close() return def run(): add = '/page/' first = 'http://codeforces.com/submissions/'+user for i in range(1,page+1): # 這是由於http://codeforces.com/submissions/yejinru這裏只有21頁 url = '' if i==1: url = first else: url = first+add+str(i) response = urllib.request.urlopen(url) html = response.read() fun(html) return run()