Python手記-7:爬蟲初體驗-Requests庫與re庫的使用

目錄

1. Requests庫

1.1 GET請求

1.2 POST請求

1.3 Session

1.4 代理

1.5 超時設置

2. 正則表達式re庫

2.1 re.match(pattern,string,flags=0)

2.2 re.search(pattern,string,flags=0)

2.3 re.findall(pattern,string,flags=0)

2.4 re.finditer(pattern,string,flags=0)

2.5 re.split(pattern,string,maxsplit=0,flags=0)

2.6 re.sub(pattern,repl,string,count=0,flags=0)

2.7 re.compile(pattern,flags=0)

2.8 正則表達式常用操作符

3. heads參數


枯燥學習好幾天,今天就拿即將學習的知識中的案例來提升一下學習的熱情,百度資訊搜一波熱詞“羅志祥”,參考《Python金融大數據挖掘與分析》書中的案例:

# -*- coding: utf-8 -*- 
# @Time : 2020/4/23 16:37 
# @Author : ChengYu 
# @File : requests_get.py

import requests
import re

# 加上headers用來告訴網站這是通過一個瀏覽器進行的訪問
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                        'Chrome/81.0.4044.122 Safari/537.36'}  
url = 'https://www.baidu.com/s?tn=news&rtt=1&bsst=1&cl=2&wd=羅志祥'
res = requests.get(url, headers=headers).text
print(res)  # 輸出網頁源碼

p_info = '<p class="c-author">(.*?)</p>'
# 查找符合匹配規則的文本,re.S是用於匹配換行的修飾符,如果源文本有換行需要它來準確獲取匹配內容
info = re.findall(p_info, res, re.S)  
p_href = '<h3 class="c-title">.*?<a href="(.*?)"'
href = re.findall(p_href, res, re.S)
p_title = '<h3 class="c-title">.*?>(.*?)</a>'
title = re.findall(p_title, res, re.S)

# 先創建兩個空列表來儲存等會分割後的來源和日期
source = []  
date = []
for i in range(len(info)):
    title[i] = title[i].strip()  # 去除字符串中多餘的空格和換行符
    title[i] = re.sub('<.*?>', '', title[i])
    info[i] = re.sub('<.*?>', '', info[i])
    source.append(info[i].split('&nbsp;&nbsp;')[0])  # append()整理分割後的來源和日期
    date.append(info[i].split('&nbsp;&nbsp;')[1])
    source[i] = source[i].strip()
    date[i] = date[i].strip()

    print(str(i + 1) + '.' + title[i] + '(' + date[i] + '-' + source[i] + ')')
    print(href[i])

  • 爬取的部分網頁源碼

  • 提取新聞標題、網址、時間及來源

雖一慣秉承合格吃瓜別較真,未知全貌不予置評;但生而爲人皆“眼裏有尺,心中有秤”,“朱碧石”的濾鏡也救不了令人咋舌的作:貴圈真亂。

1. Requests庫

關於Requests庫的常用方法:https://requests.readthedocs.io/en/latest/user/quickstart/

高級用法:https://requests.readthedocs.io/en/latest/user/advanced/

1.1 GET請求

構建簡單的GET請求,返回請求信息:

>>> import requests                                     
>>> r = requests.get('https://httpbin.org/get')         
>>> print(r.text)
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.23.0",
    "X-Amzn-Trace-Id": "Root=1-5ea5010f-b5e6a06af401bcbd34004025"
  },
  "origin": "120.234.135.254",
  "url": "https://httpbin.org/get"
}

1.2 POST請求

構建POST請求,返回請求信息:

>>> import requests
>>> r = requests.post('https://httpbin.org/post', data = {'name': 'chengyu', 'age': '18'})
>>> print(r.text)
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "age": "18",
    "name": "chengyu"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "19",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.23.0",
    "X-Amzn-Trace-Id": "Root=1-5ea50411-d0733e172c7cbc49985b1dc6"
  },
  "json": null,
  "origin": "120.234.135.254",
  "url": "https://httpbin.org/post"
}

其他HTTP請求類型:PUT,DELETE,HEAD和OPTIONS:

