1. 程式人生 > >python爬蟲入門(二)Opener和Requests

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. 明確目標 (要知道你準備在哪個範圍或者網站去搜索)
  2. 爬 (將所有的網站的內容全部爬下來)
  3. 取 (去掉對我們沒用處的資料)
  4. 處理資料(按照我們想要的方式儲存和使用)

一般來講對我們而言,需要抓取的是某個網站或者某個應用的內容,提取有用的價值。內容一般分為兩部分,非結構化資料和結構化資料。

非結構化資料:先有資料,再有結構

結構化資料:先有結構,再有資料

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 模組的一般使用步驟如下:

  1. 使用 compile() 函式將正則表示式的字串形式編譯為一個 Pattern 物件

  2. 通過 Pattern 物件提供的一系列方法對文字進行匹配查詢,獲得匹配結果,一個 Match 物件。

  3. 最後使用 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退出。

 爬取後的內容: