一、cookie和session
(一)產生的緣由
由於http是一個無狀態的協議,每次請求如果需要之前請求的一些信息,就必須重新發送之前的請求。
爲了解決這種問題,產生了一種記錄狀態的技術–就是cookie和session。
(二)作用
- cookie是在客戶端記錄狀態,session是在服務端記錄狀態。
- 在做爬蟲的時候,如果要實現登錄,只需要將瀏覽器中登錄後的cookie信息封裝到請求頭中就可以了。
(三)詳解
1.session
對於session(會話),其本來的含義是指有始有終的一系列動作或消息。而在web中,會話對象用來存儲特定用戶的會話所需的屬性及配置信息。
當一個客戶端發送一個cookie,服務器會從這個cookie中找到sessionid,再查找出相應session信息返回給客戶端,來進行用戶頁面的流轉。
問題1
可能會出現,通過sessionid來查找session,發現沒有的情況。因爲第一次登錄時,會創建一個session,在session的有效期內,繼續訪問頁面,服務器會直接查找到這個session返回給客戶端。失效後就找不到了。
那session什麼時候失效呢?
當session的壽命達到時會失效,一般是三十分鐘。
2.cookie
cookie指某些網站爲了辨別用戶身份、進行會話跟蹤而存儲在用戶本地終端上的數據。
問題2
cookie和session一般是配合使用的
那當cookie被用戶禁用,session怎麼使用?
有兩種方法:
- 提示必須開啓cookie
- 使用url重傳,就是將sessionid附帶在url後面傳遞給瀏覽器
1.cookie的組成
- Name:該cookie的名稱,一旦創建,不可更改。
- value:該cookie的值,如果值爲Unicode字符,需要爲字符編碼。如果值爲二進制數據,則需要使用BASE64編碼。
- Domain:可以訪問該cookie的域名。例如:如果設置爲.zhihu.com,則所有以zhihu.com結尾的域名都可以訪問該cookie。
- MaxAge:該cookie的失效時間,單位爲秒。也常和Expires— 起使用,通過它可以計算出其有效時間。Max Age 如果爲正數,則該cookie在MaxAge 秒之後失效。如果爲負數,則關閉瀏覽器時cookie失效,瀏覽器也不會以任何形式保存該cookie 。
- Path:該cookie 的使用路徑。如果設置爲/path/,則只有路徑爲/path/的頁面可以訪問該cookie。如果設置爲/,則本域名下的所有頁面都可以訪問該cookie。
- Size:該cookie的大小。
- HTTP:該cookie的httponly屬性。若此屬性爲true,則只有在HTTP頭中會帶有此cookie的信息,而不能通過document.cookie來訪問此cookie。
- Secure:該cookie是否僅被使用安全協議傳輸。安全協議有http,https和SSL等,在網絡上傳輸數據之前先將數據加密,默認爲flase。
2.會話cookie和持久cookie
會話cookie就是指存儲在瀏覽器內存中的cookie,當瀏覽器關閉,會話cookie失效。
持久cookie是保存在硬盤上的cookie。
這兩個cookie的分類標準:
主要是通過maxAge或者expires字段定義的。爲負數,則爲會話cookie。
二、在爬蟲中實現登錄
(一)方法一
將登錄後頁面中的cookie,封裝到請求頭中。
新浪微博
import requests
base_url = 'https://weibo.com/5567019285/profile?'
params = {
'topnav': '1',
'wvr': '6',
'is_all': '1'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
'Cookie': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
response = requests.get(base_url,headers=headers,params=params)
if '呂-二' in response.text:
print('登錄成功')
else:
print('登錄失敗')
(二)方法二
使用requests模塊中的session對象
session = requests.session() # 這個session對象可以記錄登錄狀態
人人網
import requests
base_url = 'http://www.renren.com/PLogin.do'
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Host': 'www.renren.com',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
data = {
'email':str(13811780191),
'password':str(123123)
}
session = requests.session()
session.post(base_url,headers=headers,data=data)
response = session.get('http://www.renren.com/972683871/newsfeed/photo')
print(response.headers)
if '呂二三' in response.text:
print('登錄成功')
else:
print('登錄失敗')
三、代理
(一)基本原理
代理實際上指的就是代理服務器,英文叫作proxy server。它的功能是代理網絡用戶去取得網絡信息。形象地說,它是網絡信息的中轉站。在我們正常請求一個網站時,是發送了請求給web服務器,web服務器把響應傳回給我們。如果設置了代理服務器,實際上就是在本機和服務器之間搭建了一個橋,此時本機不是直接向web服務器發起請求,而是向代理服務器發出請求,請求會發送給代理服務器,然後由代理服務器再發送給web服務器,接着由代理服務器再把web服務器返回的響應轉發給本機。這樣我們同樣可以正常訪問網頁,但這個過程中web服務器識別出的真實IP就不再是我們本機的IP了,就成功實現了IP僞裝。
(二)作用
-
突破自身IP訪問限制
訪問一些平時不能訪問的站點。
-
訪問一些單位或團體內部的內部資源
比如使用教育網內地址段免費代理服務器,就可以用於對教育網開放的各類FTP下載上傳,以及各類資料查詢共享等服務。
-
提高訪問速度
通常代理服務器都設置一個較大的硬盤緩衝區,當有外界的信息通過時,同時也將其保存到緩衝區中,當其他用戶再訪問相同的信息時,則直接由緩衝區中取出信息傳給用戶即可,可以提高訪問速度。
-
隱藏真實ip
上網者也可以通過這種方法隱藏自己的IP,免受攻擊。對於爬蟲來說,我們用代理就是爲了隱藏自身IP,防止自身的被封鎖。
(三)分類
1.根據協議區分
- FTP 代理服務器:主要用於訪問FTP 服務器, 一般有上傳、下載以及緩存功能, 端口一般爲21 、2121 等。
- HTTP 代理服務器:主要用於訪問網頁, 一般有內容過濾和緩存功能, 端口一般爲80 、8080 、3128 等。
- SSL/TLS 代理:主要用於訪問加密網站, 一般有SSL 或TLS加密功能( 最高支持128 位加密強度) , 端口一般爲443 。
- RTSP 代理:主要用於訪問Real 流媒體服務器, 一般有緩存功能, 端口一般爲554 。
- Telnet 代理:主要用於telnet 遠程控制( 黑客人侵計算機時常用於隱藏身份),端口一般爲23 。
- POP3/SMTP 代理:主要用於POP3/SMTP 方式收發郵件, 一般有緩存功能, 端口一般爲110 / 25 。
- SOCKS 代理:只是單純傳遞數據包, 不關心具體協議和用法, 所以速度快很多, 一般有緩存功能, 端口一般爲1080 。SOCKS 代理協議又分爲SOCKS4 和SOCKS5 , 前者只支持TCP ,而後者支持TCP 和UDP , 還支持各種身份驗證機制、服務器端域名解析等。簡單來說,SOCKS4 能做到的SOCKS5 都可以做到, 但SOCKS5 能做到的SOCKS4 不一定能做到。
2.根據匿名程度區分
- 高度匿名代理: 會將數據包原封不動地轉發, 在服務端看來就好像真的是一個普通客戶端在訪問, 而記錄的IP 是代理服務器的IPO。
- 普通匿名代理: 會在數據包上做一些改動, 服務端上有可能發現這是個代理服務器, 也有一定機率追查到客戶端的真實伊代理服務器通常會加人的HITP 頭有HTTP_VIA 和HTTP_X_FORWARDED FOR 。
- 透明代理:不但改動了數據包, 還會告訴服務器客戶端的真實IPO 這種代理除了能用緩存技術提高瀏覽速度, 能用內容過濾提高安全性之外, 並無其他顯著作用, 最常見的例子是內網中的硬件防火牆。
- 間諜代理:指組織或個人創建的用於記錄用戶傳輸的數據, 然後進行研究、監控等目的的代理服務器。
(四)在requests模塊中設置代理
步驟
-
創建一個代理字典
proxies = ( 'http':代理ip, 'https':'https://ip:port' )
-
用get或者post請求是,增加proxies這個參數就可以了
四、數據的分類
(一)結構化數據
數據以行爲單位,一行數據表示一個實體的信息,每一行的數據的屬性是相同的。
常見於關係型數據庫。
(二)半結構化數據
結構化數據的一種形式,並不符合關係型數據庫或其他數據表的形式關聯起來的數據模型結構,但包含相關標記,用來分隔語義元素以及對記錄和字段進行分層。因此它也被稱爲自描述的結構。
常見的半結構化數據有:xml,html,json…
json數據
JSON是JS對象的字符串表達式,他使用文本形式表示一個JS對象的信息,本質是一個字符串。
JS的對象就相當於python中的字典。
JS的數組就相當於python中的列表。
因爲json用來存儲js的對象或者數組,所以在python中我們可以將json轉化爲list或者dict。
方法
json.dumps(Python中的list或dict) # 返回值爲json字符串
json.loads(json字符串) # 返回值爲Python中的list或dict
json.dump(list/dict,fp) # 將list/dict保存到json文件中
json.load(fp) # 從json文件中讀取json數據,返回值爲list/dict
(三)非結構化數據
沒有固定的結構,常見的非結構化數據有:文檔,圖片,視頻等…
對於非結構化數據,一般我們以二進制的格式進行整體存儲。
五、正則表達式
(一)元字符
1.匹配邊界
- ^:行首
- $:行尾
2.重複次數
- ?:0或1
- *:>=0
- +:>0
- {n,}:>=n
- {n,m}:>=n&&<=m
- {n}:n
3.各種字符的表示
- []:表示單字符
- [abc]:匹配a或b或c
- [a-z]:匹配a到z中的一個
- [0-9a-zA-Z_]:匹配數字字母下劃線
- \d:數字
- \D:非數字
- \w:數字字母下劃線
- \W:非數字字母下劃線
- \s:空白(空格、製表符、換行)
- \S:非空白
- \b:單詞的邊界–開始、結束(不消耗待匹配字符)
- \B:非邊界
- .:除換行符外的任意字符
(二)re模塊
1.使用步驟
-
將正則表達式編譯成一個pattern對象。
pattern = re.compile('正則表達式')
-
pattern提供了一系列的方法,對文本進行匹配,一般返回值爲match對象。
-
通過使用match對象提供的方法獲取匹配結果。
match.group() # 獲取匹配結果 match.group(0) # 獲取匹配結果 match.span() # 獲取匹配範圍 match.start() # 匹配開始位置 match.end() # 匹配結束位置
2.pattern對象的方法
1.match():默認從頭開始匹配,只匹配一次,返回一個match對象。
pattern.match(
'待匹配的字符串'(必須指定),
start(匹配的開始位置),
end(匹配的結束位置)
)
返回值爲match對象
import re
pattern = re.compile(r'\d+')
content = 'one12twothree34four'
m1 = pattern.match(content)
m2 = pattern.match(content,2,10)
m3 = pattern.match(content,3,10)
print(m1)
print(m2)
print(m3)
# match對象的方法
print(m3.group())
print(m3.group(0))
print(m3.span()) # 到匹配字符的下一個位置
print(m3.start())
print(m3.end()) # 到匹配字符的下一個位置
2.search():從任意位置匹配,只匹配一次,返回一個match對象。
pattern.match(
'待匹配的字符串'(必須指定),
start(匹配的開始位置),
end(匹配的結束位置)
)
返回值爲match對象
import re
pattern = re.compile(r'\d+')
content = 'one12twothree34four'
m1 = pattern.search(content)
m2 = pattern.search(content,5)
print(m1)
print(m1.group())
print(m2)
print(m2.group())
3.findall():全文匹配,多次匹配,將匹配到的結果放到一個list中返回。
pattern.findall(
'待匹配的字符串'(必須指定),
start(匹配的開始位置),
end(匹配的結束位置)
)
返回值爲list
import re
# content = 'we are well welcome'
# pattern1 = re.compile(r'\bwe\b')
# pattern2 = re.compile('\\bwe\\b')
# pattern3 = re.compile('we')
# result1 = pattern1.findall(content)
# result2 = pattern2.findall(content)
# result3 = pattern3.findall(content)
# print(result1)
# print(result2)
# print(result3)
pattern = re.compile(r'\d+')
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4')
print(result1)
print(result2)
4.finditer():全文匹配,多次匹配,返回一個包含匹配結果的迭代器。
pattern.finditer(
'待匹配的字符串'(必須指定),
start(匹配的開始位置),
end(匹配的結束位置)
)
返回值爲迭代器iterator
import re
pattern = re.compile(r'\d+')
content1 = 'hello 123456 789'
content2 = 'one1two2three3four4'
result_iter1 = pattern.finditer(content1)
result_iter2 = pattern.finditer(content2,0,7)
print(type(result_iter1))
print(type(result_iter2))
for m1 in result_iter1:
print('matching string:{},position:{}'.format(m1.group(),m1.span()))
print('-----------------------------------------')
for m2 in result_iter2:
print('matching string:{},position:{}'.format(m2.group(), m2.span()))
5.split():切分字符串,按照整個表達式所指定的內容切分,返回值是list。
pattern.split(
‘要切分的字符串’,
切分的次數(不指定就默認全部切割)
)
返回值爲list
import re
p = re.compile(r'[\s,;]+')
a = p.split('a,b;;,c,; d')
print(a)
6.sub():用指定的字符串,替換正則表達式匹配到的目標字符串的內容。
pattern.sub(
repl(替換成什麼),
content(待替換內容),
count(替換次數,可不指定,默認替換所有)
)
repl可以是字符串也可以是函數。
repl是字符串時,侷限性是隻能替換爲固定的內容,不靈活。
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123,hello 456'
print(p.sub('ssss',s))
print(p.sub(r'\2 \1',s)) # 引用分組
repl是函數時,就很靈活了。
當repl是函數的時候,這個函數必須滿足以下要求:
-
函數必須攜帶一個參數,這個參數是一個match對象。
def func(match): ''' 對match對象的操作。 ''' print(macth.group())
-
當在sub方法中傳入這個參數時,這個match對象其實就是用正則匹配到的每一個match對象。
-
這個函數是有返回值的,返回值必須是一個字符串,將來是用這個字符串進行替換目標字符串的。
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123,hello 456'
def func(m):
return 'hi' + ' ' + m.group(2)
print(p.sub(func,s))
print(p.sub(func,s,1))
p = re.compile(r'\d+')
content = 'zhangsan:3000,lisi:4000'
def add(match):
return str(int(match.group())+2000)
print(p.sub(add,content))
(三)正則表達式中的分組
分組是通過()來表示的,一個括號就表示一個分組。
1.作用
-
篩選特定內容。
取分組內容可以通過match對象的group方法取。
group(1)取的是正則表達式中第一個括號(分組)匹配到的內容,以此類推。
import re content = '{name:"zhangsan",age:"10",hobby:["basktball","football","read"]}' pattern = re.compile(r'{name:"(\w+)",age:"(\d+)".+}') match = pattern.search(content) print(match.group(1)) print(match.group(2))
-
可以在同一個表達式的後面引用前面的分組表達式。
import re s = '<html><h1>正則表達式</h1></html>' pattern = re.compile(r'<(html)><(h1)>(.*)</\2></\1>') match = pattern.search(s) print(match.group(3))
(四)正則表達式的模式
pattern = re.compile(
‘正則表達式’,
‘正則表達式的模式’
)
正則表達式的模式有以下幾種:
- re.I:使匹配對大小寫不敏感
- re.M:多行匹配,影響^和$
- re.S:使.匹配包括換行在內的所有字符
(五)貪婪和非貪婪模式
- 貪婪是用*控制的
- 非貪婪是用?控制的
- *和?都是作用於表示重複次數的元字符的
- 正則默認是貪婪模式,所以數量控制符默認是最大值,也就是貪婪
- 在表示重複的元字符後面加一個?,此時就是非貪婪,取這個重複元字符的最小值
(六)通用匹配正則表達式
‘.*?’,配合re.S
content = '{name:"zhangsan",age:"10",hobby:["basktball","football","read"]}'
pattern = re.compile(r'.*?"(.*?)".*?"(.*?)".*?')
match = pattern.search(content)
print(match)
print(match.group(1))
print(match.group(2))