系統:Mac OS X 10.10.1
編輯器: Sublime Text2
Python版本: 2.7.8
模組依賴: import sys, string, re, random, urllib, urllib2, cookielib, getpass(均為系統內的模組
)
0. 抓站小結
0.1. cookie處理
需要進行登陸的時候, 要進行cookie的處理,使用以下方法
1 2 3 4 5 |
cookie = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie)) urllib2.install_opener(opener) req = urllib2.Request(url) content = urllib2.urlopen(req) |
0.2. 表單處理
某些網站需要進行賬戶和密碼登陸, 需要使用POST方法向伺服器傳送賬戶和密碼錶單資料, 這裡就需要模擬登陸.
如何獲取表單資料的格式呢?
通過谷歌瀏覽器開發者工具中Network鎖定請求頭部和post發出的表單資料,偽裝表單資料
當由於Method太多, 找不到POST提交登入請求Method方法的時候, 可以嘗試使用錯誤密碼, 這樣就可以容易的找POST方法對應的頭部.
1 2 3 4 5 |
form_data = urllib.urlencode({ #注意urlencode方法 "email": self.user_name, "password": self.password, "webrequest": "true" }) |
0.3. 防盜鏈和偽裝成瀏覽器訪問
防盜鏈就是需要在請求的頭部加入Referer
欄位, Referer 指的是HTTP頭部的一個欄位, 用來表示從哪兒連結到目前的網頁,採用的格式是URL。換句話說,藉著 HTTP Referer 頭部網頁可以檢查訪客從哪裡而來,這也常被用來對付偽造的跨網站請求。
偽裝成瀏覽器就是將User-Agent設定為瀏覽器的欄位
1 2 3 4 5 6 7 |
user_agent = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/38.0.2125.111 Safari/537.36") request_header = { "Referer": "https://accounts.coursera.org/signin", #對付防盜鏈設定, 為跳轉來源的url "User-Agent": user_agent, #偽裝成瀏覽器訪問 } |
1. 偽裝頭部
使用谷歌瀏覽器自帶的開發者工具
, 選擇Network
(Element用來檢視網站原始碼等功能
), 獲取詳細的GET和POST方法, 從中獲取登入請求
的的頭部資訊,
從POST中獲得Headers資訊如下(省略部分不重要資訊
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Request URL:https://accounts.coursera.org/api/v1/login //真正的登陸驗證頁面 Request Method:POST Status Code:401 Unauthorized //Request Headers Connection:keep-alive Content-Length:55 Content-Type:application/x-www-form-urlencoded Cookie:(省略cookie資訊, 下面詳細介紹) ... Referer:https://accounts.coursera.org/signin //防盜鏈設定 User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 //瀏覽器瀏覽標識 //下面四行為伺服器所做的限制欄位 X-CSRF2-Cookie:csrf2_token_el67QDLg X-CSRF2-Token:1oxZDVMuZGX0qCggdReQyj2R X-CSRFToken:WnVtiMDpvw0JXJqHjPrFk0EU X-Requested-With:XMLHttpRequest //Form Data email:1095...@qq.com //Coursera賬戶資訊 password:FAFA //賬戶密碼 webrequest:true //固定欄位 |
這樣就能寫出模擬頭部的函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def structure_headers(self) : #模擬表單資料,這個引數不是字典 form_data = urllib.urlencode({ "email": self.user_name, "password": self.password, "webrequest": "true" }) user_agent = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/38.0.2125.111 Safari/537.36") request_header = { "Referer": "https://accounts.coursera.org/signin", #對付防盜鏈設定, 為跳轉來源的url "User-Agent": user_agent, #偽裝成瀏覽器訪問 } return form_data, request_header |
試了幾次竟然都是400錯誤
, 也就是頭部請求的格式不正確, 通過多次Headers檢視, 發現有下面四處不同的頭部
1 2 3 4 |
X-CSRF2-Cookie:csrf2_token_hTu4Zy8Y 最後八位不同 X-CSRF2-Token:O5OIRan9I99lTHmnYS27ocYb 完全隨機 X-CSRFToken:HClYbs9HZoGweU54iR5r5z2y 完全隨機 X-Requested-With:XMLHttpRequest 固定不變 |
通過放上搜尋找到了解決方案, coursera的請求頭部中
X-CSRF2-Token和X-CSRFToken
是完全隨機的,X-CSRF2-Cookie
後八位是隨機生成的, 都是由字母和數字隨機生成的.
於是修改程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
def structure_headers(self) : #模擬表單資料,這個引數不是字典 form_data = urllib.urlencode({ "email": self.user_name, "password": self.password, "webrequest": "true" }) user_agent = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/38.0.2125.111 Safari/537.36") XCSRF2Cookie = 'csrf2_token_%s' % ''.join(self.random_string(8)) XCSRF2Token = ''.join(self.random_string(24)) XCSRFToken = ''.join(self.random_string(24)) cookie = "csrftoken=%s; %s=%s" % (XCSRFToken, XCSRF2Cookie, XCSRF2Token) request_header = { "Referer": "https://accounts.coursera.org/signin", #對付防盜鏈設定, 為跳轉來源的url "User-Agent": user_agent, #偽裝成瀏覽器訪問 "X-Requested-With": "XMLHttpRequest", "X-CSRF2-Cookie": XCSRF2Cookie, "X-CSRF2-Token": XCSRF2Token, "X-CSRFToken": XCSRFToken, "Cookie": cookie } return form_data, request_header def random_string(self, length): return ''.join(random.choice(string.letters + string.digits) for i in xrange(length)) |
2. 模擬登陸
登陸coursra的下載頁面時計算機組成視訊下載, 會發現是要求登陸呢, 這時候就使用cookielib
模組進行cookie的處理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def simulation_login(self) : cookie = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie)) urllib2.install_opener(opener) form_data, request_header = self.structure_headers() req = urllib2.Request(self.login_url, data = form_data, headers = request_header) try : result = urllib2.urlopen(req) except urllib2.URLError,e : if hasattr(e, "code"): print "The server couldn't fulfill the request.Please check your url and read the Reason" print "Error code: %s" % e.code elif hasattr(e, "reason"): print "We failed to reach a server. Please check your url and read the Reason" print "Reason: %s" % e.reason sys.exit(2) if result.getcode() == 200 : print "登陸成功..." |
這個函式用於模擬登陸, 並顯示登陸成功或者失敗
3. 抓取下載連結
抓取連結通過正規表示式, 主要匹配PDF下載連結和MP4視訊下載連結
使用re.findall()
函式進行函式匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def get_links(self) : try : result = urllib2.urlopen(self.url) except urllib2.URLError,e : if hasattr(e, "code"): print "The server couldn't fulfill the request." print "Error code: %s" % e.code elif hasattr(e, "reason"): print "We failed to reach a server. Please check your url and read the Reason" print "Reason: %s" % e.reason sys.exit(2) content = result.read().decode("utf-8") print "讀取網頁成功..." down_links = re.findall(r' |
4. 寫入完本
既然已經匹配了所有的連結, 這一步就是將連結寫入到檔案中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def start_spider(self) : self.simulation_login() down_links, down_pdfs = self.get_links() with open("coursera.html", "w+") as my_file : print "下載連結的長度", len(down_links) for link in down_links : print link try : my_file.write(link + "n") except UnicodeEncodeError: sys.exit(2) with open("coursera.pdf", "w+") as my_file : print "下載pdf的長度", len(down_pdfs) for pdf in down_pdfs : try : my_file.write(pdf + "n") except UnicodeEncodeError : sys.exit(2) print "抓取Coursera課程下載連結和pdf連結成功" |
5. 登陸呼叫
使用getpass模組中的getpass()
輸入密碼, 使用這個函式輸入密碼的時候不會顯示任何字元, 貌似體驗不好, 下次修改一下, 然後命令列傳入下載課程的引數.
通過比較每門課下載頁面, 發現之後/lecture
前的這個欄位是不同的,這樣可以設定通過命令列傳入這這個引數
https://class.coursera.org/pkuco-001/lecture
https://class.coursera.org/electromagnetism-001/lecture
這裡在執行命令列只需要傳入
pkuco-001或者electromagnetism-001
這種欄位
1 2 3 4 5 6 7 8 9 10 |
def main() : if len(sys.argv) != 2 : print "Please Input what course you want to download.." sys.exit(2) url = "https://class.coursera.org/{course}/lecture" user_name = raw_input("Input your Email > ") password = getpass.getpass("Input your Password > ") spider = Coursera(url.format(course = sys.argv[1]), user_name, password) spider.start_spider() |
6. 下載指令碼
下載可以使用curl
, Mac下安裝方法
1 |
brew install curl |
編寫下載使用的Python指令碼, 編寫成功後, 修改指令碼檔案的檔案許可權
1 |
chmod 755 downloadshell.py #最後一個引數為Python指令碼名稱 |
執行下載指令碼
1 |
$python downloadshell.py coursera.pdf #最後一個引數為連線儲存檔案 |
下面是我自己編寫的簡單的下載指令碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#!/usr/bin/python2 # -*- coding:utf-8 -*- #簡單的檔案下載指令碼 import os import sys, re def read_file(file_name) : down_links = [] with open(file_name, "r") as my_file : for url in my_file : down_links.append(url.replace("n", "")) return down_links def main() : if len(sys.argv) != 2 : print "Please input file name..." sys.exit(2) down_links = read_file(sys.argv[1]) pdf_index = 1 mp4_index = 1 for index, link in enumerate(down_links) : if link.find("mp4") != -1 : os.system("curl " + link + " -o " + "%d.mp4" % mp4_index) elif link.find("pdf") != -1 : os.system("curl " + link + " -o " + "%d.pdf" % pdf_index) pdf_index += 1 else : print "請自行補全下載命令..." if __name__ == '__main__': main() |