什么是爬蟲
- 爬取網(wǎng)頁數(shù)據(jù)的程序 它是一門工具
網(wǎng)頁特征
- 每個網(wǎng)頁都有不同的url(統(tǒng)一資源定位符)
- 網(wǎng)頁都由HTML語言構(gòu)成
- 都用HTTP/HTTPS協(xié)議傳輸
爬蟲怎么爬取網(wǎng)頁
- 定位你要的url地址
- 然后把網(wǎng)頁地址下載下來
- 提取有用的數(shù)據(jù),如果有 有用的URL,那繼續(xù)爬
為什么用python做爬蟲
python:代碼簡單易懂,并且第三方的庫也有很多,python自帶的urllib網(wǎng)絡(luò)呢請求模塊,requests網(wǎng)絡(luò)請求模塊,網(wǎng)絡(luò)解析庫xpath,BeautifulSoup4,pyquery等等,還有成熟
高效穩(wěn)定的爬蟲框架scrapy(pyspider)等等,并且還支持分布式爬蟲(scrapy-redis)框架
java:是python寫爬蟲的最大的競爭對手,java的發(fā)展周期長,生態(tài)圈都比較完善,也有很多第三方庫的支持,java的代碼量比較大,開發(fā)的成本比較高,后期維護也比較繁瑣
php:php曾經(jīng)被叫做世界上最好的語言(一般用來后端的),也可以用來寫爬蟲,但是對多任務(wù)的支持不太好,爬蟲對效率要求比較高,所有一般不適用php寫爬蟲
c/c++:比較偏向于底層的語言,代碼的運行效率高,學(xué)習的門楷非常高,代碼成型比較慢.
七層協(xié)議
應(yīng)用層 表示層 會話程 傳輸層 網(wǎng)絡(luò)層 數(shù)據(jù)鏈路層 物理層
OSI七層協(xié)議的目的:實現(xiàn)不同的系統(tǒng)互聯(lián)之間的數(shù)據(jù)通訊,實現(xiàn)數(shù)據(jù)的傳輸.
應(yīng)用層:http/https
傳輸層:TCP/UDP
TCP:網(wǎng)絡(luò)傳輸協(xié)議,面向連接的,長連接,傳輸?shù)氖菙?shù)據(jù)流
,確保數(shù)據(jù)的安全性和完整性,但是數(shù)據(jù)傳輸?shù)男实?br> UDP:網(wǎng)絡(luò)傳輸協(xié)議,是非面向連接的,短連接,傳輸?shù)氖菙?shù)據(jù)包,
傳輸數(shù)據(jù)是不安全的,可能會造成數(shù)據(jù)的丟失,傳輸速度非常快
http(超文本傳輸協(xié)議,端口號是80):
實現(xiàn)從網(wǎng)絡(luò)傳輸草文本數(shù)據(jù)到本地瀏覽器的傳送協(xié)議
https(端口號是443):是http的安全版本,在http的基礎(chǔ)上添加了一個
SSL(安全套接字層)層,用于web端的安全傳送,在傳輸層
對網(wǎng)絡(luò)連接進行加密,
1.構(gòu)建了一個安全的數(shù)據(jù)傳輸通道.
2.保證網(wǎng)站的真實性和有效性
https協(xié)議需要有一個證書(CA證書):由專門的證書機構(gòu)頒發(fā)的,
也可以自己生成,但是訪問的時候會提示連接不安全
https協(xié)議需要有一個證書(CA證書):由專門的證書機構(gòu)頒發(fā)的,
也可以自己生成,但是訪問的時候會提示連接不安全
http的工作原理:
URL介紹:
URI:統(tǒng)一資源標志符
URN:統(tǒng)一資源名稱
URL:統(tǒng)一資源定位符
URI是URN和URL的父類
URL的組成部分:
https://baike.baidu.com/item/OSI/5520?fr=aladdin
https://book.qidian.com/info/1004608738
https://book.qidian.com/info/1004608738#Catalog
get:只是用于從服務(wù)器獲取數(shù)據(jù),再url連接后面
可能會跟一些查詢參數(shù)
post:向服務(wù)器端提交數(shù)據(jù),數(shù)據(jù)會放在請求體中,
一般用于添加或者修改數(shù)據(jù)
delete:用來刪除數(shù)據(jù)
put:更新整個資源(用來做數(shù)據(jù)的更新)
patch:(更新資源)(局部數(shù)據(jù)的更新)
對比:get和post請求的區(qū)別
1.使用場景:get從服務(wù)器端獲取數(shù)據(jù),post請求向服務(wù)器端提交數(shù)據(jù)
2.安全性:get請求參數(shù)只拼接在url地址上,post請求會將參數(shù)放在
請求體中,(注意:不要誤認為只要url地址后面添加了參數(shù)就是一個get請求)
3.get請求的url是有長度限制的,post的請求體中可以添加很多字段
Cookie和Session:目的保持會話
http請求是無狀態(tài)的,每一次請求斷開后,下一次請求就
認為是一個新的請求,為了維持請求狀態(tài)就用到了Cookie
和Session
Cookie:保存在客戶端的,記錄信息確定用戶的身份
Session:保存在服務(wù)端的,同樣是記錄信息確定用戶身份
常見的請求狀態(tài)碼:
200:請求成功
3xx:重定向
301:永久重定向
302:臨時重定向
4xx:客戶端請求錯誤
400:請求錯誤,服務(wù)器無法解析
401:未授權(quán),沒有進行身份驗證
403:服務(wù)器拒絕訪問
404:訪問的頁面不存在
405:請求方式不允許
408:請求超時
5xx:服務(wù)端錯誤
500:服務(wù)端內(nèi)部錯誤
501:服務(wù)器暫時不具備完成請求的功能
503:服務(wù)器不可用
urllib
request
#1.發(fā)起請求:python自帶的urllib模塊
#request:是urllib最基本的http網(wǎng)絡(luò)請求模塊
from urllib import request
#https://www.qidian.com/all
#https://www.qidian.com/all
# ?orderId=&style=1&pageSize=20
# &siteid=1&pubflag=0&hiddenField=0&page=1
#https://www.qidian.com/all
#?orderId=&style=1&pageSize=20
# &siteid=1&pubflag=0&hiddenField=0&page=2
#https://www.qidian.com/all
# ?orderId=&style=1&pageSize=20
# &siteid=1&pubflag=0&hiddenField=0&page=3
#發(fā)起請求
"""
url:設(shè)置目標url
data=None:默認為None,表示發(fā)起的是一個get請求,
反之,不為None,表示發(fā)起的是一個post請求
timeout: 設(shè)置請求的超時時間(s)
cafile=None, 設(shè)置證書文件(一般不用)
capath=None, 設(shè)置證書文件路徑(一般不用)
context=None, 一般設(shè)置為一個ssl的對象
(ssl._create_unverified_context()),
忽略未認證的CA證書(如果出現(xiàn)了ssl證書錯誤)
"""
url = 'https://www.qidian.com/all?orderId=&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page=1'
#如果請求需要添加請求頭,urlopen并沒有headers參數(shù)來設(shè)置請求頭
"""
url:設(shè)置目標url
data=None,:默認為None,表示發(fā)起的是一個get請求,
反之,不為None,表示發(fā)起的是一個post請求
headers={}:設(shè)置請求頭,傳遞一個字段類型的參數(shù)
"""
req_header = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
#根據(jù)url構(gòu)建一個請求對象
req = request.Request(url=url,headers=req_header)
#使用urlopen方法faqi請求,獲得響應(yīng)結(jié)果
response = request.urlopen(req,timeout=20)
# response = request.urlopen(url=url,timeout=20)
#從響應(yīng)結(jié)果中獲取相關(guān)數(shù)據(jù)
# print(response.read())
html = response.read().decode('utf-8')
print(len(html))
"""
文件讀寫模式:
r:打開一個文件,只有可讀權(quán)限 rb: r+: rb+
w:打開一個文件,有寫入的權(quán)限 wb: w+: wb+
a:打開一個文件,有追加權(quán)限 ab: a+: ab+:
"""
with open('quanshu.html','w') as file:
file.write(html)
#獲取響應(yīng)的狀態(tài)碼
code = response.status
print(code)
#獲取響應(yīng)的響應(yīng)頭
response_headers = response.getheaders()
print(response_headers)
#獲取某一個響應(yīng)頭參數(shù)
server = response.getheader('Server')
print(server)
#獲取當前請求的url地址
current_url = response.url
print(current_url)
#獲取請求的reason(如果成功返回的是OK)
reason = response.reason
print(reason)
#注意:response.read(),只可以讀取一次
#https://www.baidu.com/s?wd=新年愿望
error
# error模塊:在我們請求過程中,可能因為服務(wù)器錯誤,弱網(wǎng)環(huán)境...
# 造成請求失敗,這時我們需要對這些錯誤進行異常處理,不然會造成代碼崩潰
from urllib import request,error
'''
error.URLError:
產(chǎn)生的原因主要有:
沒有網(wǎng)絡(luò)連接
服務(wù)器連接失敗
找不到指定的服務(wù)器
有一個reason屬性:返回錯誤的原因
'''
'''
error.HTTPError
HTTP請求錯誤,比如未認證,頁面不存在
code:請求的狀態(tài)碼
reason:返回錯誤的原因
headers:返回響應(yīng)頭部
'''
import ssl
context = ssl._create_unverified_context()
# url = 'https://www.baidu.com/12345.htm'
url = 'https://www.qidian.com/all/nsacnscn.htm'
try:
response = request.urlopen(url,timeout=0.01,context=context)
print(response.status)
except error.HTTPError as err:
print('HTTPError')
print(err.code)
print(err.reason)
print(err.headers)
except error.URLError as err:
print('URLError',err.reason)
parse
#parse:可以對url進行拆分,組合,編碼,解碼,拼接
from urllib import parse
#parse.urlencode():將字典類型的參數(shù)轉(zhuǎn)為url編碼格式
form_data ={
'first': 'false',
'pn': 2,
'kd': '后端',
}
#get請求直接使用urlencode將參數(shù)轉(zhuǎn)為url編碼格式
form_data1 = parse.urlencode(form_data)
print(form_data1)
#post請求urlencode將參數(shù)轉(zhuǎn)為url編碼格式,然后使
# 用encode方法將字符串轉(zhuǎn)為bytes類型
form_data2 = parse.urlencode(form_data).encode('utf-8')
print(form_data2)
# parse.parse_qs():將url編碼格式的字符串轉(zhuǎn)化為字典類型
#注意key對應(yīng)的value是一個list
parmas = parse.parse_qs(form_data1)
print(parmas)
#parse.quote()將中文字符轉(zhuǎn)為url編碼的字符
key = '我的國'
result = parse.quote(key)
print(result)
#parse.unquote()將url編碼的字符轉(zhuǎn)換為中文字符
unquote_result = parse.unquote(result)
print(unquote_result)
#將不完整的url參照基類url,拼接完整
base_url = 'http://www.qidian.com/book/123456.html'
sub_url = '12345789.html'
full_url = parse.urljoin(base_url,sub_url)
print(full_url)
#parse.urlparse():將url進行拆分
base_url = 'http://www.qidian.com/book/123456.html'
result = parse.urlparse(base_url)
"""
ParseResult(
scheme='http',:協(xié)議
netloc='www.qidian.com', ip或域
path='/book/123456.html', 路徑
params='', 參數(shù)
query='', 查詢參數(shù)(?后面拼接的參數(shù))
fragment='' : 錨點
)
"""
print(result)
print(result.scheme)
#parse.urlunparse():將url的各個部分合并為一個完整的url
#scheme, netloc, url, params, query, fragment
url_datas = ('https','www.baidu.com','book','','wd=xxx','1234')
full_url = parse.urlunparse(url_datas)
print(full_url)
正則表達式
#正則的規(guī)則:
#單字符匹配
"""
. 除換行符之外的任意字符
\d 表示數(shù)字
\D 匹配非數(shù)字
\w 匹配單詞字符[a-z,A-Z,0-9]
\W 匹配非單詞字符
\s 匹配空白字符,空格,\n \t ...
\S 匹配非空白字符
^ 匹配以...開頭
$ 匹配以...結(jié)尾
[0-9] => \d 匹配0-9
"""
#多字符匹配(貪婪匹配)
"""
* 匹配*前面的字符任意次數(shù)
+ 匹配+前面的字符至少1次
? 匹配?前面的字符0~1次
{n,m} 匹配{n,m}前面的字符n~m次
"""
#多字符匹配(非貪婪匹配)
"""
*?
+?
??
"""
#其他
"""
() 分組
| 邏輯或
\ 轉(zhuǎn)義字符
"""
#re模塊下的方法
import re
#re.compile():構(gòu)建正則表達式對象
#re.match():從起始位置開始匹配,單次匹配,匹配到
#結(jié)果立即返回,反之,返回None
str = 'abcdebfga'
pattern = re.compile('b|e')
result = re.match(pattern,str)
if result:
print(result.group())
# re.search():在整個字符串中進行匹配,單次匹配,匹配到結(jié)果
# 立即返回,反之,返回None
result = re.search(pattern,str)
print(result.group())
# re.findall():匹配出整個字符串中,所有符合正則規(guī)則的結(jié)果
# 返回的是一個列表(list)
result = re.findall(pattern,str)
print(result)
# re.finditer():匹配出整個字符串中所有符合正則規(guī)則的結(jié)果,
# 返回的是一個可迭代對象
result = re.finditer(pattern,str)
print(type(result),result)
for i in result:
print(i,type(i))
print(i.group())
# re.sub():根據(jù)正則表達式進行字符串替換
new_str = re.sub(pattern,'h',str)
print(new_str)
#re.split():根據(jù)正則表達式進行分割,得到的是一個list
result = re.split(pattern,str)
print(result)