python爬蟲入門(二)Opener和Requests
Handler和Opener
Handler處理器和自定義Opener
opener是urllib2.OpenerDirector的例項,我們之前一直在使用urlopen,它是一個特殊的opener(也就是我們構建好的)。
但是urlopen()方法不支援代理、cookie等其他的HTTP/GTTPS高階功能。所有要支援這些功能:
1.使用相關的Handler處理器來建立特定功能的處理器物件;
2.然後通過urllib2.build_opener()方法使用這些處理器物件,建立自定義opener物件;
3.使用自定義的opener物件,呼叫open()方法傳送請求。
如果程式裡所有的請求都使用自定義的opener,可以使用urllib2.install_open()將自定義的opener物件定義為全域性opener,表示如果之後凡是呼叫urlopen,都將使用這個opener(根據自己的需求來選擇)
簡單的自定義opener()
# _*_ coding:utf-8 _*_ import urllib2 # 構建一個HTTPHandler處理器物件,支援處理HTTP的請求 http_handler = urllib2.HTTPHandler() # 呼叫build_opener()方法構建一個自定義的opener物件,引數是構建的處理器物件 opener = urllib2.build_opener(http_handler) request = urllib2.Request('http://www.baidu.com/s')
# 呼叫自定義opener物件的open()方法,傳送request請求response = opener.open(request)
print response.read()
debug log模式
可以在HTTPHandler裡面加引數(debuglevel=1)開啟
# _*_ coding:utf-8 _*_ import urllib2 # 構建一個HTTPHandler處理器物件,支援處理HTTP的請求 # http_handler = urllib2.HTTPHandler() # 主要用於除錯用 http_handler = urllib2.HTTPHandler(debuglevel=1) # 呼叫build_opener()方法構建一個自定義的opener物件,引數是構建的處理器物件opener = urllib2.build_opener(http_handler) request = urllib2.Request('http://www.baidu.com/s') response = opener.open(request) # print response.read()
ProxyHandler處理器(代理設定)
使用代理IP,這是爬蟲/反爬蟲的第二大招,通常也是最好用的。
很多網站會檢測某一段時間某個IP的訪問次數(通過流量統計,系統日誌等),如果訪問次數多的不像正常人,它會禁止這個IP的訪問。
所以我們可以設定一些代理伺服器,每隔一段時間換一個代理,就算IP被禁止,依然可以換個IP繼續爬取。
urllib2中通過ProxyHandler來設定使用代理伺服器,使用自定義opener來使用代理:
免費代理網站:http://www.xicidaili.com/;https://www.kuaidaili.com/free/inha/
# _*_ coding:utf-8 _*_ import urllib2 # 構建一個Handler處理器物件,引數是一個字典型別,包括代理型別和代理伺服器IP+Port httpproxy_handler = urllib2.ProxyHandler({'http':'118.114.77.47:8080'}) #使用代理 opener = urllib2.build_opener(httpproxy_handler) request = urllib2.Request('http://www.baidu.com/s') #1 如果這麼寫,只有使用opener.open()方法傳送請求才使用自定義的代理,而urlopen()則不使用自定義代理。 response = opener.open(request) #12如果這麼寫,就是將opener應用到全域性,之後所有的,不管是opener.open()還是urlopen() 傳送請求,都將使用自定義代理。 #urllib2.install_opener(opener) #response = urllib2.urlopen(request) print response.read()
但是,這些免費開放代理一般會有很多人都在使用,而且代理有壽命短,速度慢,匿名度不高,HTTP/HTTPS支援不穩定等缺點(免費沒好貨),所以,專業爬蟲工程師會使用高品質的私密代理
私密代理
(代理伺服器都有使用者名稱和密碼)必須先授權才能用
# _*_ coding:utf-8 _*_ import urllib2 #必須輸入使用者名稱密碼,ip和port authproxy_handler = urllib2.ProxyHandler({'http':'username:[email protected]:port}) opener = urllib2.build_opener(authproxy_handler) request = urllib2.Request('http://www.baidu.com/s') response = opener.open(request) print response.read()
為了安全一般把私密代理ip使用者名稱密碼儲存到系統環境變數裡面,再讀出來
# _*_ coding:utf-8 _*_ import urllib2 import os # 從環境變數裡面取到使用者名稱和密碼 proxyuser = os.environ.get('proxuser') proxypasswd = os.environ.get('proxpasswd') #必須輸入使用者名稱密碼,ip和port authproxy_handler = urllib2.ProxyHandler({'http':proxyuser+':'+proxypasswd+'@ip:port'}) opener = urllib2.build_opener(authproxy_handler) request = urllib2.Request('http://www.baidu.com/s') response = opener.open(request) print response.read()
Cookielib庫和HTTPCookieProcess處理器
Cookie :是指某些網站伺服器為了辨別使用者身份和進行Session跟蹤,而儲存在使用者瀏覽器上的文字檔案,Cookie可以保持登入資訊到使用者下次與伺服器的會話。
cookielib
模組:主要作用是提供用於儲存cookie的物件
HTTPCookieProcessor
處理器:主要作用是處理這些cookie物件,並構建handler物件。
cookie庫
該模組主要的物件有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。
CookieJar:管理HTTP cookie值、儲存HTTP請求生成的cookie、向傳出的HTTP請求新增cookie的物件。整個cookie都儲存在記憶體中,對CookieJar例項進行垃圾回收後cookie也將丟失。
FileCookieJar (filename,delayload=None,policy=None):從CookieJar派生而來,用來建立FileCookieJar例項,檢索cookie資訊並將cookie儲存到檔案中。filename是儲存cookie的檔名。delayload為True時支援延遲訪問訪問檔案,即只有在需要時才讀取檔案或在檔案中儲存資料。
MozillaCookieJar (filename,delayload=None,policy=None):從FileCookieJar派生而來,建立與
Mozilla瀏覽器 cookies.txt相容
的FileCookieJar例項。LWPCookieJar (filename,delayload=None,policy=None):從FileCookieJar派生而來,建立與
libwww-perl標準的 Set-Cookie3 檔案格式
相容的FileCookieJar例項。
其實大多數情況下,我們只用CookieJar(),如果需要和本地檔案互動,就用 MozillaCookjar() 或 LWPCookieJar()
下面通過例項登入人人網,學習cookieJar()用法
登入人人網
# _*_ coding:utf-8 _*_ import urllib2 import urllib import cookielib #通過CookieJar()類構建一個cookieJar物件,用來儲存cookie的值 cookie = cookielib.CookieJar() #通過HTTPCookieProcessor()處理器類構建一個處理器物件,用來處理cookie #引數就是構建的CookieJar()物件 cookie_handler = urllib2.HTTPCookieProcessor(cookie) #構建一個自定義的opener opener = urllib2.build_opener(cookie_handler) # 通過自定義opener的addheaders的引數,可以新增HTTP報頭引數 opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36')] #renren網的登入介面 url = 'http://www.renren.com/PLogin.do' #需要登入的賬號密碼 data = {'email':'15********','password':'py********'} # 通過urlencode()編碼轉換 data = urllib.urlencode(data) # 第一次是POST請求,傳送登入需要的引數,獲取cookie request = urllib2.Request(url,data = data) response = opener.open(request) print response.read()
有了cookie之後,可以直接爬取其它頁面
# _*_ coding:utf-8 _*_ import urllib2 import urllib import cookielib #通過CookieJar()類構建一個cookieJar物件,用來儲存cookie的值 cookie = cookielib.CookieJar() #通過HTTPCookieProcessor()處理器類構建一個處理器物件,用來處理cookie #引數就是構建的CookieJar()物件 cookie_handler = urllib2.HTTPCookieProcessor(cookie) #構建一個自定義的opener opener = urllib2.build_opener(cookie_handler) # 通過自定義opener的addheaders的引數,可以新增HTTP報頭引數 opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36')] #renren網的登入介面 url = 'http://www.renren.com/PLogin.do' #需要登入的賬號密碼 data = {'email':'15********','password':'python********'} # 通過urlencode()編碼轉換 data = urllib.urlencode(data) request = urllib2.Request(url,data = data) response = opener.open(request) # print response.read() # 可以直接爬取登入後的其它頁面了 response_other = opener.open('http://friend.renren.com/managefriends') print response_other.read()
Requests模組
安裝:直接pip install requests
Requests 繼承了urllib2的所有特性。Requests支援HTTP連線保持和連線池,支援使用cookie保持會話,支援檔案上傳,支援自動確定響應內容的編碼,支援國際化的 URL 和 POST 資料自動編碼。
新增headers和查詢引數
# _*_ coding:utf-8 _*_ import requests kw = {'wd':'python'} headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'} # params 接收一個字典或者字串的查詢引數,字典型別自動轉換為url編碼,不需要urlencode() response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers) # 檢視響應內容,response.text 返回的是Unicode格式的資料 print response.text # 檢視響應內容,response.content返回的位元組流資料 print response.content # 檢視完整url地址 print response.url # # 檢視響應頭部字元編碼 print response.encoding # 檢視響應碼 print response.status_code
使用代理
# _*_ coding:utf-8 _*_ import requests headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'} # 根據協議型別,選擇不同的代理 proxies = { "http": "http://119.28.152.208:80", } response = requests.get("http://www.baidu.com/", proxies = proxies,headers=headers) print response.text
私密代理驗證
urllib2 這裡的做法比較複雜,requests只需要一步:
# _*_ coding:utf-8 _*_ import requests headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'} proxy = { "http": "name:[email protected]:port" } response = requests.get("http://www.baidu.com/", proxies = proxy,headers=headers) print response.text
web客戶端驗證
import requests auth=('test', '123456') response = requests.get('http://192.168.xxx.xx', auth = auth) print response.text
session
在 requests 裡,session物件是一個非常常用的物件,這個物件代表一次使用者會話:從客戶端瀏覽器連線伺服器開始,到客戶端瀏覽器與伺服器斷開。
會話能讓我們在跨請求時候保持某些引數,比如在同一個 Session 例項發出的所有請求之間保持 cookie 。
登入人人網
# _*_ coding:utf-8 _*_ import requests # 1. 建立session物件,可以儲存Cookie值 ssion = requests.session() # 2. 處理 headers headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'} # 3. 需要登入的使用者名稱和密碼 data = {"email":"158xxxxxxxx", "password":"pythonxxxxxxx"} # 4. 傳送附帶使用者名稱和密碼的請求,並獲取登入後的Cookie值,儲存在ssion裡 ssion.post("http://www.renren.com/PLogin.do", data = data) # 5. ssion包含使用者登入後的Cookie值,可以直接訪問那些登入後才可以訪問的頁面 response = ssion.get("http://zhibo.renren.com/news/108") # 6. 列印響應內容 print response.text
頁面解析和資料處理
爬蟲一共就四個主要步驟:
- 明確目標 (要知道你準備在哪個範圍或者網站去搜索)
- 爬 (將所有的網站的內容全部爬下來)
- 取 (去掉對我們沒用處的資料)
- 處理資料(按照我們想要的方式儲存和使用)
一般來講對我們而言,需要抓取的是某個網站或者某個應用的內容,提取有用的價值。內容一般分為兩部分,非結構化資料和結構化資料。
非結構化資料:先有資料,再有結構
結構化資料:先有結構,再有資料
1.非結構化的資料處理
1.文字、電話號碼、郵箱地址 -->正則表示式 2.HTML檔案 -->正則表示式,XPath,CSS選擇器
2.結構化的資料處理
1.JSON檔案 -->JSON Path -->轉化成python型別進行操作 2.XML檔案 -->轉化成python型別(xmltodict) -->XPath -->CSS選擇器 -->正則表示式
正則表示式
簡單回顧下python正則表示式的一些使用方法
正則表示式測試網站:http://tool.oschina.net/regex/#
re 模組的一般使用步驟如下:
-
使用
compile()
函式將正則表示式的字串形式編譯為一個Pattern
物件 -
通過
Pattern
物件提供的一系列方法對文字進行匹配查詢,獲得匹配結果,一個 Match 物件。 - 最後使用
Match
物件提供的屬性和方法獲得資訊,根據需要進行其他的操作
pattern = re.compile('\d') #將正則表示式編譯成一個pattern規則物件 pattern.match() #從起始位置開始往後查詢,返回第一個符合規則的,只匹配一次 pattern.search() #從任意位置開始往後查詢,返回第一個符合規則的,只匹配一次 pattern.findall() #所有的全部匹配,返回列表 pattern.finditer() #所有的全部匹配,返回的是一個迭代器 pattern.split() #分割字串,返回列表 pattern.sub() #替換
re.I #表示忽略大小寫
re.S #表示全文匹配
1.match()
import re pattern = re.compile('\d+') m = pattern.match('aaa123bbb456',3,5) #可以指定match起始和結束的位置match(string,begin,end) print m.group() #12 m = pattern.match('aaa123bbb456',3,6) print m.group() #123
import re #匹配兩組, re.I忽略大小寫 pattern = re.compile(r"([a-z]+) ([a-z]+)",re.I) #第一組(字母)和第二組(字母)之間以空格分開 m = pattern.match("Hello world and Python") print m.group(0) #Hello world group(0)獲取所有子串 print m.group(1) #Hello group(1)所有子串裡面的第一個子串 print m.group(2) #world group(2)所有子串裡面的第二個子串
2.search()
import re pattern = re.compile(r'\d+') m = pattern.search('aaa123bbb456') print m.group() #123 m = pattern.search('aaa123bbb456',2,5) print m.group() #12
3.findall()
import re pattern = re.compile(r'\d+') m = pattern.findall('hello 123456 789') # print m #['123456', '789'] m = pattern.findall('hello 123456 789',5,10) print m #['1234']
4.split()
# _*_ coding:utf-8 _*_ import re pattern = re.compile(r'[\s\d\\\;]+') #以空格,數字,'\',';'做分割 m = pattern.split(r'a b22b\cc;d33d ee') print m #['a', 'b', 'b', 'cc', 'd', 'd', 'ee']
5.sub()
# _*_ coding:utf-8 _*_ import re pattern = re.compile(r'(\w+) (\w+)') str = 'good 111,job 222' m = pattern.sub('hello python',str) print m #hello python,hello python m = pattern.sub(r"'\1':'\2'",str) print m #'good':'111','job':'222'
# _*_ coding:utf-8 _*_ import re pattern = re.compile(r'\d+') str = 'a1b22c33d4e5f678' m = pattern.sub('*',str) #a*b*c*d*e*f* 把數字替換成'*' print m
內涵段子例項
爬取貼吧所有內容,並通過正則表示式爬取出所有的段子
url變化
-
第一頁url: http: //www.neihan8.com/article/list_5_1 .html
-
第二頁url: http: //www.neihan8.com/article/list_5_2 .html
-
第三頁url: http: //www.neihan8.com/article/list_5_3 .html
pattern = re.compile('<dd\sclass="content">(.*?)</dd>', re.S)
每個段子內容都是在 <dd class='content'>......</dd>裡面,通過正則可以獲取內
#!/usr/bin/env python # -*- coding:utf-8 -*- import urllib2 import re
class Spider: def __init__(self): # 初始化起始頁位置 self.page = 1 # 爬取開關,如果為True繼續爬取 self.switch = True def loadPage(self): """ 作用:下載頁面 """ print "正在下載資料...." url = "http://www.neihan.net/index_" + str(self.page) + ".html" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'} request = urllib2.Request(url, headers = headers) response = urllib2.urlopen(request) # 獲取每頁的HTML原始碼字串 html = response.read() #print html # 建立正則表示式規則物件,匹配每頁裡的段子內容,re.S 表示匹配全部字串內容 pattern = re.compile('<dd\sclass="content">(.*?)</dd>', re.S) # 將正則匹配物件應用到html原始碼字串裡,返回這個頁面裡的所有段子的列表 content_list = pattern.findall(html) # 呼叫dealPage() 處理段子裡的雜七雜八 self.dealPage(content_list) def dealPage(self, content_list): """ 處理每頁的段子 content_list : 每頁的段子列表集合 """ for item in content_list: # 將集合裡的每個段子按個處理,替換掉無用資料 item = item.replace("<p>","").replace("</p>", "").replace("<br/>", "") # 處理完後呼叫writePage() 將每個段子寫入檔案內 self.writePage(item) def writePage(self, item): """ 把每條段子逐個寫入檔案裡 item: 處理後的每條段子 """ # 寫入檔案內 print "正在寫入資料...." with open("tieba.txt", "a") as f: f.write(item) def startWork(self): """ 控制爬蟲執行 """ # 迴圈執行,直到 self.switch == False while self.switch: # 使用者確定爬取的次數 self.loadPage() command = raw_input("如果繼續爬取,請按回車(退出輸入quit)") if command == "quit": # 如果停止爬取,則輸入 quit self.switch = False # 每次迴圈,page頁碼自增1 self.page += 1 print "謝謝使用!" if __name__ == "__main__": duanziSpider = Spider() duanziSpider.startWork()
可以按回車接著爬取下一頁內容,輸入QUIT退出。
爬取後的內容: