python 3.3.2 爬蟲記錄

  網絡上大部分關於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的正則表達式進行搜索。而且對於特殊的編碼如&lt;須要進行解碼。

  固然,咱們須要注意保存的文件究竟是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()
相關文章
相關標籤/搜索