>>> r = requests.put('https://httpbin.org/put', data = {'key':'value'})
>>> r = requests.delete('https://httpbin.org/delete')
>>> r = requests.head('https://httpbin.org/get')
>>> r = requests.options('https://httpbin.org/get')

1.3 Session

 通過Session對象,可以在請求中保留某些參數,它還會在來自Session實例的所有請求中保留cookie,並將使用urllib3的連接池。

>>> import requests
>>> s = requests.Session()
>>> s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
<Response [200]>
>>> r = s.get('https://httpbin.org/cookies')
>>> print(r.text)
{
  "cookies": {
    "sessioncookie": "123456789"
  }
}

1.4 代理

某些網站對大規模且頻繁的請求,會通過驗證或者登錄認證或者封掉 IP來禁止訪問,就需要使用代理解決這個限制,如果需要使用代理,則可以使用proxies任何請求方法的參數來配置單個請求 :

import requests

proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}

requests.get('http://example.org', proxies=proxies)

1.5 超時設置

 如果服務器未及時響應或者本地網絡狀況不好,收到網頁響應需要更長的時間,就需要設置服務器的超時時間。默認情況下,除非明確設置超時值,否則請求不會超時。沒有超時,代碼可能會掛起幾分鐘或更長時間。

客戶端連接到服務器併發送HTTP請求後, 讀取超時就是客戶端將等待服務器發送響應的秒數,(具體來說,這是客戶端從服務器發送的字節之間等待的秒數,在99.9%的情況下,這是服務器發送第一個字節之前的時間)。

如果爲超時指定單個值,則如下所示:

r = requests.get('https://github.com', timeout=5)

超時值將同時應用於connectread ,如果要單獨設置值,請指定一個元組:

r = requests.get('https://github.com', timeout=(3.05, 27))

如果遠程服務器非常慢,則可以通過將None傳遞爲超時值,然後獲取一杯咖啡,從而使Requests永遠等待響應:

r = requests.get('https://github.com', timeout=None)

2. 正則表達式re庫

re庫(Regular  Expression  正則表達式)是Python自帶庫,提供正則表達式引擎的接口,可實現字符串的檢索、替換、匹配提取等,官文指路:https://docs.python.org/3/library/re.html或者參看Python官文中的howto-regex.pdf。

re庫常用函數:

函數

功能說明

re.match(pattern,string,flags=0)

從一個字符串的開始位置起匹配正則表達式,返回match對象。

re.search(pattern,string,flags=0)

在一個字符串中搜索匹配正則表達式的第一個位置,返回match對象。

re.findall(pattern,string,flags=0)

搜索字符串,以列表類型返回全部能匹配的子串。

re.finditer(pattern,string,flags=0)

搜索字符串,返回一個匹配結果的迭代類型,每個迭代元素是match對象。

re.split(pattern,string,maxsplit=0,flags=0)

將一個字符串按照正則表達式匹配結果進行分割,返回列表類型。

re.sub(pattern,repl,string,count=0,flags=0)

在一個字符串中替換所有匹配正則表達式的子串,返回替換後的字符串。

re.compile(pattern,flags=0)

生成一個正則表達式(Pattern)對象,供match()和search()這兩個函數使用。

  • Pattern匹配的正則表達式
  • String要匹配的字符串
  • Flags標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫,多行匹配等等。

2.1 re.match(pattern,string,flags=0)

如果string開始的0或者多個字符匹配到了正則表達式樣式,就返回一個相應的匹配對象如果沒有匹配,就返回None;注意它跟零長度匹配是不同的,​​​​match()對象有幾種方法:

  • group()返回正則匹配的字符串 
  • start()返回匹配的開始位置 
  • end()返回匹配的結束位置 
  • span()返回包含匹配(start, end) 位置的元組
>>> import re
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay'))
<re.Match object; span=(0, 3), match='123'>
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').span())
(0, 3)
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').start())
0
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').end())
3
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').group())
123

# 起始位置沒有匹配返回none
>>> print(re.match(r'[1-9]\d{2}', 'Nothing gold can stay 123'))
None
# 匹配返回none,則start()/end()/span()/group()返回如下的結果。
>>> print(re.match(r'[1-9]\d{2}', 'Nothing gold can stay 123')).start()
None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'start'

