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:pwd@ip: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:pwd@ip: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退出。
爬取后的內容: