urllib庫
- python2中,由urllib和urllib2兩個庫來發送請求,python3中沒有urllib2,統一為urllib
- urllib的幾個模塊
- request:最基本的http請求模塊,
- error: 異常處理的模塊
- parse:工具模塊,提供了許多url處理方法,比如拆分、解析、合并等
- robotparser:識別網絡的robot.txt文件
- request模塊下的幾個方法類
- urlopen(url,data,timeoit)
- url:必傳,請求資源的地址
- data:可選,必須是字節流格式,使用此方法后不再是get請求,而是post請求
- timeout:設定超時時間
from urllib import request, parse, error # urlopen(url, data) resp = request.urlopen("http://shuyantech.com/api/cndbpedia/avpair?q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6") # 一個http響應對象 res = resp.read() # 返回值為bytes編碼對象 res = res.decode("utf-8") # 對其解碼 print(res) # data: 像url中動態傳參數,使用此方法后不再是get請求,而是post請求 params = {"q":"清華大學"} # 定義參數 # urlencode(dict)將參數字典轉化為字符串 data = parse.urlencode(params) # q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6 # bytes(str,encode)將字符串轉化為字節流類型,并制定編碼格式 data = bytes(data, encoding="utf8") # b'q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6' resp = request.urlopen("http://shuyantech.com/api/cndbpedia/avpair?", data) res = resp.read() # b'{"status": "ok", "ret": [["\xe4\xb8\xad\xe6\x96\x87\xe5\x90\x8d", "\xe6\xb8\x85\xe5\x8d\x8e\xe5\xa4\xa7\xe5\xad\xa6"], res = res.decode("utf-8") # {"status": "ok", "ret": [["中文名", "清華大學"], print(res) # timeout,設置超時時間,單位為秒 try: resp = request.urlopen("http://shuyantech.com/api/cndbpedia/avpair?q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6", timeout=0.01) print(resp) # urllib.error.URLError: <urlopen error timed out> except error.URLError as e: print(e.reason) # timed out # isinstance,判斷對象是否為已知類型,與type的區別,isinstance() 會認為子類是一種父類類型,考慮繼承關系,type則不考慮繼承關系 import socket if isinstance(e.reason, socket.timeout): print("超時了")
- Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False,method=None)
- url:必傳,請求的資源地址
- data:必須是bytes(字節流)類型的
- headers:字典形式,構造請求頭信息,例如User-Agent
- method:字符串,指定請求方式
- origin_req_host:請求方的host和ip
- unverifiable:驗證用戶有沒有權限接受請求結果
from urllib import request, parse url = "http://httpbin.org/post" data = { "name":"Request" } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36", "Host": "httpbin.org" } method = "POST" # 構造請求信息 req = request.Request(method=method, url=url, headers=headers, data=bytes(parse.urlencode(data), encoding="utf8") ) resp = request.urlopen(req) res = resp.read().decode("utf-8") print(res)
- Handler各種處理器和opener
- HTTPBasicAuthHandler:用于管理認證,當一個連接打開時,需要登錄,可以用它來解決認證問題
- HTTPCookieProcessor:用于處理cookie
- HTTPPasswordMgr:用于管理密碼,維護了用戶名和密碼的表
- HTTPRedirectHandler:用于重定向
- ProxyHandler:用于設置代理,默認代理為空
from urllib.request import ProxyHandler, build_opener from urllib.error import URLError url = "https://www.baidu.com" # 實例化代理 p_handler = ProxyHandler({ "http":"http://39.134.66.14:8080", "https":"https://218.38.52.132:80" }) opener = build_opener(p_handler) # build_opener(實例化的handler處理器) try: res = opener.open(url) html = res.read().decode("utf-8") print(html) except URLError as e: print(e.reason)
- urlopen(url,data,timeoit)
- error:異常處理,定義了由request模塊產生的錯誤
- URLError:error模塊的基類,由request模塊產生的異常都可以通過這個類捕獲,存在reason屬性
- HTTPError: URLError的子類,專門處理HTTP請求錯誤 屬性code:返回的HTTP狀態碼,reason:錯誤原因 headers:返回請求頭
# 一個完整的異常捕獲例子 from urllib import error,request try: resp = request.urlopen("http://cuiqingcai.com/index.html") except error.HTTPError as e: print("返回的狀態碼%s" % e.code) print("返回的錯誤原因%s" % e.reason) print("返回的請求頭%s" % e.headers) except error.URLError as e: print("異常原因%s" % e.reason) else: print("成功")
-parse:解析鏈接,實現url各部分的抽取、合并、以及鏈接的轉換
- urlparse:實現url的分段和識別,分成六段 scheme://netloc/path;params?query#fragment
- scheme:表示協議
- netloc:表示域名
- path:表示訪問路徑
- params:代表參數,;后
- query:代表查新條件,?后
- fragment:表示錨點,#后
- urlparse(url, scheme,allow_fragments)
- url:必填,待解析的url
- scheme:默認的協議,假如傳入的連接沒帶協議,會采取這個默認的協議
- allow_fragments:是否忽略錨點部分,值為布爾類型
- urlunparse:根據參數,生成鏈接,參數為可迭代對象,且長度為6
- urlsplit: 與urlparse一樣,參數也形同解析url,分成五段,將params與path部分合并
- urlsplit(url, scheme,allow_fragments)
- urlunsplit: 參數為可迭代對象,且長度為5
- urljoin:生成鏈接,效果同urlunparse和urlunsplit,但此方法不需要長度
- urljoin(base_url,str) 將兩個參數合并對base_url的協議、域名、路徑進行補充,base_url其他三個部分則不起作用
- urlencode:將字典對象序列化成符合url參數格式的字符串,一般往url傳參中用
- parse_qs:將url中的參數轉成字典
- parse_qsl:將url中的參數轉化為元組組成的列表
- quote:將數據轉化為url格式的編碼,一般參數為中文的時候使用
- unquote:將url格式的編碼進行解碼
```
from urllib import parse
res = parse.urlparse("http:www.baidu.com/index.html;user?id=5#comment")
# 返回結果為元組,res[0],res.scheme
print(res, res[0], res.scheme) # ParseResult(scheme='http', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')
data = ["http", "www.baidu.com", "index.html", "user", "d=5", "comment"]
res = parse.urlunparse(data)
print(res) # http://www.baidu.com/index.html;user?d=5#comment
res = parse.urlsplit("http:www.baidu.com/index.html;user?id=5#comment")
print(res) # SplitResult(scheme='http', netloc='', path='www.baidu.com/index.html;user', query='id=5', fragment='comment')
data = ["http", "www.baidu.com", "index.html", "d=5", "comment"]
res = parse.urlunsplit(data)
print(res) # http://www.baidu.com/index.html?d=5#comment
res = parse.urljoin("http://www.baidu.com", "index.html")
print(res) # http://www.baidu.com/index.html ,注意合并的規則
data= {
"name":"bob",
"age":18
}
res = parse.urlencode(data, encoding="utf8")
print(res) # name=bob&age=18
res = parse.parse_qs("http:www.baidu.com/index.html;user?id=5&name=3")
print(res) # {'user?id': ['5'], 'name': ['3#comment']}
res = parse.parse_qsl("http:www.baidu.com/index.html;user?id=5&name=3")
print(res) # [('user?id', '5'), ('name', '3')]
q= "張三"
res = "http:www.baidu.com/index.html?q=%s " % parse.quote(q) # 編碼
print(res) # http:www.baidu.com/index.html?q=%E5%BC%A0%E4%B8%89
res = parse.unquote(res) # 解碼
print(res) # http:www.baidu.com/index.html?q=張三
```
- robotparse:分析網站的robot協議
- robots.txt:網站爬蟲協議,全稱網絡爬蟲排除標準,一般存放于網站的根目錄下
- 返回的內容:
- user-agent:設置可以爬取網站的爬蟲名稱
- disallow:設置哪些路徑不能爬取
- allow:設置哪些路徑可以爬取
# 京東的爬蟲協議 # url: https://www.jd.com/robots.txt User-agent: * # 任何爬蟲都可以爬 Disallow: /?* Disallow: /pop/*.html Disallow: /pinpai/*.html?* User-agent: EtaoSpider Disallow: / User-agent: HuihuiSpider Disallow: / User-agent: GwdangSpider Disallow: / User-agent: WochachaSpider Disallow: /
- RobotFileParser(url="") # 根據網站的robot協議來判斷爬蟲是否有權限爬取
- set_url():設置robots.txt文件的連接
- read():讀取robots.txt文件并分析,無返回值,必須調用
- parse():解析robots.txt文件
- can_fetch(User-agent, url):返回值為布爾類型,判斷此user-agent是否可以爬取此url
- mtime():返回值為上次抓取分析robots的時間
- modified():將當前時間設置為上次抓取的時間
from urllib import robotparser rp = robotparser.RobotFileParser() # 實例化分析類 rp.set_url("https://www.jd.com/robots.txt") # 添加robots地址 rp.read() # 讀取robot協議 print(rp.can_fetch("*", "https://www.jd.com/")) # 判斷當前爬蟲是否可以爬取, True print(rp.mtime()) # 上次抓取的時間 1553685029.0578604 print(rp.modified()) # 設置抓取的時間 none
requests的使用
- 基本用法
- Get請求 requests.get("url", "params", "headers")
- url:請求資源的地址
- params:傳入的參數
- headers:請求頭,反扒的一種措施
- Post請求 requests.post("url", "params", "headers")
- url:請求資源的地址
- params:傳入的參數
- headers:請求頭,反扒的一種措施
- 響應屬性信息
- text:返回的文本數據
- content:二進制格式的數據,例如音頻、視頻、圖片的
- status_code:返回的狀態碼
- headers:返回的響應頭
- cookies:cookies信息
- url:請求的url信息
- history: 請求歷史
import requests # get請求 requests.get(url, params, headers) url = "http://httpbin.org/get" # 請求的url # 請求參數 params = { "name":"Arale", "age":25 } # 請求頭 headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36" } # 發送get請求,得到響應 resp = requests.get(url, params=params, headers=headers) print(resp.url) # 打印請求的url 參數在url http://httpbin.org/get?name=Arale&age=25 # post請求 requests.post(url, data, headers) url = "http://httpbin.org/post" # 請求的url # 發送post請求 resp = requests.post(url, data=params, headers=headers) print(resp.url) # 打印請求的url 參數在請求體中 http://httpbin.org/post # 響應屬性方法 print(resp.text) # 打印str文本數據 print(resp.content) # 處理二進制數據,例如圖片 print(resp.url) # http://httpbin.org/post print(resp.headers) # {'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Encoding': 'gzip', 'Content-Type': 'application/json', 'Date': 'Thu, 28 Mar 2019 01:53:32 GMT', 'Server': 'nginx', 'Content-Length': '343', 'Connection': 'keep-alive'} print(resp.cookies) # <RequestsCookieJar[]> print(resp.status_code) # 200 print(resp.history) # []
- Get請求 requests.get("url", "params", "headers")
- 高級用法
- 上傳文件
# 上傳文件 files = { "file": open("01_urllib的使用.py", "rb") } resp = requests.post(url, files=files) # 注意key必須為files否則報錯 print(resp.text) # 返回內容會多一個files字段
- cookies的使用
- 以簡書為例,寫文章的頁面需要登陸后才能看見,拿到網站登錄后的cookie,再去請求,以達到維持登錄狀態的效果。
# cookie的使用 headers = { # 此cookie為登錄網站后的cookie, "Cookie": "Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1553742403; sajssdk_2015_cross_new_user=1; locale=zh-CN; read_mode=day; default_font=font2; remember_user_token=W1sxMjE1NTM2Ml0sIiQyYSQxMSRmZEdzaHlpLnFsYnZpMG9PbFRQLk91IiwiMTU1Mzc0MjQxMC44MTg2OTc3Il0%3D--48708ad37562cd9a12cfaac066b92cc24e4305d3; _m7e_session_core=167a540dc0e51fd3bb10e0e502e174de; __yadk_uid=8uaAcl2jljk5KfYwGemwVKFoMN89sBuC; Hm_lpvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1553742450; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22169c243959d13-00d4259c8ce7dd-7a1b34-1296000-169c243959e606%22%2C%22%24device_id%22%3A%22169c243959d13-00d4259c8ce7dd-7a1b34-1296000-169c243959e606%22%2C%22props%22%3A%7B%7D%2C%22first_id%22%3A%22%22%7D", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36", "Referer": "http://www.lxweimin.com/writer" } url = "http://www.lxweimin.com/writer#/notebooks/35136025/notes/43035331" resp = requests.get(url, headers=headers) print(resp.status_code) # 設置cookie,可以訪問需要登錄才能看見的頁面200 # 查看cookie,并可以得到cookie的key value cookie = requests.get("https://www.baidu.com/").cookies print(cookie) for k, v in cookie.items(): print(k + "=" + v)
- session會話維持
- Session():用來維持同一個會話,使用其實例化的對象進行請求
# session:維持會話 session = requests.Session() # 實例化session類,使請求為同一示例,即維持同一會話 session.get("https://www.httpbin.org/cookies/set/arale/123456") resp = session.get("https://www.httpbin.org/cookies") print(resp.text)
- ssl證書認證
- requests中有個參數verify,默認weiTrue,會自動驗證網站的證書
# ssl證書的驗證,verify參數默認為True try: resp = requests.get("https://inv-veri.chinatax.gov.cn/") print(resp.status_code) # requests.exceptions.SSLError會報錯,默認會驗證CA證書 except Exception as e: # from requests.packages import urllib3 # urllib3.disable_warnings() # 忽略警告 import logging logging.captureWarnings(True) # 捕獲警告到日志的方式忽略警告 resp = requests.get("https://inv-veri.chinatax.gov.cn/", verify=False) # 不去驗證證書 print(resp.status_code)
- 代理的設置
# 代理ip的設置 proxies = { "http":"http://47.107.227.104:8888", } resp = requests.get("https://www.baidu.com/", proxies=proxies) print(resp.status_code)
- 超時設置
# 設置超時 try: resp = requests.get("https://www.baidu.com/", timeout=0.001) print(resp.status_code) # requests.exceptions.ConnectTimeout except Exception as e: print(e)
- 身份認證
# 傳入auth=(元組) resp = requests.get("url", auth=("user", "pwd")
- prepared requests對象
- 引入Request對象
s = requests.Session() # 實例化session對象 req = requests.Request("get", "https://www.baidu.com/") # 構造請求對象 pre = s.prepare_request(req) # 通過session準備請求數據 resp = s.send(pre) # 發送請求數據,返回響應 print(resp.status_code)
正則表達式
- 常用方法
- match(re, str):從開頭開始匹配,如果開頭不符合,則匹配失敗
- search(re,str):掃描整個字符串,返回第一個符合條件的結果,沒有匹配到,則返回none
- findall(re,str):查找整個字符串,返回所有匹配到的結果
- sub(re,要替換的內容,str):替換匹配到的數據
- compile(re):將匹配規則編譯成正則表達式對象,以達到復用的效果
- 返回結果為sre_match對象,有兩個方法group()\span()
- group():得到結果的內容
- span():得到匹配到的范圍,即結果字符串在原字符竄中的位置范圍。
- 分組:即字符串截取,用()包裹好想要提取的子字符串,調用group()方法,并傳入分組索引即可得到,索引從1開始
- 通用匹配:., . 表示除換行符之外的任意字符,表示匹配前面字符0或多個
- 貪婪與非貪婪:.* 表示貪婪模式,會盡可能的匹配多個導致匹配結果不準確,.?表示非貪婪模式,盡可能匹配更少的,注意:非貪婪模式放在字符串結尾,可能會匹配不到*
- 修飾符:第三個參數,做修飾用
- re.S:表示匹配包括換行符在內的所有字符
- re.I:表示對大小寫不敏感
- 轉移符: \,用來轉義特殊字符
import re
str = "Extra stings Hello 123456 world_This is a Regex Demo Exrea stings"
# + 表示一個或多了
# .*?表示非貪婪模式
# \w{10}匹配字母數字下劃線10個,{4,10}貪婪模式,會取10個
# ()分組字符串提取
result = re.match(r"^extra.*?(\d+)\s(\w{10})", str, re.I)
print(result.group()) # 匹配結果的內容
print(result.span()) # 結果字符串在原字符串中的位置范圍
print(result.group(1)) # 取出第一個分組結果
print(result.group(2))
爬取貓眼電影
- 使用requests第三方庫,請求網站,用正則解析頁面,將解析結果寫入excel
import requests
import re
import time
import xlwt
# 爬取網頁
def get_page(url, headers):
resp = requests.get(url, headers)
return resp.text
# 解析返回的結果
def parse_res(res):
'''<dd>
<i class="board-index board-index-1">1</i>
<a href="/films/1203" title="霸王別姬" class="image-link" data-act="boarditem-click" data-val="{movieId:1203}">
<img src="http://s0.meituan.net/bs/?f=myfe/mywww:/image/loading_2.e3d934bf.png" alt="" class="poster-default">
<img data-src="https://p0.meituan.net/movie/223c3e186db3ab4ea3bb14508c709400427933.jpg@160w_220h_1e_1c" alt="亂世佳人" class="board-img" />
</a>
<div class="board-item-main">
<div class="board-item-content">
<div class="movie-item-info">
<p class="name"><a href="/films/1203" title="霸王別姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王別姬</a></p>
<p class="star">
主演:張國榮,張豐毅,鞏俐
</p>
<p class="releasetime">上映時間:1993-01-01</p>
</div>
<div class="movie-item-number score-num">
<p class="score">
<i class="integer">9.</i>
<i class="fraction">5</i>
</p>
</div>
</div>
</div>
</dd>
'''
par = re.compile(r'<dd>.*?'
r'<i.*?>(\d+)</i>'
r'.*?data-src="(.*?)"'
r'.*?class="name"><a .*?>(.*?)</a>'
r'.*?class="star">(.*?)</p>'
r'.*?releasetime">(.*?)</p>'
r'.*?class="integer">(.*?)</i>'
r'.*?class="fraction">(.*?)</i>'
r'.*?</dd>', re.S)
items = re.findall(par, res) # 返回list
for item in items:
# 利用生成器返回數據,或者構造元組,存放在list中用return返回
# TODO 返回這樣格式是寫入excel需要
yield [
item[0],
item[1],
item[2],
item[3].strip()[3:],
item[4][5:],
item[5]+item[6]
]
# 寫入文件
def write_to_excel(items):
# 創建一個excel
excel = xlwt.Workbook()
# 添加一個工作區
sheet = excel.add_sheet("電影排名")
# 構造表頭信息
head = ["序號", "海報", "名稱", "主演", "上映時間", "評分"]
# 將頭部信息寫入excel表頭
for index, value in enumerate(head):
sheet.write(0, index, value)
# 將內容寫入excel
for row, item in enumerate(items, 1): # 行數據
for col in range(0, len(item)): # 列數據
sheet.write(row, col, item[col])
excel.save("./貓眼電影排名.xlsx")
# 主程序入口
def main(offset):
url = "https://maoyan.com/board/4?offset=" + str(offset)
headers = {
"User-Agent": '''Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36(KHTML, like Gecko) Chrome
/73.0.3683.75 Safari/537.36'''
}
res = get_page(url, headers) # 請求url
items = parse_res(res) # 解析結果,返回生成器
print(items.__next__()) # python3改為__next__(),之前版本為next()
return items # 返回解析后的結果,生成器
if __name__ == "__main__":
items = []
offset = None
for i in range(0, 10):
item= main(i*10) # 分頁爬取
items += list(item) # 將每頁結果進行拼接
time.sleep(1) # 每頁休眠一秒鐘,反扒措施
write_to_excel(items) # 將所有結果一次性寫入文件,不一次一次寫,因為會覆蓋