2.2 re.search(pattern,string,flags=0)

掃描整個字符串找到匹配樣式的第一個位置,並返回一個相應的匹配對象如果沒有匹配,就返回一個None;注意這和找到一個零長度匹配是不同的。

>>> print(re.match(r'[1-9]\d{2}', 'Nothing 123 gold can stay'))
None
>>> print(re.match(r'[1-9]\d{2}', 'Nothing 123 gold can stay').start())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'start'
>>> print(re.search(r'[1-9]\d{2}', 'Nothing 123 gold can stay'))
<re.Match object; span=(8, 11), match='123'>
>>> print(re.search(r'[1-9]\d{2}', 'Nothing 123 gold can stay').start())
8

2.3 re.findall(pattern,string,flags=0)

string返回一個不重複的pattern的匹配列表,string從左到右進行掃描,匹配按找到的順序返回。如果樣式裏存在一到多個組,就返回一個組合列表;就是一個元組的列表(如果樣式裏有超過一個組合的話),空匹配也會包含在結果裏。

>>> print(re.findall(r'[1-9]\d{2}', 'Nothing 123 gold can 456stay'))
['123', '456']
>>> print(re.findall(r'[1-9]\d{2}', 'Nothing gold can stay'))
[]

2.4 re.finditer(pattern,string,flags=0)

pattern在string裏所有的非重複匹配,findall類似,它在字符串中找到正則表達式所匹配的所有子串,並把它們作爲一個迭代器返回;string從左到右掃描,匹配按順序排列,空匹配也包含在結果裏。

 

>>> print(re.finditer(r'[1-9]\d{2}', 'Nothing 123 gold can 456stay'))
<callable_iterator object at 0x7f4289b4d970>
>>> print(re.finditer(r'[1-9]\d{2}', 'Nothing gold can stay'))
<callable_iterator object at 0x7f4289b4d970>

2.5 re.split(pattern,string,maxsplit=0,flags=0)

用pattern分開string,也就是將字符串與模式匹配的子字符串都作爲分隔符來分隔這個字符串,如果在pattern中捕獲到括號,那麼所有的組裏的文字也會包含在列表裏;如果maxsplit非零,最多進行maxsplit次分隔,剩下的字符全部返回到列表的最後一個元素。

>>> print(re.split(r'[1-9]\d{1}', 'Nothing 456gold 123can stay'))
['Nothing ', '6gold ', '3can stay']
>>> print(re.split(r'[1-9]\d{2}', 'Nothing 456gold 123can stay'))
['Nothing ', 'gold ', 'can stay']

2.6 re.sub(pattern,repl,string,count=0,flags=0)

返回通過使用repl替換在string最左邊非重疊出現的pattern而獲得的字符串如果樣式沒有找到,則不加改變地返回string。

  • pattern:正則中的模式字符串
  • repl:替換的字符串,也可爲一個函數
  • string:要被查找替換的原始字符串
  • count:模式匹配後替換的最大次數,默認0表示替換所有的匹配。
# sub()實現搜索和替換的功能。
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay'))
Nothing 8gold 8can stay
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',0))
Nothing 8gold 8can stay
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',1))
Nothing 8gold 123can stay
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',2))
Nothing 8gold 8can stay
# subn()與sub()功能類似,返回值多了替換的次數。
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay'))
('Nothing 8gold 8can stay', 2)
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',1))
('Nothing 8gold 123can stay', 1)
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',2))
('Nothing 8gold 8can stay', 2)
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',3))
('Nothing 8gold 8can stay', 2)

2.7 re.compile(pattern,flags=0)

compile函數用於編譯正則表達式,生成一個正則表達式(Pattern)對象,供match()和search()這兩個函數使用

  • pattern:一個字符串形式的正則表達式
  • flags:可選,表示匹配模式,比如忽略大小寫,多行模式等。
>>> import re
>>> pattern = re.compile(r'[1-9]\d{2}')
>>> print(pattern.match('Nothing 456gold 123can stay'))
None
>>> print(pattern.search('Nothing 456gold 123can stay'))
<re.Match object; span=(8, 11), match='456'>

