#============================爬蟲準備工作============================
# 參考資料
# python網絡資料采集, 圖靈工業出版
# 精通Python爬蟲架構Scrapy, 人民郵電出版社
# [Python3網絡爬蟲](http://blog.csdn.net/c406495762/article/details/72858983)
# [Scrapy官方教程](http://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html)
# 前提知識
# url
# http協定
# web前端,html, css, js
# ajax
# re, xpath
# xml
#============================爬蟲簡介============================
# 爬蟲定義:網絡爬蟲(又被稱為網頁蜘蛛,網絡機器人,在FOAF社群中間,更經常的稱為網頁追逐者),
#是一種按照一定的規則,自動地抓取網際網路資訊的程式或者腳本。
#另外一些不常使用的名字還有螞蟻、自動索引、模拟程式或者蠕蟲。
# 兩大特征
# 能按作者要求下載下傳資料或者内容
# 能自動在網絡上流竄
# 三大步驟:
# 下載下傳網頁
# 提取正确的資訊
# 根據一定規則自動跳到另外的網頁上執行上兩步内容
# 爬蟲分類
# 通用爬蟲
# 專用爬蟲(聚焦爬蟲)
# Python網絡包簡介
# Python2.x:urllib, urllib2, urllib3, httplib, httplib2, requests
# Python3.x: urllib, urllib3, httplib2, requests
# python2: urllib和urllib2配合使用,或者requests
# Python3: urllib,requests
#==========================urllib=========================
# 包含子產品
# urllib.request: 打開和讀取urls
# urllib.error: 包含urllib.request産生的常見的錯誤,使用try捕捉
# urllib.parse: 包含解析url的方法
# urllib.robotparse: 解析robots.txt檔案
# 案例v1:
from urllib import request
url = "http://jobs.zhaopin.com/..."
# 打開相應url并把相應頁面作為傳回
rsp = request.urlopen(url)
# 把傳回結果讀取出來
# 讀取出來内容類型為bytes
html = rsp.read()
# 如果想把bytes内容轉換成字元串,需要解碼
html = html.decode("utf-8")
# 網頁編碼問題解決
# chardet 可以自動檢測頁面檔案的編碼格式,但是,可能有誤
# 需要安裝, conda install chardet
# 案例v2片段:
#利用 chardet自動檢測
cs = chardet.detect(html)
# 使用get取值保證不會出錯
html = html.decode(cs.get("encoding", "utf-8"))
# urlopen 的傳回對象
# geturl: 傳回請求對象的url
# info: 請求回報對象的meta資訊
# getcode:傳回的http code
# request.data 的使用
# 通路網絡的兩種方法
# get:
# 利用參數給伺服器傳遞資訊,
# 參數為dict,然後用parse編碼
# 案例v4片段:
from urllib import request, parse
url = 'http://www.baidu.com/s?'
wd = input("Input your keyword:")
# 要想使用data, 需要使用字典結構
qs = {"wd": wd}
# 轉換url編碼
qs = parse.urlencode(qs)
fullurl = url + qs
# 如果直接用可讀的帶參數的url,是不能通路的
#fullurl = 'http://www.baidu.com/s?wd=大熊貓'
rsp = request.urlopen(fullurl)
html = rsp.read()
# post
# 一般向伺服器傳遞參數使用
# post是把資訊自動加密處理
# 我們如果想使用post資訊,需要用到data參數
# 使用post,意味着Http的請求頭可能需要更改:
# Content-Type: application/x-www.form-urlencode
# Content-ength: 資料長度
# 簡而言之,一旦更改請求方法,請注意其他請求頭部資訊相适應
# urllib.parse.urlencode可以将字元串自動轉換成上面的
# 案例v5:經典案例:
'''
利用parse子產品模拟post請求
分析百度詞典
分析步驟:
1. 打開F12
2. 嘗試輸入單詞girl,發現每敲一個字母後都有請求
3. 請求位址是 http://fanyi.baidu.com/sug
4. 利用NetWork-All-Hearders,檢視,發現FormData的值是 kw:girl
5. 檢查傳回内容格式,發現傳回的是json格式内容==>需要用到json包
'''
from urllib import request, parse
# 負責處理json格式的子產品
import json
'''
大緻流程是:
1. 利用data構造内容,然後urlopen打開
2. 傳回一個json格式的結果
3. 結果就應該是girl的釋義
'''
baseurl = 'http://fanyi.baidu.com/sug'
# 存放用來模拟form的資料一定是dict格式
data = {
# girl是翻譯輸入的英文内容,應該是由使用者輸入,此處使用寫死
'kw': 'girl'
}
# 需要使用parse子產品對data進行編碼
data = parse.urlencode(data).encode("utf-8")
rsp = request.urlopen(baseurl, data=data)
json_data = rsp.read().decode('utf-8')
# 把json字元串轉化成字典
json_data = json.loads(json_data)
print(type(json_data))
print(json_data)
for item in json_data['data']:
print(item['k'], "--", item['v'])
# 為了更多的設定請求資訊,單純的通過urlopen函數已經不太好用了
# 需要利用request.Request 類
# 構造一個Request的執行個體
# 我們需要構造一個請求頭,請求頭部應該至少包含傳入的資料的長度
# request要求傳入的請求頭是一個dict格式
headers = {
# 因為使用post,至少應該包含content-length 字段
'Content-Length':len(data)
}
# 有了headers,data,url,就可以嘗試送出請求了
req = request.Request(url=baseurl, data=data, headers=headers)
# 因為已經構造了一個Request的請求執行個體,則所有的請求資訊都可以封裝在Request執行個體中
rsp = request.urlopen(req)
# urllib.error
# URLError産生的原因:
# 沒網
# 伺服器連結失敗
# 知不道制定伺服器
# 是OSError的子類
# HTTPError, 是URLError的一個子類
# 案例v8
from urllib import request, error
try:
req = request.Request(url)
rsp = request.urlopen(req)
html = rsp.read().decode()
print(html)
except error.HTTPError as e:
print("HTTPError: {0}".format(e.reason))
print("HTTPError: {0}".format(e))
except error.URLError as e:
print("URLError: {0}".format(e.reason))
print("URLError: {0}".format(e))
except Exception as e:
print(e)
# 兩者差別:
# HTTPError是對應的HTTP請求的傳回碼錯誤, 如果傳回錯誤碼是400以上的,則引發HTTPError
# URLError對應的一般是網絡出現問題,包括url問題
# 關系差別: OSError-URLError-HTTPError
# UserAgent
# UserAgent: 使用者代理,簡稱UA, 屬于heads的一部分,伺服器通過UA來判斷通路者身份
# 常見的UA值,使用的時候可以直接複制粘貼,也可以用浏覽器通路的時候抓包
1.Android
Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19
Mozilla/5.0 (Linux; U; Android 4.0.4; en#gb; GT#I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
Mozilla/5.0 (Linux; U; Android 2.2; en#gb; GT#P1000 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
2.Firefox
Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0
Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0
3.Google Chrome
Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36
Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19
4.iOS
Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3
Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3
# 設定UA可以通過兩種方式:
# heads
# add_header
# 使用head方法僞裝UA
# headers = {}
# headers['User-Agent'] = 'Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3'
# req = request.Request( url, headers=headers)
# 使用add_header方法
req = request.Request(url)
req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36")
# 正常通路
rsp = request.urlopen( req )
html = rsp.read().decode()
print(html)
# ProxyHandler處理(代理伺服器)
# 使用代理IP,是爬蟲的常用手段
# 擷取代理伺服器的位址:
# www.xicidaili.com
# www.goubanjia.com
# 代理用來隐藏真實通路中,代理也不允許頻繁通路某一個固定網站,是以,代理一定要很多很多
# 基本使用步驟:
from urllib import request, error
url = "http://www.baidu.com"
# 使用代理步驟
# 1. 設定代理位址
proxy = {'http': '120.194.18.90:81' }
# 2. 建立ProxyHandler
proxy_handler = request.ProxyHandler(proxy)
# 3. 建立Opener
opener = request.build_opener(proxy_handler)
# 4. 安裝Opener
request.install_opener(opener)
# 現在如果通路url,則使用代理伺服器
try:
rsp = request.urlopen(url)
html = rsp.read().decode()
print(html)
except error.URLError as e:
print(e)
except Exception as e:
print(e)
# cookie & session
# 由于http協定的無記憶性,人們為了彌補這個缺憾,所采用的一個補充協定
# cookie是發放給使用者(即http浏覽器)的一段資訊,session是儲存在伺服器上的對應的另一半資訊,用來記錄使用者資訊
# cookie和session的差別
# 存放位置不同
# cookie不安全
# session會儲存在伺服器上一定時間,會過期
# 單個cookie儲存資料不超過4k, 很多浏覽器限制一個站點最多儲存20個
# session的存放位置
# 存在伺服器端
# 一般情況,session是放在記憶體中或者資料庫中
# 沒有cookie登入,回報網頁為未登入狀态
# 使用cookie登入
# 直接把cookie複制下來,然後手動放入請求頭:
headers = {
"Cookie": "xxxx"
}
req = request.Request(url, headers=headers)
# http子產品包含一些關于cookie的子產品,通過他們我們可以自動使用cookie
# CookieJar
# 管理存儲cookie,向傳出的http請求添加cookie,
# cookie存儲在記憶體中,CookieJar執行個體回收後cookie将消失
# FileCookieJar(filename, delayload=None, policy=None):
# 使用檔案管理cookie
# filename是儲存cookie的檔案
# MozillaCookieJar(filename, delayload=None, policy=None):
# 建立與mozilla浏覽器cookie.txt相容的FileCookieJar執行個體
# LwpCookieJar(filename, delayload=None, policy=None):
# 建立與libwww-perl标準相容的Set-Cookie3格式的FileCookieJar執行個體
# 他們的關系是: CookieJar-->FileCookieJar-->MozillaCookieJar & LwpCookieJar
# handler是Handler的執行個體
# 用來處理複雜請求
# 創立handler後,使用opener打開,打開後相應的業務由相應的hanlder處理
# cookie的儲存-FileCookieJar
cookie.save(ignore_discard=True, ignore_expires=True)
# cookie的讀取:
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
# 案例:利用cookiejar通路網站
# 自動使用cookie登入,大緻流程是
# 打開登入頁面後自動通過使用者名密碼登入
# 自動提取回報回來的cookie
# 利用提取的cookie登入隐私頁面
from urllib import request, parse
from http import cookiejar
# 建立cookiejar的執行個體
# cookie = cookiejar.CookieJar()
# 需要儲存成文本時建立MozillaCookieJar或FileCookieJar的執行個體
filename = "cookie.txt"
cookie = cookiejar.MozillaCookieJar(filename)
# cookie的讀取:
# cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
# 生成 cookie的管理器
cookie_handler = request.HTTPCookieProcessor(cookie)
# 建立http請求管理器
http_handler = request.HTTPHandler()
# 生成https管理器
https_handler = request.HTTPSHandler()
# 建立請求管理器
opener = request.build_opener(http_handler, https_handler, cookie_handler)
def login():
'''
負責初次登入
需要輸入使用者名密碼,用來擷取登入cookie憑證
:return:
'''
# 此url需要從登入form的action屬性中提取
url = "http://www.renren.com/PLogin.do"
# 此鍵值需要從登入form的兩個對應input中提取name屬性
data = {
"email": "13119144223",
"password": "123456"
}
# 把資料進行編碼
data = parse.urlencode(data)
# 建立一個請求對象
req = request.Request(url, data=data.encode())
# 使用opener發起請求
rsp = opener.open(req)
# 儲存cookie到檔案
# ignor_discard表示即使cookie将要被丢棄也要儲存下來
# ignore_expire表示如果該檔案中cookie即使已經過期,儲存
cookie.save(ignore_discard=True, ignore_expires=True)
def getHomePage():
url = "http://www.renren.com/965187997/profile"
# 如果已經執行了login函數,則opener自動已經包含相應的cookie值
rsp = opener.open(url)
html = rsp.read().decode()
if __name__ == '__main__':
login()
getHomePage()
# cookie作為一個變量,列印出來, 案例片段:
print(cookie)
for item in cookie:
print(type(item))
print(item)
for i in dir(item):
print(i)
# cookie的屬性
# name: 名稱
# value: 值
# domain:可以通路此cookie的域名
# path: 可以通路此cookie的頁面路徑
# expires:過期時間
# size: 大小
# Http字段
# SSL
# SSL證書就是指遵守SSL安全套階層協定的伺服器數字證書(SercureSocketLayer)
# 美國網景公司開發
# CA(CertifacateAuthority)是數字證書認證中心,是發放,管理,廢除數字證書的收信人的第三方機構
# 遇到不信任的SSL證書,需要單獨處理,案例:
# 導入pythopn ssl處理子產品
import ssl
# 利用非認證上下文環境替換認證的向下文環境
ssl._create_default_https_context = ssl._create_unverified_context
url = "https://www.12306.cn/mormhweb/"
rsp = request.urlopen(url)
html = rsp.read().decode()
# js加密
# 有的反爬蟲政策采用js對需要傳輸的資料進行加密處理(通常是取md5值)
# 經過加密,傳輸的就是密文,但是
# 加密函數或者過程一定是在浏覽器完成,也就是一定會把代碼(js代碼)暴露給使用者
# 通過閱讀加密算法,就可以模拟出加密過程,進而達到破解
# 過程參看案例v19
# 注意salt,sign的計算
# ajax
# 異步請求
# 一定會有url,請求方法,可能有資料
# 一般使用json格式
# 案例,爬去豆瓣電影, 案例v20
# 注意請求值
#================================Requests==========================
# HTTP for Humans,更簡潔更友好
# 繼承了urllib的所有特征
# 底層使用的是urllib3
# 開源位址: https://github.com/requests/requests
# 中文文檔: http://docs.python#requests.org/zh_CN/latest/index.html
# 安裝: conda install requests
# get請求
# requests.get(url)
# requests.request("get", url)
# 可以帶有headers和parmas參數
import requests
url = "http://www.baidu.com"
# 兩種請求方式
# 使用get請求
rsp = requests.get(url)
print(rsp.text)
# 使用request請求
rsp = requests.request("get", url, params=kw, headers=headers)
print(rsp.text)
# get傳回内容
print(rsp.text)
print(rsp.content)
print(rsp.url)
print(rsp.encoding)
print(rsp.status_code) # 請求傳回碼
# post
rsp = requests.post(baseurl, data=data, headers=headers)
# date, headers要求dict類型,不需要編碼成byte流
# proxy
#
proxies = {
"http":"address of proxy",
"https": "address of proxy"
}
rsp = requests.request("get", "http:xxxxxx", proxies=proxies)
# 代理有可能報錯,如果使用人數多,考慮安全問題,可能會被強行關閉
# 使用者驗證
# 代理驗證
#可能需要使用HTTP basic Auth, 可以這樣
# 格式為 使用者名:密碼@代理位址:端口位址
proxy = { "http": "china:[email protected]:4444"}
rsp = requests.get("http://baidu.com", proxies=proxy)
# web用戶端驗證
# 如果遇到web用戶端驗證,需要添加auth=(使用者名,密碼)
autu=("test1", "123456")#授權資訊
rsp = requests.get("http://www.baidu.com", auth=auth)
# cookie
# requests可以自動處理cookie資訊
rsp = requests.get("http://xxxxxxxxxxx")
# 如果對方伺服器給傳送過來cookie資訊,則可以通過回報的cookie屬性得到
# 傳回一個cookiejar執行個體
cookiejar = rsp.cookies
#可以講cookiejar轉換成字典
cookiedict = requests.utils.dict_from_cookiejar(cookiejar)
# session
# 跟伺服器端session不是一個東東
# 模拟一次會話,從用戶端浏覽器連結伺服器開始,到用戶端浏覽器斷開
# 能讓我們跨請求時保持某些參數,比如在同一個session執行個體發出的 所有請求之間保持cookie
# 建立session對象,可以保持cookie值
ss = requests.session()
headers = {"User-Agent":"xxxxxxxxxxxxxxxxxx"}
data = {"name":"xxxxxxxxxxx"}
# 此時,由建立的session管理請求,負責送出請求,
ss.post("http://www.baidu.com", data=data, headers=headers)
rsp = ss.get("xxxxxxxxxxxx")
# https請求驗證ssl證書
# 參數verify負責表示是否需要驗證ssL證書,預設是True
# 如果不需要驗證ssl證書,則設定成False表示關閉
# 如果用verify=True通路12306,會報錯,因為他證書有問題
rsp = requests.get("https://www.baidu.com", verify=False)