2.8 正則表達式常用操作符

操作符

說明

實例

.

表示任何單個字符,換行符除外

 

[ ]

字符集,對單個字符給出取值範圍

[abc]表示a、b、c,[a‐z]表示a到z單個字符

[^ ]

非字符集,對單個字符給出排除範圍

[^abc]表示非a或b或c的單個字符

*

前一個字符0次或無限次擴展

abc* 表示ab、abc、abcc、abccc等

+

前一個字符1次或無限次擴展

abc+ 表示abc、abcc、abccc等

?

前一個字符0次或1次擴展,非貪婪限定符,常與. 和*聯合使用

abc? 表示ab、abc

|

左右表達式任意一個

abc|def表示abc、def

{m}

擴展前一個字符m次

ab{2}c表示abbc

{m,n}

擴展前一個字符m至n次(含n)

ab{1,2}c表示abc、abbc

^

匹配字符串開頭

^abc表示abc且在一個字符串的開頭

$

匹配字符串結尾

abc$表示abc且在一個字符串的結尾

( )

分組標記,內部只能使用

操作符|(abc)表示abc,(abc|def)表示abc、def

\b

匹配單詞邊界,也就是指單詞和空格間的位置

er\b 可以匹配never中的 er,但不能匹配verb中的er

 

\B

匹配非單詞邊界

er\B 能匹配verb中的er,但不能匹配never中的er

\d

數字,等價於[0‐9]

 

\D

非數字字符,等價於 [^0-9]

 

\w

匹配數字、字母、下劃線,等價於[A-Za-z0-9_]

 

\w

匹配非字母、數字、下劃線,等價於[^A‐Za‐z0‐9_]

 

正則匹配模式分兩種:

  • 貪婪匹配:儘可能匹配最長的字符串
  • 非貪婪匹配: 儘可能匹配最短的字符串
>>> import re
>>> re.match(r'someone(\d+?)','someone2343you')
<re.Match object; span=(0, 8), match='someone2'>
>>> re.match(r'someone(\d+)','someone2343you').group(1)
'2343'
>>> re.match(r'someone(\d+)','someone2343you')
<re.Match object; span=(0, 11), match='someone2343'>
>>> re.match(r'someone(\d+?)','someone2343you').group(1)
'2'

默認情況下是貪婪模式,如要改成非貪婪模式,只需要在“*”,“?”,“+”,“{m,n}”量詞後面加上一個問號“?”。在金融數據的挖掘分析中,常用的匹配正則式是非貪婪匹配:(.*?)和.*?。

  • .*?:文本A.*?文本B,用於獲取文本A和文本B之間的內容,文本A和文本B之間的我內容經常變動或者沒有規律或者內容較多,無法寫入匹配規則。

  • (.*?):文本C(.*?)文本D,用於獲取文本C和文本D之間的內容,不需指定長度及格式,需指定兩端的內容。

>>> import re
>>> w = 'There is no royal2233 road2233 to learning'
>>> print(re.findall('.*\d+', w))
['There is no royal2233 road2233']
>>> print(re.findall('.*?\d+', w))
['There is no royal2233', ' road2233']
>>> print(re.findall('(.*?)\d+', w))
['There is no royal', ' road']

正則表達式可以包含一些可選標誌修飾符來控制匹配的模式(用於函數的flag參數),修飾符被指定爲一個可選的標誌,多個標誌可以通過按位 OR(|) 來指定,如 re.I | re.M 被設置成 I 和 M 標誌,修飾符如下:

  • re.I:使匹配對大小寫不敏感;
  • re.L:做本地化識別(locale-aware)匹配;
  • re.M:多行匹配,影響 ^ 和 $;
  • re.S:使 . 匹配包括換行在內的所有字符;
  • re.U:根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B.;
  • re.X:該標誌通過給予你更靈活的格式以便你將正則表達式寫得更易於理解。

3. heads參數

heads參數提供的事網站訪問者的信息,heads中的User-Agent表示用什麼瀏覽器訪問,在地址欄輸入“about:version”獲取User-Agent:

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36'}

使用方式:requests.get()中添加headers=headers參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章